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