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