LayoutBinder.java revision c1560e6b00b398867da12fbdc5a1fcd1d50b801c
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
17package android.databinding.tool;
18
19import org.antlr.v4.runtime.misc.Nullable;
20
21import android.databinding.tool.expr.Dependency;
22import android.databinding.tool.expr.Expr;
23import android.databinding.tool.expr.ExprModel;
24import android.databinding.tool.expr.ExprModel.ResolveListenersCallback;
25import android.databinding.tool.expr.IdentifierExpr;
26import android.databinding.tool.store.Location;
27import android.databinding.tool.store.ResourceBundle;
28import android.databinding.tool.store.ResourceBundle.BindingTargetBundle;
29import android.databinding.tool.util.Preconditions;
30import android.databinding.tool.writer.LayoutBinderWriter;
31import android.databinding.tool.writer.WriterPackage;
32
33import java.util.ArrayList;
34import java.util.Collections;
35import java.util.Comparator;
36import java.util.HashMap;
37import java.util.List;
38import java.util.Map;
39
40/**
41 * Keeps all information about the bindings per layout file
42 */
43public class LayoutBinder implements ResolveListenersCallback {
44    private static final Comparator<BindingTarget> COMPARE_FIELD_NAME = new Comparator<BindingTarget>() {
45        @Override
46        public int compare(BindingTarget first, BindingTarget second) {
47            final String fieldName1 = WriterPackage.getFieldName(first);
48            final String fieldName2 = WriterPackage.getFieldName(second);
49            return fieldName1.compareTo(fieldName2);
50        }
51    };
52
53    /*
54    * val pkg: String, val projectPackage: String, val baseClassName: String,
55        val layoutName:String, val lb: LayoutExprBinding*/
56    private final ExprModel mExprModel;
57    private final ExpressionParser mExpressionParser;
58    private final List<BindingTarget> mBindingTargets;
59    private final List<BindingTarget> mSortedBindingTargets;
60    private String mModulePackage;
61    private final HashMap<String, String> mUserDefinedVariables = new HashMap<String, String>();
62
63    private LayoutBinderWriter mWriter;
64    private ResourceBundle.LayoutFileBundle mBundle;
65    private static final String[] sJavaLangClasses = {
66            "Deprecated",
67            "Override",
68            "SafeVarargs",
69            "SuppressWarnings",
70            "Appendable",
71            "AutoCloseable",
72            "CharSequence",
73            "Cloneable",
74            "Comparable",
75            "Iterable",
76            "Readable",
77            "Runnable",
78            "Thread.UncaughtExceptionHandler",
79            "Boolean",
80            "Byte",
81            "Character",
82            "Character.Subset",
83            "Character.UnicodeBlock",
84            "Class",
85            "ClassLoader",
86            "Compiler",
87            "Double",
88            "Enum",
89            "Float",
90            "InheritableThreadLocal",
91            "Integer",
92            "Long",
93            "Math",
94            "Number",
95            "Object",
96            "Package",
97            "Process",
98            "ProcessBuilder",
99            "Runtime",
100            "RuntimePermission",
101            "SecurityManager",
102            "Short",
103            "StackTraceElement",
104            "StrictMath",
105            "String",
106            "StringBuffer",
107            "StringBuilder",
108            "System",
109            "Thread",
110            "ThreadGroup",
111            "ThreadLocal",
112            "Throwable",
113            "Void",
114            "Thread.State",
115            "ArithmeticException",
116            "ArrayIndexOutOfBoundsException",
117            "ArrayStoreException",
118            "ClassCastException",
119            "ClassNotFoundException",
120            "CloneNotSupportedException",
121            "EnumConstantNotPresentException",
122            "Exception",
123            "IllegalAccessException",
124            "IllegalArgumentException",
125            "IllegalMonitorStateException",
126            "IllegalStateException",
127            "IllegalThreadStateException",
128            "IndexOutOfBoundsException",
129            "InstantiationException",
130            "InterruptedException",
131            "NegativeArraySizeException",
132            "NoSuchFieldException",
133            "NoSuchMethodException",
134            "NullPointerException",
135            "NumberFormatException",
136            "ReflectiveOperationException",
137            "RuntimeException",
138            "SecurityException",
139            "StringIndexOutOfBoundsException",
140            "TypeNotPresentException",
141            "UnsupportedOperationException",
142            "AbstractMethodError",
143            "AssertionError",
144            "ClassCircularityError",
145            "ClassFormatError",
146            "Error",
147            "ExceptionInInitializerError",
148            "IllegalAccessError",
149            "IncompatibleClassChangeError",
150            "InstantiationError",
151            "InternalError",
152            "LinkageError",
153            "NoClassDefFoundError",
154            "NoSuchFieldError",
155            "NoSuchMethodError",
156            "OutOfMemoryError",
157            "StackOverflowError",
158            "ThreadDeath",
159            "UnknownError",
160            "UnsatisfiedLinkError",
161            "UnsupportedClassVersionError",
162            "VerifyError",
163            "VirtualMachineError",
164    };
165
166    public LayoutBinder(ResourceBundle.LayoutFileBundle layoutBundle) {
167        mExprModel = new ExprModel();
168        mExpressionParser = new ExpressionParser(mExprModel);
169        mBindingTargets = new ArrayList<BindingTarget>();
170        mBundle = layoutBundle;
171        mModulePackage = layoutBundle.getModulePackage();
172        // copy over data.
173        for (ResourceBundle.NameTypeLocation variable : mBundle.getVariables()) {
174            addVariable(variable.name, variable.type, variable.location);
175        }
176
177        for (ResourceBundle.NameTypeLocation userImport : mBundle.getImports()) {
178            mExprModel.addImport(userImport.name, userImport.type, userImport.location);
179        }
180        for (String javaLangClass : sJavaLangClasses) {
181            mExprModel.addImport(javaLangClass, "java.lang." + javaLangClass, null);
182        }
183        for (BindingTargetBundle targetBundle : mBundle.getBindingTargetBundles()) {
184            final BindingTarget bindingTarget = createBindingTarget(targetBundle);
185            for (ResourceBundle.BindingTargetBundle.BindingBundle bindingBundle : targetBundle
186                    .getBindingBundleList()) {
187                bindingTarget.addBinding(bindingBundle.getName(), parse(bindingBundle.getExpr(),
188                        targetBundle.getLocation()));
189            }
190            bindingTarget.resolveMultiSetters();
191        }
192        mSortedBindingTargets = new ArrayList<BindingTarget>(mBindingTargets);
193        Collections.sort(mSortedBindingTargets, COMPARE_FIELD_NAME);
194    }
195
196    public void resolveWhichExpressionsAreUsed() {
197        List<Expr> used = new ArrayList<Expr>();
198        for (BindingTarget target : mBindingTargets) {
199            for (Binding binding : target.getBindings()) {
200                binding.getExpr().setIsUsed(true);
201                used.add(binding.getExpr());
202            }
203        }
204        while (!used.isEmpty()) {
205            Expr e = used.remove(used.size() - 1);
206            for (Dependency dep : e.getDependencies()) {
207                if (!dep.getOther().isUsed()) {
208                    used.add(dep.getOther());
209                    dep.getOther().setIsUsed(true);
210                }
211            }
212        }
213    }
214
215    public IdentifierExpr addVariable(String name, String type, Location location) {
216        Preconditions.check(!mUserDefinedVariables.containsKey(name),
217                "%s has already been defined as %s", name, type);
218        final IdentifierExpr id = mExprModel.identifier(name);
219        id.setUserDefinedType(type);
220        id.enableDirectInvalidation();
221        if (location != null) {
222            id.addLocation(location);
223        }
224        mUserDefinedVariables.put(name, type);
225        return id;
226    }
227
228    public HashMap<String, String> getUserDefinedVariables() {
229        return mUserDefinedVariables;
230    }
231
232    public BindingTarget createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle) {
233        final BindingTarget target = new BindingTarget(targetBundle);
234        mBindingTargets.add(target);
235        target.setModel(mExprModel);
236        return target;
237    }
238
239    public Expr parse(String input, @Nullable Location locationInFile) {
240        final Expr parsed = mExpressionParser.parse(input, locationInFile);
241        parsed.setBindingExpression(true);
242        return parsed;
243    }
244
245    public List<BindingTarget> getBindingTargets() {
246        return mBindingTargets;
247    }
248
249    public List<BindingTarget> getSortedTargets() {
250        return mSortedBindingTargets;
251    }
252
253    public boolean isEmpty() {
254        return mExprModel.size() == 0;
255    }
256
257    public ExprModel getModel() {
258        return mExprModel;
259    }
260
261    private void ensureWriter() {
262        if (mWriter == null) {
263            mWriter = new LayoutBinderWriter(this);
264        }
265    }
266
267    public String writeViewBinderBaseClass(boolean forLibrary) {
268        ensureWriter();
269        return mWriter.writeBaseClass(forLibrary);
270    }
271
272
273    public String writeViewBinder(int minSdk) {
274        mExprModel.seal(this);
275        ensureWriter();
276        Preconditions.checkNotNull(getPackage(), "package cannot be null");
277        Preconditions.checkNotNull(getClassName(), "base class name cannot be null");
278        return mWriter.write(minSdk);
279    }
280
281    public String getPackage() {
282        return mBundle.getBindingClassPackage();
283    }
284
285    public boolean isMerge() {
286        return mBundle.isMerge();
287    }
288
289    public String getModulePackage() {
290        return mModulePackage;
291    }
292
293    public String getLayoutname() {
294        return mBundle.getFileName();
295    }
296
297    public String getImplementationName() {
298        if (hasVariations()) {
299            return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl";
300        } else {
301            return mBundle.getBindingClassName();
302        }
303    }
304
305    public String getClassName() {
306        return mBundle.getBindingClassName();
307    }
308
309    public String getTag() {
310        return mBundle.getDirectory() + "/" + mBundle.getFileName();
311    }
312
313    public boolean hasVariations() {
314        return mBundle.hasVariations();
315    }
316
317    @Override
318    public void resolveListeners() {
319        for (BindingTarget target : mBindingTargets) {
320            for (Binding binding : target.getBindings()) {
321                binding.resolveListeners();
322            }
323        }
324    }
325}
326