• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.databinding.tool;
18 
19 import android.databinding.tool.expr.Dependency;
20 import android.databinding.tool.expr.Expr;
21 import android.databinding.tool.expr.ExprModel;
22 import android.databinding.tool.expr.IdentifierExpr;
23 import android.databinding.tool.processing.Scope;
24 import android.databinding.tool.processing.scopes.FileScopeProvider;
25 import android.databinding.tool.store.Location;
26 import android.databinding.tool.store.ResourceBundle;
27 import android.databinding.tool.store.ResourceBundle.BindingTargetBundle;
28 import android.databinding.tool.util.L;
29 import android.databinding.tool.util.Preconditions;
30 import android.databinding.tool.writer.LayoutBinderWriter;
31 import android.databinding.tool.writer.LayoutBinderWriterKt;
32 
33 import com.android.annotations.Nullable;
34 
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
41 
42 /**
43  * Keeps all information about the bindings per layout file
44  */
45 public class LayoutBinder implements FileScopeProvider {
46     private static final Comparator<BindingTarget> COMPARE_FIELD_NAME = new Comparator<BindingTarget>() {
47         @Override
48         public int compare(BindingTarget first, BindingTarget second) {
49             final String fieldName1 = LayoutBinderWriterKt.getFieldName(first);
50             final String fieldName2 = LayoutBinderWriterKt.getFieldName(second);
51             return fieldName1.compareTo(fieldName2);
52         }
53     };
54 
55     /*
56     * val pkg: String, val projectPackage: String, val baseClassName: String,
57         val layoutName:String, val lb: LayoutExprBinding*/
58     private final ExprModel mExprModel;
59     private final ExpressionParser mExpressionParser;
60     private final List<BindingTarget> mBindingTargets;
61     private final List<BindingTarget> mSortedBindingTargets;
62     private String mModulePackage;
63     private final HashMap<String, String> mUserDefinedVariables = new HashMap<String, String>();
64 
65     private LayoutBinderWriter mWriter;
66     private ResourceBundle.LayoutFileBundle mBundle;
67     private static final String[] sJavaLangClasses = {
68             "Deprecated",
69             "Override",
70             "SafeVarargs",
71             "SuppressWarnings",
72             "Appendable",
73             "AutoCloseable",
74             "CharSequence",
75             "Cloneable",
76             "Comparable",
77             "Iterable",
78             "Readable",
79             "Runnable",
80             "Thread.UncaughtExceptionHandler",
81             "Boolean",
82             "Byte",
83             "Character",
84             "Character.Subset",
85             "Character.UnicodeBlock",
86             "Class",
87             "ClassLoader",
88             "Compiler",
89             "Double",
90             "Enum",
91             "Float",
92             "InheritableThreadLocal",
93             "Integer",
94             "Long",
95             "Math",
96             "Number",
97             "Object",
98             "Package",
99             "Process",
100             "ProcessBuilder",
101             "Runtime",
102             "RuntimePermission",
103             "SecurityManager",
104             "Short",
105             "StackTraceElement",
106             "StrictMath",
107             "String",
108             "StringBuffer",
109             "StringBuilder",
110             "System",
111             "Thread",
112             "ThreadGroup",
113             "ThreadLocal",
114             "Throwable",
115             "Void",
116             "Thread.State",
117             "ArithmeticException",
118             "ArrayIndexOutOfBoundsException",
119             "ArrayStoreException",
120             "ClassCastException",
121             "ClassNotFoundException",
122             "CloneNotSupportedException",
123             "EnumConstantNotPresentException",
124             "Exception",
125             "IllegalAccessException",
126             "IllegalArgumentException",
127             "IllegalMonitorStateException",
128             "IllegalStateException",
129             "IllegalThreadStateException",
130             "IndexOutOfBoundsException",
131             "InstantiationException",
132             "InterruptedException",
133             "NegativeArraySizeException",
134             "NoSuchFieldException",
135             "NoSuchMethodException",
136             "NullPointerException",
137             "NumberFormatException",
138             "ReflectiveOperationException",
139             "RuntimeException",
140             "SecurityException",
141             "StringIndexOutOfBoundsException",
142             "TypeNotPresentException",
143             "UnsupportedOperationException",
144             "AbstractMethodError",
145             "AssertionError",
146             "ClassCircularityError",
147             "ClassFormatError",
148             "Error",
149             "ExceptionInInitializerError",
150             "IllegalAccessError",
151             "IncompatibleClassChangeError",
152             "InstantiationError",
153             "InternalError",
154             "LinkageError",
155             "NoClassDefFoundError",
156             "NoSuchFieldError",
157             "NoSuchMethodError",
158             "OutOfMemoryError",
159             "StackOverflowError",
160             "ThreadDeath",
161             "UnknownError",
162             "UnsatisfiedLinkError",
163             "UnsupportedClassVersionError",
164             "VerifyError",
165             "VirtualMachineError",
166     };
167 
LayoutBinder(ResourceBundle.LayoutFileBundle layoutBundle)168     public LayoutBinder(ResourceBundle.LayoutFileBundle layoutBundle) {
169         try {
170             Scope.enter(this);
171             mExprModel = new ExprModel();
172             mExpressionParser = new ExpressionParser(mExprModel);
173             mBindingTargets = new ArrayList<BindingTarget>();
174             mBundle = layoutBundle;
175             mModulePackage = layoutBundle.getModulePackage();
176             HashSet<String> names = new HashSet<String>();
177             // copy over data.
178             for (ResourceBundle.VariableDeclaration variable : mBundle.getVariables()) {
179                 addVariable(variable.name, variable.type, variable.location, variable.declared);
180                 names.add(variable.name);
181             }
182 
183             for (ResourceBundle.NameTypeLocation userImport : mBundle.getImports()) {
184                 mExprModel.addImport(userImport.name, userImport.type, userImport.location);
185                 names.add(userImport.name);
186             }
187             if (!names.contains("context")) {
188                 mExprModel.builtInVariable("context", "android.content.Context",
189                         "getRoot().getContext()");
190                 names.add("context");
191             }
192             for (String javaLangClass : sJavaLangClasses) {
193                 mExprModel.addImport(javaLangClass, "java.lang." + javaLangClass, null);
194             }
195             // First resolve all the View fields
196             // Ensure there are no conflicts with variable names
197             for (BindingTargetBundle targetBundle : mBundle.getBindingTargetBundles()) {
198                 try {
199                     Scope.enter(targetBundle);
200                     final BindingTarget bindingTarget = createBindingTarget(targetBundle);
201                     if (bindingTarget.getId() != null) {
202                         final String fieldName = LayoutBinderWriterKt.
203                                 getReadableName(bindingTarget);
204                         if (names.contains(fieldName)) {
205                             L.w("View field %s collides with a variable or import", fieldName);
206                         } else {
207                             names.add(fieldName);
208                             mExprModel.viewFieldExpr(bindingTarget);
209                         }
210                     }
211                 } finally {
212                     Scope.exit();
213                 }
214             }
215 
216             for (BindingTarget bindingTarget : mBindingTargets) {
217                 try {
218                     Scope.enter(bindingTarget.mBundle);
219                     final String className = getPackage() + "." + getClassName();
220                     for (BindingTargetBundle.BindingBundle bindingBundle : bindingTarget.mBundle
221                             .getBindingBundleList()) {
222                         try {
223                             Scope.enter(bindingBundle.getValueLocation());
224                             Expr expr = parse(bindingBundle.getExpr(),
225                                     bindingBundle.getValueLocation(),
226                                     bindingTarget);
227                             bindingTarget.addBinding(bindingBundle.getName(), expr);
228                             if (bindingBundle.isTwoWay()) {
229                                 bindingTarget.addInverseBinding(bindingBundle.getName(), expr,
230                                         className);
231                             }
232                         } finally {
233                             Scope.exit();
234                         }
235                     }
236                     // resolve callbacks first because they introduce local variables.
237                     bindingTarget.resolveCallbackParams();
238                     bindingTarget.resolveTwoWayExpressions();
239                     bindingTarget.resolveMultiSetters();
240                     bindingTarget.resolveListeners();
241                 } finally {
242                     Scope.exit();
243                 }
244             }
245             mSortedBindingTargets = new ArrayList<BindingTarget>(mBindingTargets);
246             Collections.sort(mSortedBindingTargets, COMPARE_FIELD_NAME);
247         } finally {
248             Scope.exit();
249         }
250     }
251 
resolveWhichExpressionsAreUsed()252     public void resolveWhichExpressionsAreUsed() {
253         List<Expr> used = new ArrayList<Expr>();
254         for (BindingTarget target : mBindingTargets) {
255             for (Binding binding : target.getBindings()) {
256                 binding.getExpr().markAsUsed();
257                 used.add(binding.getExpr());
258             }
259         }
260         while (!used.isEmpty()) {
261             Expr e = used.remove(used.size() - 1);
262             for (Dependency dep : e.getDependencies()) {
263                 if (!dep.getOther().isUsed()) {
264                     used.add(dep.getOther());
265                     dep.getOther().markAsUsed();
266                 }
267             }
268         }
269     }
270 
addVariable(String name, String type, Location location, boolean declared)271     public IdentifierExpr addVariable(String name, String type, Location location,
272             boolean declared) {
273         Preconditions.check(!mUserDefinedVariables.containsKey(name),
274                 "%s has already been defined as %s", name, type);
275         final IdentifierExpr id = mExprModel.identifier(name);
276         id.setUserDefinedType(type);
277         id.enableDirectInvalidation();
278         if (location != null) {
279             id.addLocation(location);
280         }
281         mUserDefinedVariables.put(name, type);
282         if (declared) {
283             id.setDeclared();
284         }
285         return id;
286     }
287 
getUserDefinedVariables()288     public HashMap<String, String> getUserDefinedVariables() {
289         return mUserDefinedVariables;
290     }
291 
createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle)292     public BindingTarget createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle) {
293         final BindingTarget target = new BindingTarget(targetBundle);
294         mBindingTargets.add(target);
295         target.setModel(mExprModel);
296         return target;
297     }
298 
parse(String input, @Nullable Location locationInFile, BindingTarget target)299     public Expr parse(String input, @Nullable Location locationInFile, BindingTarget target) {
300         final Expr parsed = mExpressionParser.parse(input, locationInFile, target);
301         parsed.setBindingExpression(true);
302         return parsed;
303     }
304 
getBindingTargets()305     public List<BindingTarget> getBindingTargets() {
306         return mBindingTargets;
307     }
308 
getSortedTargets()309     public List<BindingTarget> getSortedTargets() {
310         return mSortedBindingTargets;
311     }
312 
isEmpty()313     public boolean isEmpty() {
314         return mExprModel.size() == 0;
315     }
316 
getModel()317     public ExprModel getModel() {
318         return mExprModel;
319     }
320 
ensureWriter()321     private void ensureWriter() {
322         if (mWriter == null) {
323             mWriter = new LayoutBinderWriter(this);
324         }
325     }
326 
sealModel()327     public void sealModel() {
328         mExprModel.seal();
329     }
330 
writeViewBinderBaseClass(boolean forLibrary)331     public String writeViewBinderBaseClass(boolean forLibrary) {
332         ensureWriter();
333         return mWriter.writeBaseClass(forLibrary);
334     }
335 
writeViewBinder(int minSdk)336     public String writeViewBinder(int minSdk) {
337         ensureWriter();
338         Preconditions.checkNotNull(getPackage(), "package cannot be null");
339         Preconditions.checkNotNull(getClassName(), "base class name cannot be null");
340         return mWriter.write(minSdk);
341     }
342 
getPackage()343     public String getPackage() {
344         return mBundle.getBindingClassPackage();
345     }
346 
isMerge()347     public boolean isMerge() {
348         return mBundle.isMerge();
349     }
350 
getModulePackage()351     public String getModulePackage() {
352         return mModulePackage;
353     }
354 
getLayoutname()355     public String getLayoutname() {
356         return mBundle.getFileName();
357     }
358 
getImplementationName()359     public String getImplementationName() {
360         if (hasVariations()) {
361             return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl";
362         } else {
363             return mBundle.getBindingClassName();
364         }
365     }
366 
getClassName()367     public String getClassName() {
368         return mBundle.getBindingClassName();
369     }
370 
getTag()371     public String getTag() {
372         return mBundle.getDirectory() + "/" + mBundle.getFileName();
373     }
374 
hasVariations()375     public boolean hasVariations() {
376         return mBundle.hasVariations();
377     }
378 
379     @Override
provideScopeFilePath()380     public String provideScopeFilePath() {
381         return mBundle.getAbsoluteFilePath();
382     }
383 }
384