ExprModel.java revision 019c36b97c7c172ac03997f6bf170e65b2ed7fe4
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.Preconditions; 20import com.google.common.base.Predicate; 21import com.google.common.collect.Iterables; 22import com.google.common.collect.Lists; 23 24import android.databinding.tool.reflection.ModelAnalyzer; 25import android.databinding.tool.util.L; 26import android.databinding.tool.writer.FlagSet; 27 28import java.util.ArrayList; 29import java.util.Arrays; 30import java.util.BitSet; 31import java.util.HashMap; 32import java.util.List; 33import java.util.Map; 34 35public class ExprModel { 36 37 Map<String, Expr> mExprMap = new HashMap<String, Expr>(); 38 39 List<Expr> mBindingExpressions = new ArrayList<Expr>(); 40 41 private int mInvalidateableFieldLimit = 0; 42 43 private int mRequirementIdCount = 0; 44 45 private static final String TRUE_KEY_SUFFIX = "== true"; 46 private static final String FALSE_KEY_SUFFIX = "== false"; 47 48 /** 49 * Any expression can be invalidated by invalidating this flag. 50 */ 51 private BitSet mInvalidateAnyFlags; 52 53 /** 54 * Used by code generation. Keeps the list of expressions that are waiting to be evaluated. 55 */ 56 private List<Expr> mPendingExpressions; 57 58 /** 59 * Used for converting flags into identifiers while debugging. 60 */ 61 private String[] mFlagMapping; 62 63 private BitSet mInvalidateableFlags; 64 private BitSet mConditionalFlags; 65 66 private int mFlagBucketCount;// how many buckets we use to identify flags 67 68 private List<Expr> mObservables; 69 70 private Map<String, String> mImports = new HashMap<String, String>(); 71 72 /** 73 * Adds the expression to the list of expressions and returns it. 74 * If it already exists, returns existing one. 75 * 76 * @param expr The new parsed expression 77 * @return The expression itself or another one if the same thing was parsed before 78 */ 79 public <T extends Expr> T register(T expr) { 80 T existing = (T) mExprMap.get(expr.getUniqueKey()); 81 if (existing != null) { 82 Preconditions.checkState(expr.getParents().isEmpty(), 83 "If an expression already exists, it should've never been added to a parent," 84 + "if thats the case, somewhere we are creating an expression w/o" 85 + "calling expression model"); 86 // tell the expr that it is being swapped so that if it was added to some other expr 87 // as a parent, those can swap their references 88 expr.onSwappedWith(existing); 89 return existing; 90 } 91 mExprMap.put(expr.getUniqueKey(), expr); 92 expr.setModel(this); 93 return expr; 94 } 95 96 public void unregister(Expr expr) { 97 mExprMap.remove(expr.getUniqueKey()); 98 } 99 100 public Map<String, Expr> getExprMap() { 101 return mExprMap; 102 } 103 104 public int size() { 105 return mExprMap.size(); 106 } 107 108 public ComparisonExpr comparison(String op, Expr left, Expr right) { 109 return register(new ComparisonExpr(op, left, right)); 110 } 111 112 public FieldAccessExpr field(Expr parent, String name) { 113 return register(new FieldAccessExpr(parent, name)); 114 } 115 116 public FieldAccessExpr observableField(Expr parent, String name) { 117 return register(new FieldAccessExpr(parent, name, true)); 118 } 119 120 public SymbolExpr symbol(String text, Class type) { 121 return register(new SymbolExpr(text, type)); 122 } 123 124 public TernaryExpr ternary(Expr pred, Expr ifTrue, Expr ifFalse) { 125 return register(new TernaryExpr(pred, ifTrue, ifFalse)); 126 } 127 128 public IdentifierExpr identifier(String name) { 129 return register(new IdentifierExpr(name)); 130 } 131 132 public StaticIdentifierExpr staticIdentifier(String name) { 133 return register(new StaticIdentifierExpr(name)); 134 } 135 136 public MethodCallExpr methodCall(Expr target, String name, List<Expr> args) { 137 return register(new MethodCallExpr(target, name, args)); 138 } 139 140 public MathExpr math(Expr left, String op, Expr right) { 141 return register(new MathExpr(left, op, right)); 142 } 143 144 public Expr group(Expr grouped) { 145 return register(new GroupExpr(grouped)); 146 } 147 148 public Expr resourceExpr(String packageName, String resourceType, String resourceName, 149 List<Expr> args) { 150 return register(new ResourceExpr(packageName, resourceType, resourceName, args)); 151 } 152 153 public Expr bracketExpr(Expr variableExpr, Expr argExpr) { 154 return register(new BracketExpr(variableExpr, argExpr)); 155 } 156 157 public Expr castExpr(String type, Expr expr) { 158 return register(new CastExpr(type, expr)); 159 } 160 161 public List<Expr> getBindingExpressions() { 162 return mBindingExpressions; 163 } 164 165 public void addImport(String alias, String type) { 166 Preconditions.checkState(!mImports.containsKey(alias), 167 "%s has already been defined as %s", alias, type); 168 final StaticIdentifierExpr id = staticIdentifier(alias); 169 L.d("adding import %s as %s klass: %s", type, alias, id.getClass().getSimpleName()); 170 id.setUserDefinedType(type); 171 mImports.put(alias, type); 172 } 173 174 public Map<String, String> getImports() { 175 return mImports; 176 } 177 178 /** 179 * The actual thingy that is set on the binding target. 180 * 181 * Input must be already registered 182 */ 183 public Expr bindingExpr(Expr bindingExpr) { 184 Preconditions.checkArgument(mExprMap.containsKey(bindingExpr.getUniqueKey()), 185 "Main expression should already be registered"); 186 if (!mBindingExpressions.contains(bindingExpr)) { 187 mBindingExpressions.add(bindingExpr); 188 } 189 return bindingExpr; 190 } 191 192 /** 193 * Nodes to which no one depends 194 */ 195 public Iterable<Expr> findRootNodes() { 196 return Iterables.filter(mExprMap.values(), new Predicate<Expr>() { 197 @Override 198 public boolean apply(Expr input) { 199 return input.getParents().isEmpty(); 200 } 201 }); 202 } 203 204 /** 205 * Nodes, which do not depend on any other node 206 */ 207 public Iterable<Expr> findLeafNodes() { 208 return Iterables.filter(mExprMap.values(), new Predicate<Expr>() { 209 @Override 210 public boolean apply(Expr input) { 211 return input.getChildren().isEmpty(); 212 } 213 }); 214 } 215 216 public List<Expr> getObservables() { 217 return mObservables; 218 } 219 220 /** 221 * Give id to each expression. Will be useful if we serialize. 222 */ 223 public void seal() { 224 List<Expr> notifiableExpressions = new ArrayList<Expr>(); 225 //ensure class analyzer. We need to know observables at this point 226 final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 227 228 ArrayList<Expr> exprs = new ArrayList<Expr>(mBindingExpressions); 229 for (Expr expr: exprs) { 230 expr.updateExpr(modelAnalyzer); 231 } 232 233 int counter = 0; 234 final Iterable<Expr> observables = filterObservables(modelAnalyzer); 235 List<String> flagMapping = Lists.newArrayList(); 236 mObservables = Lists.newArrayList(); 237 for (Expr expr : observables) { 238 // observables gets initial ids 239 flagMapping.add(expr.getUniqueKey()); 240 expr.setId(counter++); 241 mObservables.add(expr); 242 notifiableExpressions.add(expr); 243 L.d("observable %s", expr.getUniqueKey()); 244 } 245 246 // non-observable identifiers gets next ids 247 final Iterable<Expr> nonObservableIds = filterNonObservableIds(modelAnalyzer); 248 for (Expr expr : nonObservableIds) { 249 flagMapping.add(expr.getUniqueKey()); 250 expr.setId(counter++); 251 notifiableExpressions.add(expr); 252 L.d("non-observable %s", expr.getUniqueKey()); 253 } 254 255 // descendents of observables gets following ids 256 for (Expr expr : observables) { 257 for (Expr parent : expr.getParents()) { 258 if (parent.hasId()) { 259 continue;// already has some id, means observable 260 } 261 // only fields earn an id 262 if (parent instanceof FieldAccessExpr) { 263 FieldAccessExpr fae = (FieldAccessExpr) parent; 264 L.d("checking field access expr %s. getter: %s", fae,fae.getGetter()); 265 if (fae.isDynamic() && fae.getGetter().canBeInvalidated()) { 266 flagMapping.add(parent.getUniqueKey()); 267 parent.setId(counter++); 268 notifiableExpressions.add(parent); 269 L.d("notifiable field %s : %s for %s : %s", parent.getUniqueKey(), 270 Integer.toHexString(System.identityHashCode(parent)), 271 expr.getUniqueKey(), 272 Integer.toHexString(System.identityHashCode(expr))); 273 } 274 } 275 } 276 } 277 278 // non-dynamic binding expressions receive some ids so that they can be invalidated 279 for (int i = 0; i < mBindingExpressions.size(); i++) { 280 L.d("[" + i + "] " + mBindingExpressions.get(i)); 281 } 282 for (Expr expr : mBindingExpressions) { 283 if (!(expr.isDynamic() || !expr.hasId())) { 284 L.d("Expr " + expr + " is dynamic? " + expr.isDynamic() + ", has ID? " + expr.hasId()); 285 } 286 Preconditions.checkState(expr.isDynamic() || !expr.hasId()); 287 if (!expr.isDynamic()) { 288 // give it an id for invalidateAll 289 expr.setId(counter ++); 290 notifiableExpressions.add(expr); 291 } 292 } 293 294 for (Expr expr : notifiableExpressions) { 295 expr.enableDirectInvalidation(); 296 } 297 298 // make sure all dependencies are resolved to avoid future race conditions 299 for (Expr expr : mExprMap.values()) { 300 expr.getDependencies(); 301 } 302 303 mInvalidateableFieldLimit = counter + 1; 304 mInvalidateableFlags = new BitSet(); 305 for (int i = 0; i < mInvalidateableFieldLimit; i++) { 306 mInvalidateableFlags.set(i, true); 307 } 308 309 // make sure all dependencies are resolved to avoid future race conditions 310 for (Expr expr : mExprMap.values()) { 311 if (expr.isConditional()) { 312 expr.setRequirementId(counter); 313 flagMapping.add(expr.getUniqueKey() + FALSE_KEY_SUFFIX); 314 flagMapping.add(expr.getUniqueKey() + TRUE_KEY_SUFFIX); 315 counter += 2; 316 } 317 } 318 mConditionalFlags = new BitSet(); 319 for (int i = mInvalidateableFieldLimit; i < counter; i++) { 320 mConditionalFlags.set(i, true); 321 } 322 323 mRequirementIdCount = (counter - mInvalidateableFieldLimit) / 2; 324 325 // everybody gets an id 326 for (Map.Entry<String, Expr> entry : mExprMap.entrySet()) { 327 final Expr value = entry.getValue(); 328 if (!value.hasId()) { 329 value.setId(counter++); 330 } 331 } 332 flagMapping.add("ALL"); 333 mFlagMapping = new String[flagMapping.size()]; 334 flagMapping.toArray(mFlagMapping); 335 336 mFlagBucketCount = 1 + (getTotalFlagCount() / FlagSet.sBucketSize); 337 mInvalidateAnyFlags = new BitSet(); 338 mInvalidateAnyFlags.set(mInvalidateableFieldLimit - 1, true); 339 340 for (Expr expr : mExprMap.values()) { 341 expr.getShouldReadFlagsWithConditionals(); 342 } 343 344 for (Expr expr : mExprMap.values()) { 345 // ensure all types are calculated 346 expr.getResolvedType(); 347 } 348 349 350 } 351 352 public int getFlagBucketCount() { 353 return mFlagBucketCount; 354 } 355 356 public int getTotalFlagCount() { 357 return mRequirementIdCount * 2 + mInvalidateableFieldLimit; 358 } 359 360 public int getInvalidateableFieldLimit() { 361 return mInvalidateableFieldLimit; 362 } 363 364 public String[] getFlagMapping() { 365 return mFlagMapping; 366 } 367 368 public String getFlag(int id) { 369 return mFlagMapping[id]; 370 } 371 372 private Iterable<Expr> filterNonObservableIds(final ModelAnalyzer modelAnalyzer) { 373 return Iterables.filter(mExprMap.values(), new Predicate<Expr>() { 374 @Override 375 public boolean apply(Expr input) { 376 return input instanceof IdentifierExpr 377 && !input.hasId() 378 && !input.isObservable() 379 && input.isDynamic(); 380 } 381 }); 382 } 383 384 private Iterable<Expr> filterObservables(final ModelAnalyzer modelAnalyzer) { 385 return Iterables.filter(mExprMap.values(), new Predicate<Expr>() { 386 @Override 387 public boolean apply(Expr input) { 388 return input.isObservable(); 389 } 390 }); 391 } 392 393 public List<Expr> getPendingExpressions() { 394 if (mPendingExpressions == null) { 395 mPendingExpressions = Lists.newArrayList(); 396 for (Expr expr : mExprMap.values()) { 397 if (!expr.isRead() && expr.isDynamic()) { 398 mPendingExpressions.add(expr); 399 } 400 } 401 } 402 return mPendingExpressions; 403 } 404 405 public boolean markBitsRead() { 406 // each has should read flags, we set them back on them 407 for (Expr expr : filterShouldRead(getPendingExpressions())) { 408 expr.markFlagsAsRead(expr.getShouldReadFlags()); 409 } 410 return pruneDone(); 411 } 412 413 private boolean pruneDone() { 414 boolean marked = true; 415 List<Expr> markedAsReadList = Lists.newArrayList(); 416 while (marked) { 417 marked = false; 418 for (Expr expr : mExprMap.values()) { 419 if (expr.isRead()) { 420 continue; 421 } 422 if (expr.markAsReadIfDone()) { 423 L.d("marked %s as read ", expr.getUniqueKey()); 424 marked = true; 425 markedAsReadList.add(expr); 426 } 427 428 } 429 } 430 boolean elevated = false; 431 for (Expr markedAsRead : markedAsReadList) { 432 for (Dependency dependency : markedAsRead.getDependants()) { 433 if (dependency.getDependant().considerElevatingConditionals(markedAsRead)) { 434 elevated = true; 435 } 436 } 437 } 438 if (elevated) { 439 // some conditionals are elevated. We should re-calculate flags 440 for (Expr expr : getPendingExpressions()) { 441 if (!expr.isRead()) { 442 expr.invalidateReadFlags(); 443 } 444 } 445 mPendingExpressions = null; 446 } 447 return elevated; 448 } 449 450 public static Iterable<Expr> filterShouldRead(Iterable<Expr> exprs) { 451 return toCollection(Iterables.filter(exprs, sShouldReadPred)); 452 } 453 454 public static List<Expr> toCollection(Iterable<Expr> iterable) { 455 return Arrays.asList(Iterables.toArray(iterable, Expr.class)); 456 } 457 458 private static final Predicate<Expr> sShouldReadPred = new Predicate<Expr>() { 459 @Override 460 public boolean apply(final Expr expr) { 461 return !expr.getShouldReadFlags().isEmpty() && !Iterables.any( 462 expr.getDependencies(), new Predicate<Dependency>() { 463 @Override 464 public boolean apply(Dependency dependency) { 465 final boolean result = dependency.isConditional() || 466 dependency.getOther().hasNestedCannotRead(); 467 return result; 468 } 469 }); 470 } 471 }; 472 473 public Expr findFlagExpression(int flag) { 474 final String key = mFlagMapping[flag]; 475 if (mExprMap.containsKey(key)) { 476 return mExprMap.get(key); 477 } 478 int falseIndex = key.indexOf(FALSE_KEY_SUFFIX); 479 if (falseIndex > -1) { 480 final String trimmed = key.substring(0, falseIndex); 481 return mExprMap.get(trimmed); 482 } 483 int trueIndex = key.indexOf(TRUE_KEY_SUFFIX); 484 if (trueIndex > -1) { 485 final String trimmed = key.substring(0, trueIndex); 486 return mExprMap.get(trimmed); 487 } 488 Preconditions.checkArgument(false, "cannot find expression for flag %d", flag); 489 return null; 490 } 491 492 public BitSet getInvalidateAnyBitSet() { 493 return mInvalidateAnyFlags; 494 } 495} 496