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