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