1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * Unless required by applicable law or agreed to in writing, software 8 * distributed under the License is distributed on an "AS IS" BASIS, 9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 * See the License for the specific language governing permissions and 11 * limitations under the License. 12 */ 13 14 package android.databinding.tool; 15 16 import android.databinding.tool.reflection.InjectedClass; 17 import android.databinding.tool.reflection.InjectedMethod; 18 import android.databinding.tool.reflection.ModelAnalyzer; 19 import android.databinding.tool.reflection.ModelClass; 20 import android.databinding.tool.store.ResourceBundle; 21 import android.databinding.tool.util.L; 22 import android.databinding.tool.util.Preconditions; 23 import android.databinding.tool.writer.BRWriter; 24 import android.databinding.tool.writer.DataBinderWriter; 25 import android.databinding.tool.writer.DynamicUtilWriter; 26 import android.databinding.tool.writer.JavaFileWriter; 27 28 import java.util.HashMap; 29 import java.util.Set; 30 31 /** 32 * Chef class for compiler. 33 * 34 * Different build systems can initiate a version of this to handle their work 35 */ 36 public class CompilerChef { 37 private static final String[] VERSION_CODES = { 38 "BASE", // 1 39 "BASE_1_1", // 2 40 "CUPCAKE", // 3 41 "DONUT", // 4 42 "ECLAIR", // 5 43 "ECLAIRE_0_1", // 6 44 "ECLAIR_MR1", // 7 45 "FROYO", // 8 46 "GINGERBREAD", // 9 47 "GINGERBREAD_MR1", // 10 48 "HONEYCOMB", // 11 49 "HONEYCOMB_MR1", // 12 50 "HONEYCOMB_MR2", // 13 51 "ICE_CREAM_SANDWICH", // 14 52 "ICE_CREAM_SANDWICH_MR1",// 15 53 "JELLY_BEAN", // 16 54 "JELLY_BEAN_MR1", // 17 55 "JELLY_BEAN_MR2", // 18 56 "KITKAT", // 19 57 "KITKAT_WATCH", // 20 58 "LOLLIPOP", // 21 59 "LOLLIPOP_MR1", // 22 60 "M", // 23 61 }; 62 private JavaFileWriter mFileWriter; 63 private ResourceBundle mResourceBundle; 64 private DataBinder mDataBinder; 65 CompilerChef()66 private CompilerChef() { 67 } 68 createChef(ResourceBundle bundle, JavaFileWriter fileWriter)69 public static CompilerChef createChef(ResourceBundle bundle, JavaFileWriter fileWriter) { 70 CompilerChef chef = new CompilerChef(); 71 72 chef.mResourceBundle = bundle; 73 chef.mFileWriter = fileWriter; 74 chef.mResourceBundle.validateMultiResLayouts(); 75 chef.pushClassesToAnalyzer(); 76 chef.pushDynamicUtilToAnalyzer(); 77 return chef; 78 } 79 getResourceBundle()80 public ResourceBundle getResourceBundle() { 81 return mResourceBundle; 82 } 83 ensureDataBinder()84 public void ensureDataBinder() { 85 if (mDataBinder == null) { 86 mDataBinder = new DataBinder(mResourceBundle); 87 mDataBinder.setFileWriter(mFileWriter); 88 } 89 } 90 hasAnythingToGenerate()91 public boolean hasAnythingToGenerate() { 92 L.d("checking if we have anything to generate. bundle size: %s", 93 mResourceBundle == null ? -1 : mResourceBundle.getLayoutBundles().size()); 94 return mResourceBundle != null && mResourceBundle.getLayoutBundles().size() > 0; 95 } 96 97 /** 98 * Injects ViewDataBinding subclasses to the ModelAnalyzer so that they can be 99 * analyzed prior to creation. This is useful for resolving variable setters and 100 * View fields during compilation. 101 */ pushClassesToAnalyzer()102 private void pushClassesToAnalyzer() { 103 ModelAnalyzer analyzer = ModelAnalyzer.getInstance(); 104 for (String layoutName : mResourceBundle.getLayoutBundles().keySet()) { 105 ResourceBundle.LayoutFileBundle layoutFileBundle = 106 mResourceBundle.getLayoutBundles().get(layoutName).get(0); 107 final HashMap<String, String> imports = new HashMap<String, String>(); 108 for (ResourceBundle.NameTypeLocation imp : layoutFileBundle.getImports()) { 109 imports.put(imp.name, imp.type); 110 } 111 final HashMap<String, String> variables = new HashMap<String, String>(); 112 for (ResourceBundle.VariableDeclaration variable : layoutFileBundle.getVariables()) { 113 final String variableName = variable.name; 114 String type = variable.type; 115 if (imports.containsKey(type)) { 116 type = imports.get(type); 117 } 118 variables.put(variableName, type); 119 } 120 final HashMap<String, String> fields = new HashMap<String, String>(); 121 for (ResourceBundle.BindingTargetBundle bindingTargetBundle : 122 layoutFileBundle.getBindingTargetBundles()) { 123 if (bindingTargetBundle.getId() != null) { 124 fields.put(bindingTargetBundle.getId(), bindingTargetBundle.getInterfaceType()); 125 } 126 } 127 final String className = layoutFileBundle.getBindingClassPackage() + "." + 128 layoutFileBundle.getBindingClassName(); 129 analyzer.injectViewDataBinding(className, variables, fields); 130 } 131 } 132 pushDynamicUtilToAnalyzer()133 public static InjectedClass pushDynamicUtilToAnalyzer() { 134 InjectedClass injectedClass = new InjectedClass("android.databinding.DynamicUtil", 135 "java.lang.Object"); 136 injectedClass.addMethod(new InjectedMethod(injectedClass, true, "getColorFromResource", 137 "int", "android.view.View", "int")); 138 injectedClass.addMethod(new InjectedMethod(injectedClass, true, 139 "getColorStateListFromResource", "android.content.res.ColorStateList", 140 "android.view.View", "int")); 141 injectedClass.addMethod(new InjectedMethod(injectedClass, true, "getDrawableFromResource", 142 "android.graphics.drawable.Drawable", "android.view.View", "int")); 143 injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse", 144 "boolean", "java.lang.String", "boolean")); 145 injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse", 146 "short", "java.lang.String", "short")); 147 injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse", 148 "int", "java.lang.String", "int")); 149 injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse", 150 "long", "java.lang.String", "long")); 151 injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse", 152 "float", "java.lang.String", "float")); 153 injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse", 154 "double", "java.lang.String", "double")); 155 injectedClass.addMethod(new InjectedMethod(injectedClass, true, "parse", 156 "char", "java.lang.String", "char")); 157 ModelAnalyzer analyzer = ModelAnalyzer.getInstance(); 158 analyzer.injectClass(injectedClass); 159 return injectedClass; 160 } 161 writeDataBinderMapper(int minSdk, BRWriter brWriter)162 public void writeDataBinderMapper(int minSdk, BRWriter brWriter) { 163 ensureDataBinder(); 164 final String pkg = "android.databinding"; 165 DataBinderWriter dbr = new DataBinderWriter(pkg, mResourceBundle.getAppPackage(), 166 "DataBinderMapper", mDataBinder.getLayoutBinders(), minSdk); 167 mFileWriter.writeToFile(pkg + "." + dbr.getClassName(), dbr.write(brWriter)); 168 } 169 writeDynamicUtil()170 public void writeDynamicUtil() { 171 DynamicUtilWriter dynamicUtil = new DynamicUtilWriter(); 172 // TODO: Replace this with targetSDK check from plugin 173 ModelClass versionCodes = ModelAnalyzer.getInstance().findClass( 174 "android.os.Build.VERSION_CODES", null); 175 Preconditions.checkNotNull(versionCodes, "Could not find compile SDK"); 176 int compileVersion = 1; 177 for (int i = VERSION_CODES.length - 1; i >= 0; i--) { 178 if (versionCodes.findGetterOrField(VERSION_CODES[i], true) != null) { 179 compileVersion = i + 1; 180 break; 181 } 182 } 183 mFileWriter.writeToFile("android.databinding.DynamicUtil", 184 dynamicUtil.write(compileVersion).generate()); 185 } 186 187 /** 188 * Adds variables to list of Bindables. 189 */ addBRVariables(BindableHolder bindables)190 public void addBRVariables(BindableHolder bindables) { 191 ensureDataBinder(); 192 for (LayoutBinder layoutBinder : mDataBinder.mLayoutBinders) { 193 for (String variableName : layoutBinder.getUserDefinedVariables().keySet()) { 194 bindables.addVariable(variableName, layoutBinder.getClassName()); 195 } 196 } 197 } 198 sealModels()199 public void sealModels() { 200 ensureDataBinder(); 201 mDataBinder.sealModels(); 202 } 203 writeViewBinderInterfaces(boolean isLibrary)204 public void writeViewBinderInterfaces(boolean isLibrary) { 205 ensureDataBinder(); 206 mDataBinder.writerBaseClasses(isLibrary); 207 } 208 writeViewBinders(int minSdk)209 public void writeViewBinders(int minSdk) { 210 ensureDataBinder(); 211 mDataBinder.writeBinders(minSdk); 212 } 213 writeComponent()214 public void writeComponent() { 215 ensureDataBinder(); 216 mDataBinder.writeComponent(); 217 } 218 getWrittenClassNames()219 public Set<String> getWrittenClassNames() { 220 ensureDataBinder(); 221 return mDataBinder.getWrittenClassNames(); 222 } 223 224 public interface BindableHolder { addVariable(String variableName, String containingClassName)225 void addVariable(String variableName, String containingClassName); 226 } 227 } 228