LayoutBinder.java revision 4df4ba38a62b791bbbc25e923efe8d9c2f9a52e9
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    public String writeViewBinder(int minSdk) {
273        mExprModel.seal(this);
274        ensureWriter();
275        Preconditions.checkNotNull(getPackage(), "package cannot be null");
276        Preconditions.checkNotNull(getClassName(), "base class name cannot be null");
277        return mWriter.write(minSdk);
278    }
279
280    public String getPackage() {
281        return mBundle.getBindingClassPackage();
282    }
283
284    public boolean isMerge() {
285        return mBundle.isMerge();
286    }
287
288    public String getModulePackage() {
289        return mModulePackage;
290    }
291
292    public String getLayoutname() {
293        return mBundle.getFileName();
294    }
295
296    public String getImplementationName() {
297        if (hasVariations()) {
298            return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl";
299        } else {
300            return mBundle.getBindingClassName();
301        }
302    }
303
304    public String getClassName() {
305        return mBundle.getBindingClassName();
306    }
307
308    public String getTag() {
309        return mBundle.getDirectory() + "/" + mBundle.getFileName();
310    }
311
312    public boolean hasVariations() {
313        return mBundle.hasVariations();
314    }
315
316    @Override
317    public void resolveListeners() {
318        for (BindingTarget target : mBindingTargets) {
319            for (Binding binding : target.getBindings()) {
320                binding.resolveListeners();
321            }
322        }
323    }
324}
325