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