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