ExprModel.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.expr;
18
19import com.google.common.base.Preconditions;
20import com.google.common.base.Predicate;
21import com.google.common.collect.Iterables;
22import com.google.common.collect.Lists;
23
24import android.databinding.tool.reflection.ModelAnalyzer;
25import android.databinding.tool.util.L;
26import android.databinding.tool.writer.FlagSet;
27
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.BitSet;
31import java.util.HashMap;
32import java.util.List;
33import java.util.Map;
34
35public class ExprModel {
36
37    Map<String, Expr> mExprMap = new HashMap<String, Expr>();
38
39    List<Expr> mBindingExpressions = new ArrayList<Expr>();
40
41    private int mInvalidateableFieldLimit = 0;
42
43    private int mRequirementIdCount = 0;
44
45    // each arg list receives a unique id even if it is the same arguments and method.
46    private int mArgListIdCounter = 0;
47
48    private static final String TRUE_KEY_SUFFIX = "== true";
49    private static final String FALSE_KEY_SUFFIX = "== false";
50
51    /**
52     * Any expression can be invalidated by invalidating this flag.
53     */
54    private BitSet mInvalidateAnyFlags;
55
56    /**
57     * Used by code generation. Keeps the list of expressions that are waiting to be evaluated.
58     */
59    private List<Expr> mPendingExpressions;
60
61    /**
62     * Used for converting flags into identifiers while debugging.
63     */
64    private String[] mFlagMapping;
65
66    private BitSet mInvalidateableFlags;
67    private BitSet mConditionalFlags;
68
69    private int mFlagBucketCount;// how many buckets we use to identify flags
70
71    private List<Expr> mObservables;
72
73    private boolean mSealed = false;
74
75    private Map<String, String> mImports = new HashMap<String, String>();
76
77    /**
78     * Adds the expression to the list of expressions and returns it.
79     * If it already exists, returns existing one.
80     *
81     * @param expr The new parsed expression
82     * @return The expression itself or another one if the same thing was parsed before
83     */
84    public <T extends Expr> T register(T expr) {
85        Preconditions.checkState(!mSealed, "Cannot add expressions to a model after it is sealed");
86        T existing = (T) mExprMap.get(expr.getUniqueKey());
87        if (existing != null) {
88            Preconditions.checkState(expr.getParents().isEmpty(),
89                    "If an expression already exists, it should've never been added to a parent,"
90                            + "if thats the case, somewhere we are creating an expression w/o"
91                            + "calling expression model");
92            // tell the expr that it is being swapped so that if it was added to some other expr
93            // as a parent, those can swap their references
94            expr.onSwappedWith(existing);
95            return existing;
96        }
97        mExprMap.put(expr.getUniqueKey(), expr);
98        expr.setModel(this);
99        return expr;
100    }
101
102    public void unregister(Expr expr) {
103        mExprMap.remove(expr.getUniqueKey());
104    }
105
106    public Map<String, Expr> getExprMap() {
107        return mExprMap;
108    }
109
110    public int size() {
111        return mExprMap.size();
112    }
113
114    public ComparisonExpr comparison(String op, Expr left, Expr right) {
115        return register(new ComparisonExpr(op, left, right));
116    }
117
118    public InstanceOfExpr instanceOfOp(Expr expr, String type) {
119        return register(new InstanceOfExpr(expr, type));
120    }
121
122    public FieldAccessExpr field(Expr parent, String name) {
123        return register(new FieldAccessExpr(parent, name));
124    }
125
126    public FieldAccessExpr observableField(Expr parent, String name) {
127        return register(new FieldAccessExpr(parent, name, true));
128    }
129
130    public SymbolExpr symbol(String text, Class type) {
131        return register(new SymbolExpr(text, type));
132    }
133
134    public TernaryExpr ternary(Expr pred, Expr ifTrue, Expr ifFalse) {
135        return register(new TernaryExpr(pred, ifTrue, ifFalse));
136    }
137
138    public IdentifierExpr identifier(String name) {
139        return register(new IdentifierExpr(name));
140    }
141
142    public StaticIdentifierExpr staticIdentifier(String name) {
143        return register(new StaticIdentifierExpr(name));
144    }
145
146    public MethodCallExpr methodCall(Expr target, String name, List<Expr> args) {
147        return register(new MethodCallExpr(target, name, args));
148    }
149
150    public MathExpr math(Expr left, String op, Expr right) {
151        return register(new MathExpr(left, op, right));
152    }
153
154    public TernaryExpr logical(Expr left, String op, Expr right) {
155        if ("&&".equals(op)) {
156            // left && right
157            // left ? right : false
158            return register(new TernaryExpr(left, right, symbol("false", boolean.class)));
159        } else {
160            // left || right
161            // left ? true : right
162            return register(new TernaryExpr(left, symbol("true", boolean.class), right));
163        }
164    }
165
166    public BitShiftExpr bitshift(Expr left, String op, Expr right) {
167        return register(new BitShiftExpr(left, op, right));
168    }
169
170    public UnaryExpr unary(String op, Expr expr) {
171        return register(new UnaryExpr(op, expr));
172    }
173
174    public Expr group(Expr grouped) {
175        return register(new GroupExpr(grouped));
176    }
177
178    public Expr resourceExpr(String packageName, String resourceType, String resourceName,
179            List<Expr> args) {
180        return register(new ResourceExpr(packageName, resourceType, resourceName, args));
181    }
182
183    public Expr bracketExpr(Expr variableExpr, Expr argExpr) {
184        return register(new BracketExpr(variableExpr, argExpr));
185    }
186
187    public Expr castExpr(String type, Expr expr) {
188        return register(new CastExpr(type, expr));
189    }
190
191    public List<Expr> getBindingExpressions() {
192        return mBindingExpressions;
193    }
194
195    public void addImport(String alias, String type) {
196        Preconditions.checkState(!mImports.containsKey(alias),
197                "%s has already been defined as %s", alias, type);
198        final StaticIdentifierExpr id = staticIdentifier(alias);
199        L.d("adding import %s as %s klass: %s", type, alias, id.getClass().getSimpleName());
200        id.setUserDefinedType(type);
201        mImports.put(alias, type);
202    }
203
204    public Map<String, String> getImports() {
205        return mImports;
206    }
207
208    /**
209     * The actual thingy that is set on the binding target.
210     *
211     * Input must be already registered
212     */
213    public Expr bindingExpr(Expr bindingExpr) {
214        Preconditions.checkArgument(mExprMap.containsKey(bindingExpr.getUniqueKey()),
215                "Main expression should already be registered");
216        if (!mBindingExpressions.contains(bindingExpr)) {
217            mBindingExpressions.add(bindingExpr);
218        }
219        return bindingExpr;
220    }
221
222    /**
223     * Nodes to which no one depends
224     */
225    public Iterable<Expr> findRootNodes() {
226        return Iterables.filter(mExprMap.values(), new Predicate<Expr>() {
227            @Override
228            public boolean apply(Expr input) {
229                return input.getParents().isEmpty();
230            }
231        });
232    }
233
234    /**
235     * Nodes, which do not depend on any other node
236     */
237    public Iterable<Expr> findLeafNodes() {
238        return Iterables.filter(mExprMap.values(), new Predicate<Expr>() {
239            @Override
240            public boolean apply(Expr input) {
241                return input.getChildren().isEmpty();
242            }
243        });
244    }
245
246    public List<Expr> getObservables() {
247        return mObservables;
248    }
249
250    /**
251     * Give id to each expression. Will be useful if we serialize.
252     */
253    public void seal() {
254        L.d("sealing model");
255        List<Expr> notifiableExpressions = new ArrayList<Expr>();
256        //ensure class analyzer. We need to know observables at this point
257        final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
258
259        ArrayList<Expr> exprs = new ArrayList<Expr>(mBindingExpressions);
260        for (Expr expr: exprs) {
261            expr.updateExpr(modelAnalyzer);
262        }
263
264        int counter = 0;
265        final Iterable<Expr> observables = filterObservables(modelAnalyzer);
266        List<String> flagMapping = Lists.newArrayList();
267        mObservables = Lists.newArrayList();
268        for (Expr expr : observables) {
269            // observables gets initial ids
270            flagMapping.add(expr.getUniqueKey());
271            expr.setId(counter++);
272            mObservables.add(expr);
273            notifiableExpressions.add(expr);
274            L.d("observable %s", expr.getUniqueKey());
275        }
276
277        // non-observable identifiers gets next ids
278        final Iterable<Expr> nonObservableIds = filterNonObservableIds(modelAnalyzer);
279        for (Expr expr : nonObservableIds) {
280            flagMapping.add(expr.getUniqueKey());
281            expr.setId(counter++);
282            notifiableExpressions.add(expr);
283            L.d("non-observable %s", expr.getUniqueKey());
284        }
285
286        // descendents of observables gets following ids
287        for (Expr expr : observables) {
288            for (Expr parent : expr.getParents()) {
289                if (parent.hasId()) {
290                    continue;// already has some id, means observable
291                }
292                // only fields earn an id
293                if (parent instanceof FieldAccessExpr) {
294                    FieldAccessExpr fae = (FieldAccessExpr) parent;
295                    L.d("checking field access expr %s. getter: %s", fae,fae.getGetter());
296                    if (fae.isDynamic() && fae.getGetter().canBeInvalidated()) {
297                        flagMapping.add(parent.getUniqueKey());
298                        parent.setId(counter++);
299                        notifiableExpressions.add(parent);
300                        L.d("notifiable field %s : %s for %s : %s", parent.getUniqueKey(),
301                                Integer.toHexString(System.identityHashCode(parent)),
302                                expr.getUniqueKey(),
303                                Integer.toHexString(System.identityHashCode(expr)));
304                    }
305                }
306            }
307        }
308
309        // non-dynamic binding expressions receive some ids so that they can be invalidated
310        L.d("list of binding expressions");
311        for (int i = 0; i < mBindingExpressions.size(); i++) {
312            L.d("[%d] %s", i, mBindingExpressions.get(i));
313        }
314        // we don't assign ids to constant binding expressions because now invalidateAll has its own
315        // flag.
316
317        for (Expr expr : notifiableExpressions) {
318            expr.enableDirectInvalidation();
319        }
320
321        // make sure all dependencies are resolved to avoid future race conditions
322        for (Expr expr : mExprMap.values()) {
323            expr.getDependencies();
324        }
325        final int invalidateAnyFlagIndex = counter ++;
326        flagMapping.add("INVALIDATE ANY");
327        mInvalidateableFieldLimit = counter;
328        mInvalidateableFlags = new BitSet();
329        for (int i = 0; i < mInvalidateableFieldLimit; i++) {
330            mInvalidateableFlags.set(i, true);
331        }
332
333        // make sure all dependencies are resolved to avoid future race conditions
334        for (Expr expr : mExprMap.values()) {
335            if (expr.isConditional()) {
336                L.d("requirement id for %s is %d", expr, counter);
337                expr.setRequirementId(counter);
338                flagMapping.add(expr.getUniqueKey() + FALSE_KEY_SUFFIX);
339                flagMapping.add(expr.getUniqueKey() + TRUE_KEY_SUFFIX);
340                counter += 2;
341            }
342        }
343        mConditionalFlags = new BitSet();
344        for (int i = mInvalidateableFieldLimit; i < counter; i++) {
345            mConditionalFlags.set(i, true);
346        }
347        mRequirementIdCount = (counter - mInvalidateableFieldLimit) / 2;
348
349        // everybody gets an id
350        for (Map.Entry<String, Expr> entry : mExprMap.entrySet()) {
351            final Expr value = entry.getValue();
352            if (!value.hasId()) {
353                value.setId(counter++);
354            }
355        }
356
357        mFlagMapping = new String[flagMapping.size()];
358        flagMapping.toArray(mFlagMapping);
359
360        mFlagBucketCount = 1 + (getTotalFlagCount() / FlagSet.sBucketSize);
361        mInvalidateAnyFlags = new BitSet();
362        mInvalidateAnyFlags.set(invalidateAnyFlagIndex, true);
363
364        for (Expr expr : mExprMap.values()) {
365            expr.getShouldReadFlagsWithConditionals();
366        }
367
368        for (Expr expr : mExprMap.values()) {
369            // ensure all types are calculated
370            expr.getResolvedType();
371        }
372
373        mSealed = true;
374    }
375
376    public int getFlagBucketCount() {
377        return mFlagBucketCount;
378    }
379
380    public int getTotalFlagCount() {
381        return mRequirementIdCount * 2 + mInvalidateableFieldLimit;
382    }
383
384    public int getInvalidateableFieldLimit() {
385        return mInvalidateableFieldLimit;
386    }
387
388    public String[] getFlagMapping() {
389        return mFlagMapping;
390    }
391
392    public String getFlag(int id) {
393        return mFlagMapping[id];
394    }
395
396    private Iterable<Expr> filterNonObservableIds(final ModelAnalyzer modelAnalyzer) {
397        return Iterables.filter(mExprMap.values(), new Predicate<Expr>() {
398            @Override
399            public boolean apply(Expr input) {
400                return input instanceof IdentifierExpr
401                        && !input.hasId()
402                        && !input.isObservable()
403                        && input.isDynamic();
404            }
405        });
406    }
407
408    private Iterable<Expr> filterObservables(final ModelAnalyzer modelAnalyzer) {
409        return Iterables.filter(mExprMap.values(), new Predicate<Expr>() {
410            @Override
411            public boolean apply(Expr input) {
412                return input.isObservable();
413            }
414        });
415    }
416
417    public List<Expr> getPendingExpressions() {
418        if (mPendingExpressions == null) {
419            mPendingExpressions = Lists.newArrayList();
420            for (Expr expr : mExprMap.values()) {
421                if (!expr.isRead() && expr.isDynamic()) {
422                    mPendingExpressions.add(expr);
423                }
424            }
425        }
426        return mPendingExpressions;
427    }
428
429    public boolean markBitsRead() {
430        // each has should read flags, we set them back on them
431        for (Expr expr : filterShouldRead(getPendingExpressions())) {
432            expr.markFlagsAsRead(expr.getShouldReadFlags());
433        }
434        return pruneDone();
435    }
436
437    private boolean pruneDone() {
438        boolean marked = true;
439        List<Expr> markedAsReadList = Lists.newArrayList();
440        while (marked) {
441            marked = false;
442            for (Expr expr : mExprMap.values()) {
443                if (expr.isRead()) {
444                    continue;
445                }
446                if (expr.markAsReadIfDone()) {
447                    L.d("marked %s as read ", expr.getUniqueKey());
448                    marked = true;
449                    markedAsReadList.add(expr);
450                }
451
452            }
453        }
454        boolean elevated = false;
455        for (Expr markedAsRead : markedAsReadList) {
456            for (Dependency dependency : markedAsRead.getDependants()) {
457                if (dependency.getDependant().considerElevatingConditionals(markedAsRead)) {
458                    elevated = true;
459                }
460            }
461        }
462        if (elevated) {
463            // some conditionals are elevated. We should re-calculate flags
464            for (Expr expr : getPendingExpressions()) {
465                if (!expr.isRead()) {
466                    expr.invalidateReadFlags();
467                }
468            }
469            mPendingExpressions = null;
470        }
471        return elevated;
472    }
473
474    public static Iterable<Expr> filterShouldRead(Iterable<Expr> exprs) {
475        return toCollection(Iterables.filter(exprs, sShouldReadPred));
476    }
477
478    public static List<Expr> toCollection(Iterable<Expr> iterable) {
479        return Arrays.asList(Iterables.toArray(iterable, Expr.class));
480    }
481
482    private static final Predicate<Expr> sShouldReadPred = new Predicate<Expr>() {
483        @Override
484        public boolean apply(final Expr expr) {
485            return !expr.getShouldReadFlags().isEmpty() && !Iterables.any(
486                    expr.getDependencies(), new Predicate<Dependency>() {
487                        @Override
488                        public boolean apply(Dependency dependency) {
489                            final boolean result = dependency.isConditional() ||
490                                    dependency.getOther().hasNestedCannotRead();
491                            return result;
492                        }
493                    });
494        }
495    };
496
497    /**
498     * May return null if flag is equal to invalidate any flag.
499     */
500    public Expr findFlagExpression(int flag) {
501        if (mInvalidateAnyFlags.get(flag)) {
502            return null;
503        }
504        final String key = mFlagMapping[flag];
505        if (mExprMap.containsKey(key)) {
506            return mExprMap.get(key);
507        }
508        int falseIndex = key.indexOf(FALSE_KEY_SUFFIX);
509        if (falseIndex > -1) {
510            final String trimmed = key.substring(0, falseIndex);
511            return mExprMap.get(trimmed);
512        }
513        int trueIndex = key.indexOf(TRUE_KEY_SUFFIX);
514        if (trueIndex > -1) {
515            final String trimmed = key.substring(0, trueIndex);
516            return mExprMap.get(trimmed);
517        }
518        // log everything we call
519        StringBuilder error = new StringBuilder();
520        error.append("cannot find flag:").append(flag).append("\n");
521        error.append("invalidate any flag:").append(mInvalidateAnyFlags).append("\n");
522        error.append("key:").append(key).append("\n");
523        error.append("flag mapping:").append(Arrays.toString(mFlagMapping));
524        Preconditions.checkArgument(false, error.toString());
525        return null;
526    }
527
528    public BitSet getInvalidateAnyBitSet() {
529        return mInvalidateAnyFlags;
530    }
531
532    public Expr argListExpr(Iterable<Expr> expressions) {
533        return register(new ArgListExpr(mArgListIdCounter ++, expressions));
534    }
535}
536