LayoutBinderWriter.kt revision 34a18e6a231f3b64726bd93e7e097a0d5a75995d
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * Unless required by applicable law or agreed to in writing, software 8 * distributed under the License is distributed on an "AS IS" BASIS, 9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 * See the License for the specific language governing permissions and 11 * limitations under the License. 12 */ 13 14package android.databinding.tool.writer 15 16import android.databinding.tool.LayoutBinder 17import android.databinding.tool.expr.Expr 18import kotlin.properties.Delegates 19import android.databinding.tool.ext.joinToCamelCaseAsVar 20import android.databinding.tool.BindingTarget 21import android.databinding.tool.expr.IdentifierExpr 22import android.databinding.tool.util.Log 23import java.util.BitSet 24import android.databinding.tool.expr.ExprModel 25import java.util.Arrays 26import android.databinding.tool.expr.TernaryExpr 27import android.databinding.tool.expr.FieldAccessExpr 28import android.databinding.tool.expr.ComparisonExpr 29import android.databinding.tool.expr.GroupExpr 30import android.databinding.tool.expr.MathExpr 31import android.databinding.tool.expr.MethodCallExpr 32import android.databinding.tool.expr.StaticIdentifierExpr 33import android.databinding.tool.expr.SymbolExpr 34import android.databinding.tool.ext.androidId 35import android.databinding.tool.ext.lazy 36import android.databinding.tool.ext.br 37import android.databinding.tool.expr.ResourceExpr 38import android.databinding.tool.expr.BracketExpr 39import android.databinding.tool.reflection.Callable 40import android.databinding.tool.expr.CastExpr 41import android.databinding.tool.reflection.ModelAnalyzer 42import java.util.HashMap 43 44fun String.stripNonJava() = this.split("[^a-zA-Z0-9]").map{ it.trim() }.joinToCamelCaseAsVar() 45 46class ExprModelExt { 47 val usedFieldNames = hashSetOf<String>() 48 val localizedFlags = arrayListOf<FlagSet>() 49 50 fun localizeFlag(set : FlagSet, name:String) : FlagSet { 51 localizedFlags.add(set) 52 val result = getUniqueFieldName(name) 53 set.setLocalName(result) 54 return set 55 } 56 57 fun getUniqueFieldName(base : String) : String { 58 var candidate = base 59 var i = 0 60 while (usedFieldNames.contains(candidate)) { 61 i ++ 62 candidate = base + i 63 } 64 usedFieldNames.add(candidate) 65 return candidate 66 } 67} 68 69val ExprModel.ext by Delegates.lazy { (target : ExprModel) -> 70 ExprModelExt() 71} 72 73fun ExprModel.getUniqueFieldName(base : String) : String = ext.getUniqueFieldName(base) 74 75fun ExprModel.localizeFlag(set : FlagSet, base : String) : FlagSet = ext.localizeFlag(set, base) 76 77val BindingTarget.readableUniqueName by Delegates.lazy {(target: BindingTarget) -> 78 val variableName : String 79 if (target.getId() == null) { 80 variableName = "boundView" + target.getTag() 81 } else { 82 variableName = target.getId().androidId().stripNonJava() 83 } 84 target.getModel().ext.getUniqueFieldName(variableName) 85} 86 87val BindingTarget.fieldName by Delegates.lazy { (target : BindingTarget) -> 88 if (target.getFieldName() == null) { 89 if (target.getId() == null) { 90 target.setFieldName("m${target.readableUniqueName.capitalize()}") 91 } else { 92 target.androidId.stripNonJava(); 93 target.setFieldName(target.readableUniqueName); 94 } 95 } 96 target.getFieldName(); 97} 98 99val BindingTarget.getterName by Delegates.lazy { (target : BindingTarget) -> 100 "get${target.readableUniqueName.capitalize()}" 101} 102 103val BindingTarget.androidId by Delegates.lazy { (target : BindingTarget) -> 104 "R.id.${target.getId().androidId()}" 105} 106 107val Expr.readableUniqueName by Delegates.lazy { (expr : Expr) -> 108 Log.d { "readableUniqueName for ${expr.getUniqueKey()}" } 109 val stripped = "${expr.getUniqueKey().stripNonJava()}" 110 expr.getModel().ext.getUniqueFieldName(stripped) 111} 112 113val Expr.fieldName by Delegates.lazy { (expr : Expr) -> 114 "m${expr.readableUniqueName.capitalize()}" 115} 116 117val Expr.hasFlag by Delegates.lazy { (expr : Expr) -> 118 expr.getId() < expr.getModel().getInvalidateableFieldLimit() 119} 120 121val Expr.localName by Delegates.lazy { (expr : Expr) -> 122 if(expr.isVariable()) expr.fieldName else "${expr.readableUniqueName}" 123} 124 125val Expr.setterName by Delegates.lazy { (expr : Expr) -> 126 "set${expr.readableUniqueName.capitalize()}" 127} 128 129val Expr.onChangeName by Delegates.lazy { (expr : Expr) -> 130 "onChange${expr.readableUniqueName.capitalize()}" 131} 132 133val Expr.getterName by Delegates.lazy { (expr : Expr) -> 134 "get${expr.readableUniqueName.capitalize()}" 135} 136 137val Expr.staticFieldName by Delegates.lazy { (expr : Expr) -> 138 "s${expr.readableUniqueName.capitalize()}" 139} 140 141val Expr.dirtyFlagName by Delegates.lazy { (expr : Expr) -> 142 "sFlag${expr.readableUniqueName.capitalize()}" 143} 144 145val Expr.shouldReadFlagName by Delegates.lazy { (expr : Expr) -> 146 "sFlagRead${expr.readableUniqueName.capitalize()}" 147} 148 149val Expr.invalidateFlagName by Delegates.lazy { (expr : Expr) -> 150 "sFlag${expr.readableUniqueName.capitalize()}Invalid" 151} 152 153val Expr.conditionalFlagPrefix by Delegates.lazy { (expr : Expr) -> 154 "sFlag${expr.readableUniqueName.capitalize()}Is" 155} 156 157 158fun Expr.toCode(full : Boolean = false) : KCode { 159 val it = this 160 if (isDynamic() && !full) { 161 return kcode(localName) 162 } 163 return when (it) { 164 is ComparisonExpr -> kcode("") { 165 app("", it.getLeft().toCode()) 166 app(it.getOp()) 167 app("", it.getRight().toCode()) 168 } 169 is FieldAccessExpr -> kcode("") { 170 app("", it.getChild().toCode()) 171 if (it.getGetter().type == Callable.Type.FIELD) { 172 app(".", it.getGetter().name) 173 } else { 174 app(".", it.getGetter().name).app("()") 175 } 176 } 177 is GroupExpr -> kcode("(").app("", it.getWrapped().toCode()).app(")") 178 is StaticIdentifierExpr -> kcode(it.getResolvedType().toJavaCode()) 179 is IdentifierExpr -> kcode(it.localName) 180 is MathExpr -> kcode("") { 181 app("", it.getLeft().toCode()) 182 app(it.getOp()) 183 app("", it.getRight().toCode()) 184 } 185 is MethodCallExpr -> kcode("") { 186 app("", it.getTarget().toCode()) 187 app(".", it.getGetter().name) 188 app("(") 189 var first = true 190 it.getArgs().forEach { 191 apps(if (first) "" else ",", it.toCode()) 192 first = false 193 } 194 app(")") 195 } 196 is SymbolExpr -> kcode(it.getText()) // TODO 197 is TernaryExpr -> kcode("") { 198 app("", it.getPred().toCode()) 199 app("?", it.getIfTrue().toCode()) 200 app(":", it.getIfFalse().toCode()) 201 } 202 is ResourceExpr -> kcode("") { 203 app("", it.toJava()) 204 } 205 is BracketExpr -> kcode("") { 206 app("", it.getTarget().toCode()) 207 val bracketType = it.getAccessor(); 208 when (bracketType) { 209 BracketExpr.BracketAccessor.ARRAY -> { 210 app("[", it.getArg().toCode()) 211 app("]") 212 } 213 BracketExpr.BracketAccessor.LIST -> { 214 app(".get(") 215 if (it.argCastsInteger()) { 216 app("(Integer)") 217 } 218 app("", it.getArg().toCode()) 219 app(")") 220 } 221 BracketExpr.BracketAccessor.MAP -> { 222 app(".get(", it.getArg().toCode()) 223 app(")") 224 } 225 } 226 } 227 is CastExpr -> kcode("") { 228 app("(", it.getCastType()) 229 app(") ", it.getCastExpr().toCode()) 230 } 231 else -> kcode("//NOT IMPLEMENTED YET") 232 } 233 234} 235 236fun Expr.isVariable() = this is IdentifierExpr && this.isDynamic() 237 238fun Expr.conditionalFlagName(output : Boolean, suffix : String) = "${dirtyFlagName}_${output}$suffix" 239 240 241val Expr.dirtyFlagSet by Delegates.lazy { (expr : Expr) -> 242 val fs = FlagSet(expr.getInvalidFlags(), expr.getModel().getFlagBucketCount()) 243 expr.getModel().localizeFlag(fs, expr.dirtyFlagName) 244} 245 246val Expr.invalidateFlagSet by Delegates.lazy { (expr : Expr) -> 247 val fs = FlagSet(expr.getId()) 248 expr.getModel().localizeFlag(fs, expr.invalidateFlagName) 249} 250 251val Expr.shouldReadFlagSet by Delegates.lazy { (expr : Expr) -> 252 val fs = FlagSet(expr.getShouldReadFlags(), expr.getModel().getFlagBucketCount()) 253 expr.getModel().localizeFlag(fs, expr.shouldReadFlagName) 254} 255 256val Expr.conditionalFlags by Delegates.lazy { (expr : Expr) -> 257 val model = expr.getModel() 258 arrayListOf(model.localizeFlag(FlagSet(expr.getRequirementFlagIndex(false)), 259 "${expr.conditionalFlagPrefix}False"), 260 model.localizeFlag(FlagSet(expr.getRequirementFlagIndex(true)), 261 "${expr.conditionalFlagPrefix}True")) 262} 263 264fun Expr.getRequirementFlagSet(expected : Boolean) : FlagSet = conditionalFlags[if(expected) 1 else 0] 265 266fun FlagSet.notEmpty(cb : (suffix : String, value : Long) -> Unit) { 267 buckets.withIndex().forEach { 268 if (it.value != 0L) { 269 cb(getWordSuffix(it.index), buckets[it.index]) 270 } 271 } 272} 273 274fun FlagSet.getBitSuffix(bitIndex : Int) : String { 275 val word = bitIndex / FlagSet.sBucketSize 276 return getWordSuffix(word) 277} 278 279fun FlagSet.getWordSuffix(wordIndex : Int) : String { 280 return if(wordIndex == 0) "" else "_${wordIndex}" 281} 282 283fun FlagSet.localValue(bucketIndex : Int) = 284 if (getLocalName() == null) buckets[bucketIndex] 285 else "${getLocalName()}${getWordSuffix(bucketIndex)}" 286 287fun FlagSet.or(other : FlagSet, cb : (suffix : String) -> Unit) { 288 val min = Math.min(buckets.size(), other.buckets.size()) 289 for (i in 0..(min - 1)) { 290 // if these two can match by any chance, call the callback 291 if (intersect(other, i)) { 292 cb(getWordSuffix(i)) 293 } 294 } 295} 296 297fun <T> FlagSet.mapOr(other : FlagSet, cb : (suffix : String, index : Int) -> T) : List<T> { 298 val min = Math.min(buckets.size(), other.buckets.size()) 299 val result = arrayListOf<T>() 300 for (i in 0..(min - 1)) { 301 // if these two can match by any chance, call the callback 302 if (intersect(other, i)) { 303 result.add(cb(getWordSuffix(i), i)) 304 } 305 } 306 return result 307} 308 309class LayoutBinderWriter(val layoutBinder : LayoutBinder) { 310 val model = layoutBinder.getModel() 311 val indices = HashMap<BindingTarget, kotlin.Int>() 312 val mDirtyFlags by Delegates.lazy { 313 val fs = FlagSet(BitSet(), model.getFlagBucketCount()); 314 Arrays.fill(fs.buckets, -1) 315 fs.setDynamic(true) 316 model.localizeFlag(fs, "mDirtyFlags") 317 fs 318 } 319 320 val dynamics by Delegates.lazy { model.getExprMap().values().filter { it.isDynamic() } } 321 val className = layoutBinder.getClassName() 322 323 val identifiers by Delegates.lazy { 324 dynamics.filter { it is IdentifierExpr } 325 } 326 327 val baseClassName = "${layoutBinder.getInterfaceName()}" 328 329 val includedBinders by Delegates.lazy { 330 layoutBinder.getBindingTargets().filter { it.isBinder() } 331 } 332 333 val variables by Delegates.lazy { 334 model.getExprMap().values().filterIsInstance(javaClass<IdentifierExpr>()).filter { it.isVariable() } 335 } 336 337 val usedVariables by Delegates.lazy { 338 variables.filter {it.isUsed()} 339 } 340 341 public fun write() : String { 342 layoutBinder.resolveWhichExpressionsAreUsed() 343 calculateIndices(); 344 return kcode("package ${layoutBinder.getPackage()};") { 345 nl("import ${layoutBinder.getModulePackage()}.R;") 346 nl("import ${layoutBinder.getModulePackage()}.BR;") 347 nl("import android.view.View;") 348 nl("public class ${className} extends ${baseClassName} {") { 349 tab(declareIncludeViews()) 350 tab(declareViews()) 351 tab(declareVariables()) 352 tab(declareConstructor()) 353 tab(declareInvalidateAll()) 354 tab(declareLog()) 355 tab(declareSetVariable()) 356 tab(variableSettersAndGetters()) 357 tab(onFieldChange()) 358 359 tab(executePendingBindings()) 360 361 tab(declareDirtyFlags()) 362 } 363 nl("}") 364 tab(flagMapping()) 365 tab("//end") 366 }.generate() 367 } 368 fun calculateIndices() : Unit { 369 val numTaggedViews = layoutBinder.getBindingTargets(). 370 filter{it.isUsed() && !it.isBinder()}.count() 371 layoutBinder.getBindingTargets().filter{ it.isUsed() && it.getTag() != null }.forEach { 372 indices.put(it, Integer.parseInt(it.getTag())); 373 } 374 layoutBinder.getBindingTargets().filter{ it.isUsed() && it.isBinder()}.withIndex().forEach { 375 indices.put(it.value, it.index + numTaggedViews); 376 } 377 } 378 fun declareIncludeViews() = kcode("") { 379 nl("private static final android.util.SparseIntArray sIncludes;") 380 nl("private static final android.util.SparseIntArray sViewsWithIds;") 381 nl("static {") { 382 val hasBinders = layoutBinder.getBindingTargets().firstOrNull{ it.isUsed() && it.isBinder()} != null 383 if (!hasBinders) { 384 tab("sIncludes = null;") 385 } else { 386 tab("sIncludes = new android.util.SparseIntArray();") 387 layoutBinder.getBindingTargets().filter{ it.isUsed() && it.isBinder()}.forEach { 388 tab("sIncludes.put(${it.androidId}, ${indices.get(it)});") 389 } 390 } 391 val hasViewsWithIds = layoutBinder.getBindingTargets().firstOrNull{ it.isUsed() && !it.supportsTag()} != null 392 if (!hasViewsWithIds) { 393 tab("sViewsWithIds = null;") 394 } else { 395 tab("sViewsWithIds = new android.util.SparseIntArray();") 396 layoutBinder.getBindingTargets().filter{ it.isUsed() && !it.supportsTag() }. 397 forEach { 398 tab("sViewsWithIds.put(${it.androidId}, ${indices.get(it)});") 399 } 400 } 401 } 402 nl("}") 403 } 404 fun declareConstructor() = kcode("") { 405 nl("private ${className}(View root, View[] views) {") { 406 tab("super(root, ${model.getObservables().size()}") { 407 layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach { 408 val index = indices.get(it) 409 if (!it.isUsed()) { 410 tab(", null") 411 } else if (index == null) { 412 tab(", (${it.getInterfaceType()}) root") 413 } else if (it.isBinder()) { 414 tab(", ${it.getViewClass()}.bind(views[${index}])") 415 } else { 416 tab(", (${it.getInterfaceType()}) views[${index}]") 417 } 418 } 419 tab(");") 420 } 421 val taggedViews = layoutBinder.getBindingTargets().filter{it.isUsed() && !it.isBinder()} 422 taggedViews.forEach { 423 if (it.getTag() == null) { 424 if (it.getId() == null) { 425 tab("this.${it.fieldName} = (${it.getViewClass()}) root;") 426 } 427 } else { 428 if (it.getId() == null) { 429 tab("this.${it.fieldName} = (${it.getViewClass()}) views[${it.getTag()}];") 430 } 431 if (it.supportsTag()) { 432 val originalTag = it.getOriginalTag(); 433 var tagValue = "null" 434 if (originalTag != null) { 435 tagValue = "\"${originalTag}\"" 436 if (originalTag.startsWith("@")) { 437 var packageName = layoutBinder.getModulePackage() 438 if (originalTag.startsWith("@android:")) { 439 packageName = "android" 440 } 441 val slashIndex = originalTag.indexOf('/') 442 val resourceId = originalTag.substring(slashIndex + 1) 443 tagValue = "root.getResources().getString(${packageName}.R.string.${resourceId})" 444 } 445 } 446 tab("this.${it.fieldName}.setTag(${tagValue});") 447 } 448 } 449 } 450 tab("invalidateAll();"); 451 nl("}") 452 } 453 nl("") 454 nl("public ${className}(View root) {") { 455 val viewCount = layoutBinder.getBindingTargets().filter{it.isUsed()}.count() 456 tab("this(root, mapChildViews(root, ${viewCount}, sIncludes, sViewsWithIds));") 457 } 458 nl("}") 459 } 460 461 fun declareInvalidateAll() = kcode("") { 462 nl("@Override") 463 nl("public void invalidateAll() {") { 464 val bs = BitSet() 465 bs.set(0, model.getInvalidateableFieldLimit()) 466 val fs = FlagSet(bs, mDirtyFlags.buckets.size()) 467 for (i in (0..(mDirtyFlags.buckets.size() - 1))) { 468 tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};") 469 } 470 includedBinders.filter{it.isUsed()}.forEach { binder -> 471 tab("${binder.fieldName}.invalidateAll();") 472 } 473 } 474 nl("}") 475 } 476 477 fun declareSetVariable() = kcode("") { 478 nl("public boolean setVariable(int variableId, Object variable) {") { 479 tab("switch(variableId) {") { 480 usedVariables.forEach { 481 tab ("case ${it.getName().br()} :") { 482 tab("${it.setterName}((${it.getResolvedType().toJavaCode()}) variable);") 483 tab("return true;") 484 } 485 } 486 } 487 tab("}") 488 tab("return false;") 489 } 490 nl("}") 491 } 492 493 fun declareLog() = kcode("") { 494 nl("private void log(String msg, long i) {") { 495 tab("""android.util.Log.d("BINDER", msg + ":" + Long.toHexString(i));""") 496 } 497 nl("}") 498 } 499 500 fun variableSettersAndGetters() = kcode("") { 501 variables.filterNot{it.isUsed()}.forEach { 502 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") { 503 tab("// not used, ignore") 504 } 505 nl("}") 506 nl("") 507 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 508 tab("return ${it.getDefaultValue()};") 509 } 510 nl("}") 511 } 512 usedVariables.forEach { 513 if (it.getUserDefinedType() != null) { 514 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") { 515 if (it.isObservable()) { 516 tab("updateRegistration(${it.getId()}, ${it.readableUniqueName});"); 517 } 518 tab("this.${it.fieldName} = ${it.readableUniqueName};") 519 // set dirty flags! 520 val flagSet = it.invalidateFlagSet 521 mDirtyFlags.mapOr(flagSet) { suffix, index -> 522 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 523 } 524 tab("super.requestRebind();") 525 } 526 nl("}") 527 nl("") 528 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 529 tab("return ${it.fieldName};") 530 } 531 nl("}") 532 } 533 } 534 } 535 536 fun onFieldChange() = kcode("") { 537 nl("@Override") 538 nl("protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {") { 539 tab("switch (localFieldId) {") { 540 model.getObservables().forEach { 541 tab("case ${it.getId()} :") { 542 tab("return ${it.onChangeName}((${it.getResolvedType().toJavaCode()}) object, fieldId);") 543 } 544 } 545 } 546 tab("}") 547 tab("return false;") 548 } 549 nl("}") 550 nl("") 551 552 model.getObservables().forEach { 553 nl("private boolean ${it.onChangeName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}, int fieldId) {") { 554 tab("switch (fieldId) {", { 555 val accessedFields: List<FieldAccessExpr> = it.getParents().filterIsInstance(javaClass<FieldAccessExpr>()) 556 accessedFields.filter { it.canBeInvalidated() } 557 .groupBy { it.getName() } 558 .forEach { 559 tab("case ${it.key.br()}:") { 560 val field = it.value.first() 561 mDirtyFlags.mapOr(field.invalidateFlagSet) { suffix, index -> 562 tab("${mDirtyFlags.localValue(index)} |= ${field.invalidateFlagSet.localValue(index)};") 563 } 564 tab("return true;") 565 } 566 567 } 568 tab("case ${"".br()}:") { 569 val flagSet = it.invalidateFlagSet 570 mDirtyFlags.mapOr(flagSet) { suffix, index -> 571 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 572 } 573 tab("return true;") 574 } 575 576 }) 577 tab("}") 578 tab("return false;") 579 } 580 nl("}") 581 nl("") 582 } 583 } 584 585 fun declareViews() = kcode("// views") { 586 layoutBinder.getBindingTargets().filter {it.isUsed() && (it.getId() == null)}.forEach { 587 nl("private final ${it.getInterfaceType()} ${it.fieldName};") 588 } 589 } 590 591 fun declareVariables() = kcode("// variables") { 592 usedVariables.forEach { 593 nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};") 594 } 595 } 596 597 fun declareDirtyFlags() = kcode("// dirty flag") { 598 model.ext.localizedFlags.forEach { flag -> 599 flag.notEmpty { (suffix, value) -> 600 nl("private") 601 app(" ", if(flag.isDynamic()) null else "static final"); 602 app(" ", " ${flag.type} ${flag.getLocalName()}$suffix = $value;") 603 } 604 } 605 } 606 607 fun flagMapping() = kcode("/* flag mapping") { 608 if (model.getFlagMapping() != null) { 609 val mapping = model.getFlagMapping() 610 for (i in mapping.indices) { 611 tab("flag $i: ${mapping[i]}") 612 } 613 } 614 nl("flag mapping end*/") 615 } 616 617 fun executePendingBindings() = kcode("") { 618 nl("@Override") 619 nl("public void executePendingBindings() {") { 620 val tmpDirtyFlags = FlagSet(mDirtyFlags.buckets) 621 tmpDirtyFlags.setLocalName("dirtyFlags"); 622 for (i in (0..mDirtyFlags.buckets.size() - 1)) { 623 tab("${tmpDirtyFlags.type} ${tmpDirtyFlags.localValue(i)} = ${mDirtyFlags.localValue(i)};") 624 tab("${mDirtyFlags.localValue(i)} = 0;") 625 } 626 //tab("""log("dirty flags", mDirtyFlags);""") 627 model.getPendingExpressions().filterNot {it.isVariable()}.forEach { 628 tab("${it.getResolvedType().toJavaCode()} ${it.localName} = ${it.getDefaultValue()};") 629 } 630 631 do { 632 val batch = model.filterShouldRead(model.getPendingExpressions()).toArrayList() 633 val mJustRead = arrayListOf<Expr>() 634 while (!batch.none()) { 635 val readNow = batch.filter { it.shouldReadNow(mJustRead) } 636 if (readNow.isEmpty()) { 637 throw IllegalStateException("do not know what I can read. bailing out ${batch.joinToString("\n")}") 638 } 639 Log.d { "new read now. batch size: ${batch.size()}, readNow size: ${readNow.size()}" } 640 641 readNow.forEach { 642 nl(readWithDependants(it, mJustRead, batch, tmpDirtyFlags)) 643 } 644 batch.removeAll(mJustRead) 645 } 646 tab("// batch finished") 647 } while(model.markBitsRead()) 648 649 // 650 layoutBinder.getBindingTargets().filter { it.isUsed() } 651 .flatMap { it.getBindings() } 652 .groupBy { it.getExpr() } 653 .forEach { 654 val flagSet = it.key.dirtyFlagSet 655 tab("if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 656 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 657 }.joinToString(" || ") 658 }) {") { 659 it.value.forEach { binding -> 660 tab("// api target ${binding.getMinApi()}") 661 val fieldName : String 662 if (binding.getTarget().getViewClass(). 663 equals(binding.getTarget().getInterfaceType())) { 664 fieldName = "this.${binding.getTarget().fieldName}" 665 } else { 666 fieldName = "((${binding.getTarget().getViewClass()}) this.${binding.getTarget().fieldName})" 667 } 668 val bindingCode = binding.toJavaCode(fieldName, binding.getExpr().toCode().generate()) 669 if (binding.getMinApi() > 1) { 670 tab("if(getBuildSdkInt() >= ${binding.getMinApi()}) {") { 671 tab("$bindingCode;") 672 } 673 tab("}") 674 } else { 675 tab("$bindingCode;") 676 } 677 } 678 } 679 tab("}") 680 } 681 // 682 includedBinders.filter{it.isUsed()}.forEach { binder -> 683 tab("${binder.fieldName}.executePendingBindings();") 684 } 685 } 686 nl("}") 687 } 688 689 fun readWithDependants(expr : Expr, mJustRead : MutableList<Expr>, batch : MutableList<Expr>, tmpDirtyFlags : FlagSet) : KCode = kcode("") { 690 mJustRead.add(expr) 691 Log.d { expr.getUniqueKey() } 692 val flagSet = expr.shouldReadFlagSet 693 tab("if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 694 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 695 }.joinToString(" || ") 696 }) {") { 697 if (!expr.isVariable()) { 698 // it is not a variable read it. 699 tab("// read ${expr.getUniqueKey()}") 700 // create an if case for all dependencies that might be null 701 val nullables = expr.getDependencies().filter { 702 it.isMandatory() && it.getOther().getResolvedType().isNullable() 703 } 704 .map { it.getOther() } 705 if (!expr.isEqualityCheck() && nullables.isNotEmpty()) { 706 tab ("if ( ${nullables.map { "${it.localName} != null" }.joinToString(" && ")}) {") { 707 tab("${expr.localName}").app(" = ", expr.toCode(true)).app(";") 708 //tab("""log("${expr}" + ${expr.localName},0);""") 709 } 710 tab("}") 711 } else { 712 tab("${expr.localName}").app(" = ", expr.toCode(true)).app(";") 713 //tab("""log("${expr}" + ${expr.localName},0);""") 714 } 715 if (expr.isObservable()) { 716 tab("updateRegistration(${expr.getId()}, ${expr.localName});") 717 } 718 } 719 720 // if I am the condition for an expression, set its flag 721 val conditionals = expr.getDependants().filter { !it.isConditional() 722 && it.getDependant() is TernaryExpr && (it.getDependant() as TernaryExpr).getPred() == expr } 723 .map { it.getDependant() } 724 if (conditionals.isNotEmpty()) { 725 tab("// setting conditional flags") 726 tab("if (${expr.localName}) {") { 727 conditionals.forEach { 728 val set = it.getRequirementFlagSet(true) 729 mDirtyFlags.mapOr(set) { suffix , index -> 730 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 731 } 732 } 733 } 734 tab("} else {") { 735 conditionals.forEach { 736 val set = it.getRequirementFlagSet(false) 737 mDirtyFlags.mapOr(set) { suffix , index -> 738 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 739 } 740 } 741 } tab("}") 742 } 743 744 val chosen = expr.getDependants().filter { 745 batch.contains(it.getDependant()) && it.getDependant().shouldReadNow(mJustRead) 746 } 747 if (chosen.isNotEmpty()) { 748 chosen.forEach { 749 nl(readWithDependants(it.getDependant(), mJustRead, batch, tmpDirtyFlags)) 750 } 751 } 752 } 753 tab("}") 754 } 755 756 public fun writeBaseClass() : String = 757 kcode("package ${layoutBinder.getPackage()};") { 758 nl("import android.databinding.Bindable;") 759 nl("import android.databinding.DataBindingUtil;") 760 nl("import android.databinding.ViewDataBinding;") 761 nl("public abstract class ${baseClassName} extends ViewDataBinding {") 762 layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach { 763 tab("public final ${it.getInterfaceType()} ${it.fieldName};") 764 } 765 nl("") 766 tab("protected ${baseClassName}(android.view.View root_, int localFieldCount") { 767 layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach { 768 tab(", ${it.getInterfaceType()} ${it.readableUniqueName}") 769 } 770 } 771 tab(") {") { 772 tab("super(root_, localFieldCount);") 773 layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach { 774 tab("this.${it.fieldName} = ${it.readableUniqueName};") 775 } 776 } 777 tab("}") 778 nl("") 779 variables.forEach { 780 if (it.getUserDefinedType() != null) { 781 tab("@Bindable") 782 //it.getExpandedUserDefinedType(ModelAnalyzer.getInstance()); 783 val type = ModelAnalyzer.getInstance().applyImports(it.getUserDefinedType(), model.getImports()) 784 tab("public abstract void ${it.setterName}(${type} ${it.readableUniqueName});") 785 } 786 } 787 tab("public static ${baseClassName} inflate(android.view.ViewGroup root) {") { 788 tab("return DataBindingUtil.<${baseClassName}>inflate(root.getContext(), ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, true);") 789 } 790 tab("}") 791 tab("public static ${baseClassName} inflate(android.content.Context context) {") { 792 tab("return DataBindingUtil.<${baseClassName}>inflate(context, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false);") 793 } 794 tab("}") 795 tab("public static ${baseClassName} bind(android.view.View view) {") { 796 tab("return (${baseClassName})DataBindingUtil.bindTo(view, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()});") 797 } 798 tab("}") 799 nl("}") 800 }.generate() 801}