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