• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.databinding.tool.expr;
18 
19 import android.databinding.tool.processing.ErrorMessages;
20 import android.databinding.tool.processing.Scope;
21 import android.databinding.tool.processing.scopes.LocationScopeProvider;
22 import android.databinding.tool.reflection.ModelAnalyzer;
23 import android.databinding.tool.reflection.ModelClass;
24 import android.databinding.tool.solver.ExecutionPath;
25 import android.databinding.tool.store.Location;
26 import android.databinding.tool.util.L;
27 import android.databinding.tool.util.Preconditions;
28 import android.databinding.tool.writer.KCode;
29 import android.databinding.tool.writer.LayoutBinderWriterKt;
30 
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33 
34 import java.util.ArrayList;
35 import java.util.BitSet;
36 import java.util.Collections;
37 import java.util.List;
38 import java.util.Map;
39 
40 abstract public class Expr implements VersionProvider, LocationScopeProvider {
41     public static final int NO_ID = -1;
42     protected List<Expr> mChildren = new ArrayList<Expr>();
43 
44     // any expression that refers to this. Useful if this expr is duplicate and being replaced
45     private List<Expr> mParents = new ArrayList<Expr>();
46 
47     private Boolean mIsDynamic;
48 
49     private ModelClass mResolvedType;
50 
51     private String mUniqueKey;
52 
53     private List<Dependency> mDependencies;
54 
55     private List<Dependency> mDependants = new ArrayList<Dependency>();
56 
57     private int mId = NO_ID;
58 
59     private int mRequirementId = NO_ID;
60 
61     private int mVersion = 0;
62 
63     // means this expression can directly be invalidated by the user
64     private boolean mCanBeInvalidated = false;
65 
66     @Nullable
67     private List<Location> mLocations = new ArrayList<Location>();
68 
69     /**
70      * This set denotes the times when this expression is invalid.
71      * If it is an Identifier expression, it is its index
72      * If it is a composite expression, it is the union of invalid flags of its descendants
73      */
74     private BitSet mInvalidFlags;
75 
76     /**
77      * Set when this expression is registered to a model
78      */
79     private ExprModel mModel;
80 
81     /**
82      * This set denotes the times when this expression must be read.
83      *
84      * It is the union of invalidation flags of all of its non-conditional dependants.
85      */
86     BitSet mShouldReadFlags;
87 
88     BitSet mReadSoFar = new BitSet();// i've read this variable for these flags
89 
90     /**
91      * calculated on initialization, assuming all conditionals are true
92      */
93     BitSet mShouldReadWithConditionals;
94 
95     private boolean mIsBindingExpression;
96 
97     /**
98      * Used by generators when this expression is resolved.
99      */
100     private boolean mRead;
101     private boolean mIsUsed = false;
102     private boolean mIsUsedInCallback = false;
103 
Expr(Iterable<Expr> children)104     Expr(Iterable<Expr> children) {
105         for (Expr expr : children) {
106             mChildren.add(expr);
107         }
108         addParents();
109     }
110 
Expr(Expr... children)111     Expr(Expr... children) {
112         Collections.addAll(mChildren, children);
113         addParents();
114     }
115 
getId()116     public int getId() {
117         Preconditions.check(mId != NO_ID, "if getId is called on an expression, it should have"
118                 + " an id: %s", this);
119         return mId;
120     }
121 
setId(int id)122     public void setId(int id) {
123         Preconditions.check(mId == NO_ID, "ID is already set on %s", this);
124         mId = id;
125     }
126 
addLocation(Location location)127     public void addLocation(Location location) {
128         mLocations.add(location);
129     }
130 
getLocations()131     public List<Location> getLocations() {
132         return mLocations;
133     }
134 
getModel()135     public ExprModel getModel() {
136         return mModel;
137     }
138 
getInvalidFlags()139     public BitSet getInvalidFlags() {
140         if (mInvalidFlags == null) {
141             mInvalidFlags = resolveInvalidFlags();
142         }
143         return mInvalidFlags;
144     }
145 
resolveInvalidFlags()146     private BitSet resolveInvalidFlags() {
147         BitSet bitSet = (BitSet) mModel.getInvalidateAnyBitSet().clone();
148         if (mCanBeInvalidated) {
149             bitSet.set(getId(), true);
150         }
151         for (Dependency dependency : getDependencies()) {
152             // TODO optional optimization: do not invalidate for conditional flags
153             bitSet.or(dependency.getOther().getInvalidFlags());
154         }
155         return bitSet;
156     }
157 
setBindingExpression(boolean isBindingExpression)158     public void setBindingExpression(boolean isBindingExpression) {
159         mIsBindingExpression = isBindingExpression;
160     }
161 
isBindingExpression()162     public boolean isBindingExpression() {
163         return mIsBindingExpression;
164     }
165 
canBeEvaluatedToAVariable()166     public boolean canBeEvaluatedToAVariable() {
167         return true; // anything except arg/return expr can be evaluated to a variable
168     }
169 
isObservable()170     public boolean isObservable() {
171         return getResolvedType().isObservable();
172     }
173 
resolveListeners(ModelClass valueType, Expr parent)174     public Expr resolveListeners(ModelClass valueType, Expr parent) {
175         for (int i = mChildren.size() - 1; i >= 0; i--) {
176             Expr child = mChildren.get(i);
177             child.resolveListeners(valueType, this);
178         }
179         resetResolvedType();
180         return this;
181     }
182 
resolveTwoWayExpressions(Expr parent)183     public Expr resolveTwoWayExpressions(Expr parent) {
184         for (int i = mChildren.size() - 1; i >= 0; i--) {
185             final Expr child = mChildren.get(i);
186             child.resolveTwoWayExpressions(this);
187         }
188         return this;
189     }
190 
resetResolvedType()191     protected void resetResolvedType() {
192         mResolvedType = null;
193     }
194 
getShouldReadFlags()195     public BitSet getShouldReadFlags() {
196         if (mShouldReadFlags == null) {
197             getShouldReadFlagsWithConditionals();
198             mShouldReadFlags = resolveShouldReadFlags();
199         }
200         return mShouldReadFlags;
201     }
202 
getShouldReadFlagsWithConditionals()203     public BitSet getShouldReadFlagsWithConditionals() {
204         if (mShouldReadWithConditionals == null) {
205             mShouldReadWithConditionals = resolveShouldReadWithConditionals();
206         }
207         return mShouldReadWithConditionals;
208     }
209 
setModel(ExprModel model)210     public void setModel(ExprModel model) {
211         mModel = model;
212     }
213 
resolveShouldReadWithConditionals()214     private BitSet resolveShouldReadWithConditionals() {
215         // ensure we have invalid flags
216         BitSet bitSet = new BitSet();
217         // if i'm invalid, that DOES NOT mean i should be read :/.
218         if (mIsBindingExpression) {
219             bitSet.or(getInvalidFlags());
220         }
221 
222         for (Dependency dependency : getDependants()) {
223             if (dependency.getCondition() == null) {
224                 bitSet.or(dependency.getDependant().getShouldReadFlagsWithConditionals());
225             } else {
226                 bitSet.set(dependency.getDependant()
227                         .getRequirementFlagIndex(dependency.getExpectedOutput()));
228             }
229         }
230         return bitSet;
231     }
232 
resolveShouldReadFlags()233     private BitSet resolveShouldReadFlags() {
234         // ensure we have invalid flags
235         BitSet bitSet = new BitSet();
236         if (isRead()) {
237             return bitSet;
238         }
239         if (mIsBindingExpression) {
240             bitSet.or(getInvalidFlags());
241         }
242         for (Dependency dependency : getDependants()) {
243             final boolean isUnreadElevated = isUnreadElevated(dependency);
244             if (dependency.isConditional()) {
245                 continue; // will be resolved later when conditional is elevated
246             }
247             if (isUnreadElevated) {
248                 bitSet.set(dependency.getDependant()
249                         .getRequirementFlagIndex(dependency.getExpectedOutput()));
250             } else {
251                 bitSet.or(dependency.getDependant().getShouldReadFlags());
252             }
253         }
254         bitSet.and(mShouldReadWithConditionals);
255         bitSet.andNot(mReadSoFar);
256         return bitSet;
257     }
258 
isUnreadElevated(Dependency input)259     private static boolean isUnreadElevated(Dependency input) {
260         return input.isElevated() && !input.getDependant().isRead();
261     }
addParents()262     private void addParents() {
263         for (Expr expr : mChildren) {
264             expr.mParents.add(this);
265         }
266     }
267 
onSwappedWith(Expr existing)268     public void onSwappedWith(Expr existing) {
269         for (Expr child : mChildren) {
270             child.onParentSwapped(this, existing);
271         }
272     }
273 
onParentSwapped(Expr oldParent, Expr newParent)274     private void onParentSwapped(Expr oldParent, Expr newParent) {
275         Preconditions.check(mParents.remove(oldParent), "trying to remove non-existent parent %s"
276                 + " from %s", oldParent, mParents);
277         mParents.add(newParent);
278     }
279 
getChildren()280     public List<Expr> getChildren() {
281         return mChildren;
282     }
283 
getParents()284     public List<Expr> getParents() {
285         return mParents;
286     }
287 
288     /**
289      * Whether the result of this expression can change or not.
290      *
291      * For example, 3 + 5 can not change vs 3 + x may change.
292      *
293      * Default implementations checks children and returns true if any of them returns true
294      *
295      * @return True if the result of this expression may change due to variables
296      */
isDynamic()297     public boolean isDynamic() {
298         if (mIsDynamic == null) {
299             mIsDynamic = isAnyChildDynamic();
300         }
301         return mIsDynamic;
302     }
303 
isAnyChildDynamic()304     private boolean isAnyChildDynamic() {
305         for (Expr expr : mChildren) {
306             if (expr.isDynamic()) {
307                 return true;
308             }
309         }
310         return false;
311     }
312 
getResolvedType()313     public ModelClass getResolvedType() {
314         if (mResolvedType == null) {
315             // TODO not get instance
316             try {
317                 Scope.enter(this);
318                 mResolvedType = resolveType(ModelAnalyzer.getInstance());
319                 if (mResolvedType == null) {
320                     L.e(ErrorMessages.CANNOT_RESOLVE_TYPE, this);
321                 }
322             } finally {
323                 Scope.exit();
324             }
325         }
326         return mResolvedType;
327     }
328 
toExecutionPath(ExecutionPath path)329     public final List<ExecutionPath> toExecutionPath(ExecutionPath path) {
330         List<ExecutionPath> paths = new ArrayList<ExecutionPath>();
331         paths.add(path);
332         return toExecutionPath(paths);
333     }
334 
toExecutionPath(List<ExecutionPath> paths)335     public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) {
336         if (getChildren().isEmpty()) {
337             return addJustMeToExecutionPath(paths);
338         } else {
339             return toExecutionPathInOrder(paths, getChildren());
340         }
341 
342     }
343 
344     @NotNull
addJustMeToExecutionPath(List<ExecutionPath> paths)345     protected final List<ExecutionPath> addJustMeToExecutionPath(List<ExecutionPath> paths) {
346         List<ExecutionPath> result = new ArrayList<ExecutionPath>();
347         for (ExecutionPath path : paths) {
348             result.add(path.addPath(this));
349         }
350         return result;
351     }
352 
353     @SuppressWarnings("Duplicates")
toExecutionPathInOrder(List<ExecutionPath> paths, Expr... order)354     protected final List<ExecutionPath> toExecutionPathInOrder(List<ExecutionPath> paths,
355             Expr... order) {
356         List<ExecutionPath> executionPaths = paths;
357         for (Expr anOrder : order) {
358             executionPaths = anOrder.toExecutionPath(executionPaths);
359         }
360         List<ExecutionPath> result = new ArrayList<ExecutionPath>(paths.size());
361         for (ExecutionPath path : executionPaths) {
362             result.add(path.addPath(this));
363         }
364         return result;
365     }
366 
367     @SuppressWarnings("Duplicates")
toExecutionPathInOrder(List<ExecutionPath> paths, List<Expr> order)368     protected final List<ExecutionPath> toExecutionPathInOrder(List<ExecutionPath> paths,
369             List<Expr> order) {
370         List<ExecutionPath> executionPaths = paths;
371         for (Expr expr : order) {
372             executionPaths = expr.toExecutionPath(executionPaths);
373         }
374         List<ExecutionPath> result = new ArrayList<ExecutionPath>(paths.size());
375         for (ExecutionPath path : executionPaths) {
376             result.add(path.addPath(this));
377         }
378         return result;
379     }
380 
resolveType(ModelAnalyzer modelAnalyzer)381     abstract protected ModelClass resolveType(ModelAnalyzer modelAnalyzer);
382 
constructDependencies()383     abstract protected List<Dependency> constructDependencies();
384 
385     /**
386      * Creates a dependency for each dynamic child. Should work for any expression besides
387      * conditionals.
388      */
constructDynamicChildrenDependencies()389     protected List<Dependency> constructDynamicChildrenDependencies() {
390         List<Dependency> dependencies = new ArrayList<Dependency>();
391         for (Expr node : mChildren) {
392             if (!node.isDynamic()) {
393                 continue;
394             }
395             dependencies.add(new Dependency(this, node));
396         }
397         return dependencies;
398     }
399 
getDependencies()400     public final List<Dependency> getDependencies() {
401         if (mDependencies == null) {
402             mDependencies = constructDependencies();
403         }
404         return mDependencies;
405     }
406 
addDependant(Dependency dependency)407     void addDependant(Dependency dependency) {
408         mDependants.add(dependency);
409     }
410 
getDependants()411     public List<Dependency> getDependants() {
412         return mDependants;
413     }
414 
415     protected static final String KEY_JOIN = "~";
416 
417     /**
418      * Returns a unique string key that can identify this expression.
419      *
420      * It must take into account any dependencies
421      *
422      * @return A unique identifier for this expression
423      */
getUniqueKey()424     public final String getUniqueKey() {
425         if (mUniqueKey == null) {
426             mUniqueKey = computeUniqueKey();
427             Preconditions.checkNotNull(mUniqueKey,
428                     "if there are no children, you must override computeUniqueKey");
429             Preconditions.check(!mUniqueKey.trim().equals(""),
430                     "if there are no children, you must override computeUniqueKey");
431         }
432         return mUniqueKey;
433     }
434 
computeUniqueKey()435     protected String computeUniqueKey() {
436         return computeChildrenKey();
437     }
438 
computeChildrenKey()439     protected final String computeChildrenKey() {
440         return join(mChildren);
441     }
442 
enableDirectInvalidation()443     public void enableDirectInvalidation() {
444         mCanBeInvalidated = true;
445     }
446 
canBeInvalidated()447     public boolean canBeInvalidated() {
448         return mCanBeInvalidated;
449     }
450 
trimShouldReadFlags(BitSet bitSet)451     public void trimShouldReadFlags(BitSet bitSet) {
452         mShouldReadFlags.andNot(bitSet);
453     }
454 
isConditional()455     public boolean isConditional() {
456         return false;
457     }
458 
getRequirementId()459     public int getRequirementId() {
460         return mRequirementId;
461     }
462 
setRequirementId(int requirementId)463     public void setRequirementId(int requirementId) {
464         mRequirementId = requirementId;
465     }
466 
467     /**
468      * This is called w/ a dependency of mine.
469      * Base method should thr
470      */
getRequirementFlagIndex(boolean expectedOutput)471     public int getRequirementFlagIndex(boolean expectedOutput) {
472         Preconditions.check(mRequirementId != NO_ID, "If this is an expression w/ conditional"
473                 + " dependencies, it must be assigned a requirement ID. %s", this);
474         return expectedOutput ? mRequirementId + 1 : mRequirementId;
475     }
476 
hasId()477     public boolean hasId() {
478         return mId != NO_ID;
479     }
480 
markFlagsAsRead(BitSet flags)481     public void markFlagsAsRead(BitSet flags) {
482         mReadSoFar.or(flags);
483     }
484 
isRead()485     public boolean isRead() {
486         return mRead;
487     }
488 
considerElevatingConditionals(Expr justRead)489     public boolean considerElevatingConditionals(Expr justRead) {
490         boolean elevated = false;
491         for (Dependency dependency : mDependencies) {
492             if (dependency.isConditional() && dependency.getCondition() == justRead) {
493                 dependency.elevate();
494                 elevated = true;
495             }
496         }
497         return elevated;
498     }
499 
invalidateReadFlags()500     public void invalidateReadFlags() {
501         mShouldReadFlags = null;
502         mVersion ++;
503     }
504 
505     @Override
getVersion()506     public int getVersion() {
507         return mVersion;
508     }
509 
hasNestedCannotRead()510     public boolean hasNestedCannotRead() {
511         if (isRead()) {
512             return false;
513         }
514         if (getShouldReadFlags().isEmpty()) {
515             return true;
516         }
517         for (Dependency dependency : getDependencies()) {
518             if (hasNestedCannotRead(dependency)) {
519                 return true;
520             }
521         }
522         return false;
523     }
524 
hasNestedCannotRead(Dependency input)525     private static boolean hasNestedCannotRead(Dependency input) {
526         return input.isConditional() || input.getOther().hasNestedCannotRead();
527     }
528 
markAsReadIfDone()529     public boolean markAsReadIfDone() {
530         if (mRead) {
531             return false;
532         }
533         // TODO avoid clone, we can calculate this iteratively
534         BitSet clone = (BitSet) mShouldReadWithConditionals.clone();
535 
536         clone.andNot(mReadSoFar);
537         mRead = clone.isEmpty();
538 
539         if (!mRead && !mReadSoFar.isEmpty()) {
540             // check if remaining dependencies can be satisfied w/ existing values
541             // for predicate flags, this expr may already be calculated to get the predicate
542             // to detect them, traverse them later on, see which flags should be calculated to calculate
543             // them. If any of them is completely covered w/ our non-conditional flags, no reason
544             // to add them to the list since we'll already be calculated due to our non-conditional
545             // flags
546             boolean allCovered = true;
547             for (int i = clone.nextSetBit(0); i != -1; i = clone.nextSetBit(i + 1)) {
548                 final Expr expr = mModel.findFlagExpression(i);
549                 if (expr == null) {
550                     continue;
551                 }
552                 if (!expr.isConditional()) {
553                     allCovered = false;
554                     break;
555                 }
556                 final BitSet readForConditional = (BitSet) expr
557                         .getShouldReadFlagsWithConditionals().clone();
558 
559                 // FIXME: this does not do full traversal so misses some cases
560                 // to calculate that conditional, i should've read /readForConditional/ flags
561                 // if my read-so-far bits cover that; that means i would've already
562                 // read myself
563                 readForConditional.andNot(mReadSoFar);
564                 if (!readForConditional.isEmpty()) {
565                     allCovered = false;
566                     break;
567                 }
568             }
569             mRead = allCovered;
570         }
571         if (mRead) {
572             mShouldReadFlags = null; // if we've been marked as read, clear should read flags
573         }
574         return mRead;
575     }
576 
577     BitSet mConditionalFlags;
578 
findConditionalFlags()579     private BitSet findConditionalFlags() {
580         Preconditions.check(isConditional(), "should not call this on a non-conditional expr");
581         if (mConditionalFlags == null) {
582             mConditionalFlags = new BitSet();
583             resolveConditionalFlags(mConditionalFlags);
584         }
585         return mConditionalFlags;
586     }
587 
resolveConditionalFlags(BitSet flags)588     private void resolveConditionalFlags(BitSet flags) {
589         flags.or(getPredicateInvalidFlags());
590         // if i have only 1 dependency which is conditional, traverse it as well
591         if (getDependants().size() == 1) {
592             final Dependency dependency = getDependants().get(0);
593             if (dependency.getCondition() != null) {
594                 flags.or(dependency.getDependant().findConditionalFlags());
595                 flags.set(dependency.getDependant()
596                         .getRequirementFlagIndex(dependency.getExpectedOutput()));
597             }
598         }
599     }
600 
601 
602     @Override
toString()603     public String toString() {
604         return getUniqueKey();
605     }
606 
getReadSoFar()607     public BitSet getReadSoFar() {
608         return mReadSoFar;
609     }
610 
611     private Node mCalculationPaths = null;
612 
613     /**
614      * All flag paths that will result in calculation of this expression.
615      */
getAllCalculationPaths()616     protected Node getAllCalculationPaths() {
617         if (mCalculationPaths == null) {
618             Node node = new Node();
619             if (isConditional()) {
620                 node.mBitSet.or(getPredicateInvalidFlags());
621             } else {
622                 node.mBitSet.or(getInvalidFlags());
623             }
624             for (Dependency dependency : getDependants()) {
625                 final Expr dependant = dependency.getDependant();
626                 if (dependency.getCondition() != null) {
627                     Node cond = new Node();
628                     cond.setConditionFlag(
629                             dependant.getRequirementFlagIndex(dependency.getExpectedOutput()));
630                     cond.mParents.add(dependant.getAllCalculationPaths());
631                     node.mParents.add(cond);
632                 } else {
633                     node.mParents.add(dependant.getAllCalculationPaths());
634                 }
635             }
636             mCalculationPaths = node;
637         }
638         return mCalculationPaths;
639     }
640 
getDefaultValue()641     public String getDefaultValue() {
642         return ModelAnalyzer.getInstance().getDefaultValue(getResolvedType().toJavaCode());
643     }
644 
getPredicateInvalidFlags()645     protected BitSet getPredicateInvalidFlags() {
646         throw new IllegalStateException(
647                 "must override getPredicateInvalidFlags in " + getClass().getSimpleName());
648     }
649 
650     /**
651      * Used by code generation
652      */
shouldReadNow(final List<Expr> justRead)653     public boolean shouldReadNow(final List<Expr> justRead) {
654         if (getShouldReadFlags().isEmpty()) {
655             return false;
656         }
657         for (Dependency input : getDependencies()) {
658             boolean dependencyReady = input.getOther().isRead() || (justRead != null &&
659                     justRead.contains(input.getOther()));
660             if(!dependencyReady) {
661                 return false;
662             }
663         }
664         return true;
665     }
666 
isEqualityCheck()667     public boolean isEqualityCheck() {
668         return false;
669     }
670 
markAsUsed()671     public void markAsUsed() {
672         mIsUsed = true;
673         for (Expr child : getChildren()) {
674             child.markAsUsed();
675         }
676     }
677 
markAsUsedInCallback()678     public void markAsUsedInCallback() {
679         mIsUsedInCallback = true;
680         for (Expr child : getChildren()) {
681             child.markAsUsedInCallback();
682         }
683     }
684 
isIsUsedInCallback()685     public boolean isIsUsedInCallback() {
686         return mIsUsedInCallback;
687     }
688 
isUsed()689     public boolean isUsed() {
690         return mIsUsed;
691     }
692 
updateExpr(ModelAnalyzer modelAnalyzer)693     public void updateExpr(ModelAnalyzer modelAnalyzer) {
694         final Map<String, Expr> exprMap = mModel.getExprMap();
695         for (int i = mParents.size() - 1; i >= 0; i--) {
696             final Expr parent = mParents.get(i);
697             if (exprMap.get(parent.getUniqueKey()) != parent) {
698                 mParents.remove(i);
699             }
700         }
701         for (Expr child : mChildren) {
702             child.updateExpr(modelAnalyzer);
703         }
704     }
705 
join(String... items)706     protected static String join(String... items) {
707         StringBuilder result = new StringBuilder();
708         for (int i = 0; i < items.length; i ++) {
709             if (i > 0) {
710                 result.append(KEY_JOIN);
711             }
712             result.append(items[i]);
713         }
714         return result.toString();
715     }
716 
join(List<Expr> items)717     protected static String join(List<Expr> items) {
718         StringBuilder result = new StringBuilder();
719         for (int i = 0; i < items.size(); i ++) {
720             if (i > 0) {
721                 result.append(KEY_JOIN);
722             }
723             result.append(items.get(i).getUniqueKey());
724         }
725         return result.toString();
726     }
727 
asPackage()728     protected String asPackage() {
729         return null;
730     }
731 
732     @Override
provideScopeLocation()733     public List<Location> provideScopeLocation() {
734         return mLocations;
735     }
736 
toCode()737     public KCode toCode() {
738         if (isDynamic()) {
739             return new KCode(LayoutBinderWriterKt.scopedName(this));
740         }
741         return generateCode();
742     }
743 
toFullCode()744     public KCode toFullCode() {
745         return generateCode();
746     }
747 
generateCode()748     protected abstract KCode generateCode();
749 
generateInverse(ExprModel model, Expr value, String bindingClassName)750     public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
751         throw new IllegalStateException("expression does not support two-way binding");
752     }
753 
cloneToModel(ExprModel model)754     public abstract Expr cloneToModel(ExprModel model);
755 
cloneToModel(ExprModel model, List<Expr> exprs)756     protected static List<Expr> cloneToModel(ExprModel model, List<Expr> exprs) {
757         ArrayList<Expr> clones = new ArrayList<Expr>();
758         for (Expr expr : exprs) {
759             clones.add(expr.cloneToModel(model));
760         }
761         return clones;
762     }
763 
assertIsInvertible()764     public void assertIsInvertible() {
765         final String errorMessage = getInvertibleError();
766         if (errorMessage != null) {
767             L.e(ErrorMessages.EXPRESSION_NOT_INVERTIBLE, toFullCode().generate(),
768                     errorMessage);
769         }
770     }
771 
772     /**
773      * @return The reason the expression wasn't invertible or null if it was invertible.
774      */
getInvertibleError()775     protected abstract String getInvertibleError();
776 
777     /**
778      * This expression is the predicate for 1 or more ternary expressions.
779      */
hasConditionalDependant()780     public boolean hasConditionalDependant() {
781         for (Dependency dependency : getDependants()) {
782             Expr dependant = dependency.getDependant();
783             if (dependant.isConditional() && dependant instanceof TernaryExpr) {
784                 TernaryExpr ternary = (TernaryExpr) dependant;
785                 return ternary.getPred() == this;
786             }
787         }
788         return false;
789     }
790 
791     static class Node {
792 
793         BitSet mBitSet = new BitSet();
794         List<Node> mParents = new ArrayList<Node>();
795         int mConditionFlag = -1;
796 
areAllPathsSatisfied(BitSet readSoFar)797         public boolean areAllPathsSatisfied(BitSet readSoFar) {
798             if (mConditionFlag != -1) {
799                 return readSoFar.get(mConditionFlag)
800                         || mParents.get(0).areAllPathsSatisfied(readSoFar);
801             } else {
802                 final BitSet myBitsClone = (BitSet) mBitSet.clone();
803                 myBitsClone.andNot(readSoFar);
804                 if (!myBitsClone.isEmpty()) {
805                     // read so far does not cover all of my invalidation. The only way I could be
806                     // covered is that I only have 1 conditional dependent which is covered by this.
807                     if (mParents.size() == 1 && mParents.get(0).mConditionFlag != -1) {
808                         return mParents.get(0).areAllPathsSatisfied(readSoFar);
809                     }
810                     return false;
811                 }
812                 if (mParents.isEmpty()) {
813                     return true;
814                 }
815                 for (Node parent : mParents) {
816                     if (!parent.areAllPathsSatisfied(readSoFar)) {
817                         return false;
818                     }
819                 }
820                 return true;
821             }
822         }
823 
setConditionFlag(int requirementFlagIndex)824         public void setConditionFlag(int requirementFlagIndex) {
825             mConditionFlag = requirementFlagIndex;
826         }
827     }
828 }
829