LayoutBinder.java revision 7c1b078ca84336caba7f811709836562bd5550d6
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.IdentifierExpr;
25import android.databinding.tool.processing.Scope;
26import android.databinding.tool.processing.scopes.FileScopeProvider;
27import android.databinding.tool.store.Location;
28import android.databinding.tool.store.ResourceBundle;
29import android.databinding.tool.store.ResourceBundle.BindingTargetBundle;
30import android.databinding.tool.util.Preconditions;
31import android.databinding.tool.writer.LayoutBinderWriter;
32import android.databinding.tool.writer.WriterPackage;
33
34import java.util.ArrayList;
35import java.util.Collections;
36import java.util.Comparator;
37import java.util.HashMap;
38import java.util.List;
39
40/**
41 * Keeps all information about the bindings per layout file
42 */
43public class LayoutBinder implements FileScopeProvider {
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        try {
168            Scope.enter(this);
169            mExprModel = new ExprModel();
170            mExpressionParser = new ExpressionParser(mExprModel);
171            mBindingTargets = new ArrayList<BindingTarget>();
172            mBundle = layoutBundle;
173            mModulePackage = layoutBundle.getModulePackage();
174            // copy over data.
175            boolean addContext = true;
176            for (ResourceBundle.VariableDeclaration variable : mBundle.getVariables()) {
177                addVariable(variable.name, variable.type, variable.location, variable.declared);
178                if ("context".equals(variable.name)) {
179                    addContext = false;
180                }
181            }
182
183            for (ResourceBundle.NameTypeLocation userImport : mBundle.getImports()) {
184                mExprModel.addImport(userImport.name, userImport.type, userImport.location);
185                if ("context".equals(userImport.name)) {
186                    addContext = false;
187                }
188            }
189            if (addContext) {
190                mExprModel.builtInVariable("context", "android.content.Context",
191                        "getRoot().getContext()");
192            }
193            for (String javaLangClass : sJavaLangClasses) {
194                mExprModel.addImport(javaLangClass, "java.lang." + javaLangClass, null);
195            }
196            for (BindingTargetBundle targetBundle : mBundle.getBindingTargetBundles()) {
197                try {
198                    Scope.enter(targetBundle);
199                    final BindingTarget bindingTarget = createBindingTarget(targetBundle);
200                    for (BindingTargetBundle.BindingBundle bindingBundle : targetBundle
201                            .getBindingBundleList()) {
202                        bindingTarget.addBinding(bindingBundle.getName(),
203                                parse(bindingBundle.getExpr(), bindingBundle.getValueLocation()));
204                    }
205                    bindingTarget.resolveMultiSetters();
206                } finally {
207                    Scope.exit();
208                }
209            }
210            mSortedBindingTargets = new ArrayList<BindingTarget>(mBindingTargets);
211            Collections.sort(mSortedBindingTargets, COMPARE_FIELD_NAME);
212        } finally {
213            Scope.exit();
214        }
215    }
216
217    public void resolveWhichExpressionsAreUsed() {
218        List<Expr> used = new ArrayList<Expr>();
219        for (BindingTarget target : mBindingTargets) {
220            for (Binding binding : target.getBindings()) {
221                binding.getExpr().setIsUsed(true);
222                used.add(binding.getExpr());
223            }
224        }
225        while (!used.isEmpty()) {
226            Expr e = used.remove(used.size() - 1);
227            for (Dependency dep : e.getDependencies()) {
228                if (!dep.getOther().isUsed()) {
229                    used.add(dep.getOther());
230                    dep.getOther().setIsUsed(true);
231                }
232            }
233        }
234    }
235
236    public IdentifierExpr addVariable(String name, String type, Location location,
237            boolean declared) {
238        Preconditions.check(!mUserDefinedVariables.containsKey(name),
239                "%s has already been defined as %s", name, type);
240        final IdentifierExpr id = mExprModel.identifier(name);
241        id.setUserDefinedType(type);
242        id.enableDirectInvalidation();
243        if (location != null) {
244            id.addLocation(location);
245        }
246        mUserDefinedVariables.put(name, type);
247        if (declared) {
248            id.setDeclared();
249        }
250        return id;
251    }
252
253    public HashMap<String, String> getUserDefinedVariables() {
254        return mUserDefinedVariables;
255    }
256
257    public BindingTarget createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle) {
258        final BindingTarget target = new BindingTarget(targetBundle);
259        mBindingTargets.add(target);
260        target.setModel(mExprModel);
261        return target;
262    }
263
264    public Expr parse(String input, @Nullable Location locationInFile) {
265        final Expr parsed = mExpressionParser.parse(input, locationInFile);
266        parsed.setBindingExpression(true);
267        return parsed;
268    }
269
270    public List<BindingTarget> getBindingTargets() {
271        return mBindingTargets;
272    }
273
274    public List<BindingTarget> getSortedTargets() {
275        return mSortedBindingTargets;
276    }
277
278    public boolean isEmpty() {
279        return mExprModel.size() == 0;
280    }
281
282    public ExprModel getModel() {
283        return mExprModel;
284    }
285
286    private void ensureWriter() {
287        if (mWriter == null) {
288            mWriter = new LayoutBinderWriter(this);
289        }
290    }
291
292    public void sealModel() {
293        mExprModel.seal();
294    }
295
296    public String writeViewBinderBaseClass(boolean forLibrary) {
297        ensureWriter();
298        return mWriter.writeBaseClass(forLibrary);
299    }
300
301    public String writeViewBinder(int minSdk) {
302        ensureWriter();
303        Preconditions.checkNotNull(getPackage(), "package cannot be null");
304        Preconditions.checkNotNull(getClassName(), "base class name cannot be null");
305        return mWriter.write(minSdk);
306    }
307
308    public String getPackage() {
309        return mBundle.getBindingClassPackage();
310    }
311
312    public boolean isMerge() {
313        return mBundle.isMerge();
314    }
315
316    public String getModulePackage() {
317        return mModulePackage;
318    }
319
320    public String getLayoutname() {
321        return mBundle.getFileName();
322    }
323
324    public String getImplementationName() {
325        if (hasVariations()) {
326            return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl";
327        } else {
328            return mBundle.getBindingClassName();
329        }
330    }
331
332    public String getClassName() {
333        return mBundle.getBindingClassName();
334    }
335
336    public String getTag() {
337        return mBundle.getDirectory() + "/" + mBundle.getFileName();
338    }
339
340    public boolean hasVariations() {
341        return mBundle.hasVariations();
342    }
343
344    @Override
345    public String provideScopeFilePath() {
346        return mBundle.getAbsoluteFilePath();
347    }
348}
349