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 android.databinding.tool.processing.ErrorMessages;
20import android.databinding.tool.processing.Scope;
21import android.databinding.tool.processing.scopes.LocationScopeProvider;
22import android.databinding.tool.reflection.ModelAnalyzer;
23import android.databinding.tool.reflection.ModelClass;
24import android.databinding.tool.solver.ExecutionPath;
25import android.databinding.tool.store.Location;
26import android.databinding.tool.util.L;
27import android.databinding.tool.util.Preconditions;
28import android.databinding.tool.writer.KCode;
29import android.databinding.tool.writer.LayoutBinderWriterKt;
30
31import org.jetbrains.annotations.NotNull;
32import org.jetbrains.annotations.Nullable;
33
34import java.util.ArrayList;
35import java.util.BitSet;
36import java.util.Collections;
37import java.util.List;
38import java.util.Map;
39
40abstract 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
104    Expr(Iterable<Expr> children) {
105        for (Expr expr : children) {
106            mChildren.add(expr);
107        }
108        addParents();
109    }
110
111    Expr(Expr... children) {
112        Collections.addAll(mChildren, children);
113        addParents();
114    }
115
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
122    public void setId(int id) {
123        Preconditions.check(mId == NO_ID, "ID is already set on %s", this);
124        mId = id;
125    }
126
127    public void addLocation(Location location) {
128        mLocations.add(location);
129    }
130
131    public List<Location> getLocations() {
132        return mLocations;
133    }
134
135    public ExprModel getModel() {
136        return mModel;
137    }
138
139    public BitSet getInvalidFlags() {
140        if (mInvalidFlags == null) {
141            mInvalidFlags = resolveInvalidFlags();
142        }
143        return mInvalidFlags;
144    }
145
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
158    public void setBindingExpression(boolean isBindingExpression) {
159        mIsBindingExpression = isBindingExpression;
160    }
161
162    public boolean isBindingExpression() {
163        return mIsBindingExpression;
164    }
165
166    public boolean canBeEvaluatedToAVariable() {
167        return true; // anything except arg/return expr can be evaluated to a variable
168    }
169
170    public boolean isObservable() {
171        return getResolvedType().isObservable();
172    }
173
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
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
191    protected void resetResolvedType() {
192        mResolvedType = null;
193    }
194
195    public BitSet getShouldReadFlags() {
196        if (mShouldReadFlags == null) {
197            getShouldReadFlagsWithConditionals();
198            mShouldReadFlags = resolveShouldReadFlags();
199        }
200        return mShouldReadFlags;
201    }
202
203    public BitSet getShouldReadFlagsWithConditionals() {
204        if (mShouldReadWithConditionals == null) {
205            mShouldReadWithConditionals = resolveShouldReadWithConditionals();
206        }
207        return mShouldReadWithConditionals;
208    }
209
210    public void setModel(ExprModel model) {
211        mModel = model;
212    }
213
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
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
259    private static boolean isUnreadElevated(Dependency input) {
260        return input.isElevated() && !input.getDependant().isRead();
261    }
262    private void addParents() {
263        for (Expr expr : mChildren) {
264            expr.mParents.add(this);
265        }
266    }
267
268    public void onSwappedWith(Expr existing) {
269        for (Expr child : mChildren) {
270            child.onParentSwapped(this, existing);
271        }
272    }
273
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
280    public List<Expr> getChildren() {
281        return mChildren;
282    }
283
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     */
297    public boolean isDynamic() {
298        if (mIsDynamic == null) {
299            mIsDynamic = isAnyChildDynamic();
300        }
301        return mIsDynamic;
302    }
303
304    private boolean isAnyChildDynamic() {
305        for (Expr expr : mChildren) {
306            if (expr.isDynamic()) {
307                return true;
308            }
309        }
310        return false;
311    }
312
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
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
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
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")
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")
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
381    abstract protected ModelClass resolveType(ModelAnalyzer modelAnalyzer);
382
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     */
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
400    public final List<Dependency> getDependencies() {
401        if (mDependencies == null) {
402            mDependencies = constructDependencies();
403        }
404        return mDependencies;
405    }
406
407    void addDependant(Dependency dependency) {
408        mDependants.add(dependency);
409    }
410
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     */
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
435    protected String computeUniqueKey() {
436        return computeChildrenKey();
437    }
438
439    protected final String computeChildrenKey() {
440        return join(mChildren);
441    }
442
443    public void enableDirectInvalidation() {
444        mCanBeInvalidated = true;
445    }
446
447    public boolean canBeInvalidated() {
448        return mCanBeInvalidated;
449    }
450
451    public void trimShouldReadFlags(BitSet bitSet) {
452        mShouldReadFlags.andNot(bitSet);
453    }
454
455    public boolean isConditional() {
456        return false;
457    }
458
459    public int getRequirementId() {
460        return mRequirementId;
461    }
462
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     */
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
477    public boolean hasId() {
478        return mId != NO_ID;
479    }
480
481    public void markFlagsAsRead(BitSet flags) {
482        mReadSoFar.or(flags);
483    }
484
485    public boolean isRead() {
486        return mRead;
487    }
488
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
500    public void invalidateReadFlags() {
501        mShouldReadFlags = null;
502        mVersion ++;
503    }
504
505    @Override
506    public int getVersion() {
507        return mVersion;
508    }
509
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
525    private static boolean hasNestedCannotRead(Dependency input) {
526        return input.isConditional() || input.getOther().hasNestedCannotRead();
527    }
528
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
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
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
603    public String toString() {
604        return getUniqueKey();
605    }
606
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     */
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
641    public String getDefaultValue() {
642        return ModelAnalyzer.getInstance().getDefaultValue(getResolvedType().toJavaCode());
643    }
644
645    protected BitSet getPredicateInvalidFlags() {
646        throw new IllegalStateException(
647                "must override getPredicateInvalidFlags in " + getClass().getSimpleName());
648    }
649
650    /**
651     * Used by code generation
652     */
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
667    public boolean isEqualityCheck() {
668        return false;
669    }
670
671    public void markAsUsed() {
672        mIsUsed = true;
673        for (Expr child : getChildren()) {
674            child.markAsUsed();
675        }
676    }
677
678    public void markAsUsedInCallback() {
679        mIsUsedInCallback = true;
680        for (Expr child : getChildren()) {
681            child.markAsUsedInCallback();
682        }
683    }
684
685    public boolean isIsUsedInCallback() {
686        return mIsUsedInCallback;
687    }
688
689    public boolean isUsed() {
690        return mIsUsed;
691    }
692
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
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
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
728    protected String asPackage() {
729        return null;
730    }
731
732    @Override
733    public List<Location> provideScopeLocation() {
734        return mLocations;
735    }
736
737    public KCode toCode() {
738        if (isDynamic()) {
739            return new KCode(LayoutBinderWriterKt.scopedName(this));
740        }
741        return generateCode();
742    }
743
744    public KCode toFullCode() {
745        return generateCode();
746    }
747
748    protected abstract KCode generateCode();
749
750    public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
751        throw new IllegalStateException("expression does not support two-way binding");
752    }
753
754    public abstract Expr cloneToModel(ExprModel model);
755
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
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     */
775    protected abstract String getInvertibleError();
776
777    /**
778     * This expression is the predicate for 1 or more ternary expressions.
779     */
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
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
824        public void setConditionFlag(int requirementFlagIndex) {
825            mConditionFlag = requirementFlagIndex;
826        }
827    }
828}
829