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