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