LayoutBinder.java revision 975c05c00dae9193a636c4775df81d8665fc6749
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                    bindingTarget.resolveListeners();
207                } finally {
208                    Scope.exit();
209                }
210            }
211            mSortedBindingTargets = new ArrayList<BindingTarget>(mBindingTargets);
212            Collections.sort(mSortedBindingTargets, COMPARE_FIELD_NAME);
213        } finally {
214            Scope.exit();
215        }
216    }
217
218    public void resolveWhichExpressionsAreUsed() {
219        List<Expr> used = new ArrayList<Expr>();
220        for (BindingTarget target : mBindingTargets) {
221            for (Binding binding : target.getBindings()) {
222                binding.getExpr().setIsUsed(true);
223                used.add(binding.getExpr());
224            }
225        }
226        while (!used.isEmpty()) {
227            Expr e = used.remove(used.size() - 1);
228            for (Dependency dep : e.getDependencies()) {
229                if (!dep.getOther().isUsed()) {
230                    used.add(dep.getOther());
231                    dep.getOther().setIsUsed(true);
232                }
233            }
234        }
235    }
236
237    public IdentifierExpr addVariable(String name, String type, Location location,
238            boolean declared) {
239        Preconditions.check(!mUserDefinedVariables.containsKey(name),
240                "%s has already been defined as %s", name, type);
241        final IdentifierExpr id = mExprModel.identifier(name);
242        id.setUserDefinedType(type);
243        id.enableDirectInvalidation();
244        if (location != null) {
245            id.addLocation(location);
246        }
247        mUserDefinedVariables.put(name, type);
248        if (declared) {
249            id.setDeclared();
250        }
251        return id;
252    }
253
254    public HashMap<String, String> getUserDefinedVariables() {
255        return mUserDefinedVariables;
256    }
257
258    public BindingTarget createBindingTarget(ResourceBundle.BindingTargetBundle targetBundle) {
259        final BindingTarget target = new BindingTarget(targetBundle);
260        mBindingTargets.add(target);
261        target.setModel(mExprModel);
262        return target;
263    }
264
265    public Expr parse(String input, @Nullable Location locationInFile) {
266        final Expr parsed = mExpressionParser.parse(input, locationInFile);
267        parsed.setBindingExpression(true);
268        return parsed;
269    }
270
271    public List<BindingTarget> getBindingTargets() {
272        return mBindingTargets;
273    }
274
275    public List<BindingTarget> getSortedTargets() {
276        return mSortedBindingTargets;
277    }
278
279    public boolean isEmpty() {
280        return mExprModel.size() == 0;
281    }
282
283    public ExprModel getModel() {
284        return mExprModel;
285    }
286
287    private void ensureWriter() {
288        if (mWriter == null) {
289            mWriter = new LayoutBinderWriter(this);
290        }
291    }
292
293    public void sealModel() {
294        mExprModel.seal();
295    }
296
297    public String writeViewBinderBaseClass(boolean forLibrary) {
298        ensureWriter();
299        return mWriter.writeBaseClass(forLibrary);
300    }
301
302    public String writeViewBinder(int minSdk) {
303        ensureWriter();
304        Preconditions.checkNotNull(getPackage(), "package cannot be null");
305        Preconditions.checkNotNull(getClassName(), "base class name cannot be null");
306        return mWriter.write(minSdk);
307    }
308
309    public String getPackage() {
310        return mBundle.getBindingClassPackage();
311    }
312
313    public boolean isMerge() {
314        return mBundle.isMerge();
315    }
316
317    public String getModulePackage() {
318        return mModulePackage;
319    }
320
321    public String getLayoutname() {
322        return mBundle.getFileName();
323    }
324
325    public String getImplementationName() {
326        if (hasVariations()) {
327            return mBundle.getBindingClassName() + mBundle.getConfigName() + "Impl";
328        } else {
329            return mBundle.getBindingClassName();
330        }
331    }
332
333    public String getClassName() {
334        return mBundle.getBindingClassName();
335    }
336
337    public String getTag() {
338        return mBundle.getDirectory() + "/" + mBundle.getFileName();
339    }
340
341    public boolean hasVariations() {
342        return mBundle.hasVariations();
343    }
344
345    @Override
346    public String provideScopeFilePath() {
347        return mBundle.getAbsoluteFilePath();
348    }
349}
350