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