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