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