LayoutBinderWriter.kt revision d8c8ec27ed2ec0b11fa37f476395ce27834471f0
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 FlagSet(expr.getInvalidFlags(), expr.getModel().getFlagBucketCount()) 262} 263 264val Expr.invalidateFlagSet by Delegates.lazy { expr : Expr -> 265 FlagSet(expr.getId()) 266} 267 268val Expr.shouldReadFlagSet by Delegates.lazy { expr : Expr -> 269 FlagSet(expr.getShouldReadFlags(), expr.getModel().getFlagBucketCount()) 270} 271 272val Expr.conditionalFlags by Delegates.lazy { expr : Expr -> 273 arrayListOf(FlagSet(expr.getRequirementFlagIndex(false)), 274 FlagSet(expr.getRequirementFlagIndex(true))) 275} 276 277fun Expr.getRequirementFlagSet(expected : Boolean) : FlagSet = conditionalFlags[if(expected) 1 else 0] 278 279fun FlagSet.notEmpty(cb : (suffix : String, value : Long) -> Unit) { 280 buckets.withIndex().forEach { 281 if (it.value != 0L) { 282 cb(getWordSuffix(it.index), buckets[it.index]) 283 } 284 } 285} 286 287fun FlagSet.getBitSuffix(bitIndex : Int) : String { 288 val word = bitIndex / FlagSet.sBucketSize 289 return getWordSuffix(word) 290} 291 292fun FlagSet.getWordSuffix(wordIndex : Int) : String { 293 return if(wordIndex == 0) "" else "_${wordIndex}" 294} 295 296fun FlagSet.localValue(bucketIndex : Int) = 297 if (getLocalName() == null) binaryCode(bucketIndex) 298 else "${getLocalName()}${getWordSuffix(bucketIndex)}" 299 300fun FlagSet.binaryCode(bucketIndex : Int) = longToBinary(buckets[bucketIndex]) 301 302 303fun longToBinary(l : Long) = 304 "0b${java.lang.Long.toBinaryString(l)}L" 305 306fun <T> FlagSet.mapOr(other : FlagSet, cb : (suffix : String, index : Int) -> T) : List<T> { 307 val min = Math.min(buckets.size(), other.buckets.size()) 308 val result = arrayListOf<T>() 309 for (i in 0..(min - 1)) { 310 // if these two can match by any chance, call the callback 311 if (intersect(other, i)) { 312 result.add(cb(getWordSuffix(i), i)) 313 } 314 } 315 return result 316} 317 318class LayoutBinderWriter(val layoutBinder : LayoutBinder) { 319 val model = layoutBinder.getModel() 320 val indices = HashMap<BindingTarget, kotlin.Int>() 321 val mDirtyFlags by Delegates.lazy { 322 val fs = FlagSet(BitSet(), model.getFlagBucketCount()); 323 Arrays.fill(fs.buckets, -1) 324 fs.setDynamic(true) 325 model.localizeFlag(fs, "mDirtyFlags") 326 fs 327 } 328 329 val dynamics by Delegates.lazy { model.getExprMap().values().filter { it.isDynamic() } } 330 val className = layoutBinder.getImplementationName() 331 332 val identifiers by Delegates.lazy { 333 dynamics.filter { it is IdentifierExpr } 334 } 335 336 val baseClassName = "${layoutBinder.getClassName()}" 337 338 val includedBinders by Delegates.lazy { 339 layoutBinder.getBindingTargets().filter { it.isBinder() } 340 } 341 342 val variables by Delegates.lazy { 343 model.getExprMap().values().filterIsInstance(javaClass<IdentifierExpr>()).filter { it.isVariable() } 344 } 345 346 val usedVariables by Delegates.lazy { 347 variables.filter {it.isUsed()} 348 } 349 350 public fun write() : String { 351 layoutBinder.resolveWhichExpressionsAreUsed() 352 calculateIndices(); 353 return kcode("package ${layoutBinder.getPackage()};") { 354 nl("import ${layoutBinder.getModulePackage()}.R;") 355 nl("import ${layoutBinder.getModulePackage()}.BR;") 356 nl("import android.view.View;") 357 val classDeclaration : String 358 if (layoutBinder.hasVariations()) { 359 classDeclaration = "${className} extends ${baseClassName}" 360 } else { 361 classDeclaration = "${className} extends android.databinding.ViewDataBinding" 362 } 363 nl("public class ${classDeclaration} {") { 364 tab(declareIncludeViews()) 365 tab(declareViews()) 366 tab(declareVariables()) 367 tab(declareConstructor()) 368 tab(declareInvalidateAll()) 369 tab(declareLog()) 370 tab(declareSetVariable()) 371 tab(variableSettersAndGetters()) 372 tab(onFieldChange()) 373 374 tab(executePendingBindings()) 375 376 tab(declareDirtyFlags()) 377 if (!layoutBinder.hasVariations()) { 378 tab(declareFactories()) 379 } 380 } 381 nl("}") 382 tab(flagMapping()) 383 tab("//end") 384 }.generate() 385 } 386 fun calculateIndices() : Unit { 387 val numTaggedViews = layoutBinder.getBindingTargets(). 388 filter{it.isUsed() && it.getTag() != null}.count() 389 layoutBinder.getBindingTargets().filter{ it.isUsed() && it.getTag() != null }.forEach { 390 indices.put(it, Integer.parseInt(it.getTag())); 391 } 392 layoutBinder.getBindingTargets().filter{ it.isUsed() && it.getTag() == null && it.getId() != null }.withIndex().forEach { 393 indices.put(it.value, it.index + numTaggedViews); 394 } 395 } 396 fun declareIncludeViews() = kcode("") { 397 nl("private static final android.util.SparseIntArray sIncludes;") 398 nl("private static final android.util.SparseIntArray sViewsWithIds;") 399 nl("static {") { 400 val hasBinders = layoutBinder.getBindingTargets().firstOrNull{ it.isUsed() && it.isBinder()} != null 401 if (!hasBinders) { 402 tab("sIncludes = null;") 403 } else { 404 tab("sIncludes = new android.util.SparseIntArray();") 405 layoutBinder.getBindingTargets().filter{ it.isUsed() && it.isBinder()}.forEach { 406 tab("sIncludes.put(${it.androidId}, ${indices.get(it)});") 407 } 408 } 409 val hasViewsWithIds = layoutBinder.getBindingTargets().firstOrNull{ 410 it.isUsed() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null)) 411 } != null 412 if (!hasViewsWithIds) { 413 tab("sViewsWithIds = null;") 414 } else { 415 tab("sViewsWithIds = new android.util.SparseIntArray();") 416 layoutBinder.getBindingTargets().filter{ 417 it.isUsed() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null)) 418 }.forEach { 419 tab("sViewsWithIds.put(${it.androidId}, ${indices.get(it)});") 420 } 421 } 422 } 423 nl("}") 424 } 425 fun declareConstructor() = kcode("") { 426 val viewCount = layoutBinder.getBindingTargets().filter{it.isUsed()}.count() 427 if (layoutBinder.hasVariations()) { 428 nl("") 429 nl("public ${className}(View root) {") { 430 tab("this(root, mapChildViews(root, ${viewCount}, sIncludes, sViewsWithIds));") 431 } 432 nl("}") 433 nl("private ${className}(View root, View[] views) {") { 434 tab("super(root, ${model.getObservables().size()}") { 435 layoutBinder.getBindingTargets().filter { it.getId() != null }.forEach { 436 tab(", ${fieldConversion(it)}") 437 } 438 tab(");") 439 } 440 } 441 } else { 442 nl("${baseClassName}(View root) {") { 443 tab("super(root, ${model.getObservables().size()});") 444 tab("final View[] views = mapChildViews(root, ${viewCount}, sIncludes, sViewsWithIds);") 445 } 446 } 447 val taggedViews = layoutBinder.getBindingTargets().filter{it.isUsed()} 448 taggedViews.forEach { 449 if (!layoutBinder.hasVariations() || it.getId() == null) { 450 tab("this.${it.fieldName} = ${fieldConversion(it)};") 451 } 452 if (!it.isBinder()) { 453 if (it.getResolvedType() != null && it.getResolvedType().extendsViewStub()) { 454 tab("this.${it.fieldName}.setContainingBinding(this);") 455 } 456 if (it.supportsTag() && it.getTag() != null) { 457 val originalTag = it.getOriginalTag(); 458 var tagValue = "null" 459 if (originalTag != null) { 460 tagValue = "\"${originalTag}\"" 461 if (originalTag.startsWith("@")) { 462 var packageName = layoutBinder.getModulePackage() 463 if (originalTag.startsWith("@android:")) { 464 packageName = "android" 465 } 466 val slashIndex = originalTag.indexOf('/') 467 val resourceId = originalTag.substring(slashIndex + 1) 468 tagValue = "root.getResources().getString(${packageName}.R.string.${resourceId})" 469 } 470 } 471 tab("this.${it.fieldName}.setTag(${tagValue});") 472 } 473 } 474 } 475 tab("invalidateAll();"); 476 nl("}") 477 } 478 479 fun fieldConversion(target : BindingTarget) : String { 480 val index = indices.get(target) 481 if (!target.isUsed()) { 482 return "null" 483 } else { 484 val variableName: String 485 if (index == null) { 486 variableName = "root"; 487 } else { 488 variableName = "views[${index}]" 489 } 490 return target.superConversion(variableName) 491 } 492 } 493 494 fun declareInvalidateAll() = kcode("") { 495 nl("@Override") 496 nl("public void invalidateAll() {") { 497 val bs = BitSet() 498 bs.set(0, model.getInvalidateableFieldLimit()) 499 val fs = FlagSet(bs, mDirtyFlags.buckets.size()) 500 for (i in (0..(mDirtyFlags.buckets.size() - 1))) { 501 tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};") 502 } 503 includedBinders.filter{it.isUsed()}.forEach { binder -> 504 tab("${binder.fieldName}.invalidateAll();") 505 } 506 } 507 nl("}") 508 } 509 510 fun declareSetVariable() = kcode("") { 511 nl("public boolean setVariable(int variableId, Object variable) {") { 512 tab("switch(variableId) {") { 513 usedVariables.forEach { 514 tab ("case ${it.getName().br()} :") { 515 tab("${it.setterName}((${it.getResolvedType().toJavaCode()}) variable);") 516 tab("return true;") 517 } 518 } 519 } 520 tab("}") 521 tab("return false;") 522 } 523 nl("}") 524 } 525 526 fun declareLog() = kcode("") { 527 nl("private void log(String msg, long i) {") { 528 tab("""android.util.Log.d("BINDER", msg + ":" + Long.toHexString(i));""") 529 } 530 nl("}") 531 } 532 533 fun variableSettersAndGetters() = kcode("") { 534 variables.filterNot{it.isUsed()}.forEach { 535 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") { 536 tab("// not used, ignore") 537 } 538 nl("}") 539 nl("") 540 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 541 tab("return ${it.getDefaultValue()};") 542 } 543 nl("}") 544 } 545 usedVariables.forEach { 546 if (it.getUserDefinedType() != null) { 547 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") { 548 if (it.isObservable()) { 549 tab("updateRegistration(${it.getId()}, ${it.readableUniqueName});"); 550 } 551 tab("this.${it.fieldName} = ${it.readableUniqueName};") 552 // set dirty flags! 553 val flagSet = it.invalidateFlagSet 554 mDirtyFlags.mapOr(flagSet) { suffix, index -> 555 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 556 } 557 tab("super.requestRebind();") 558 } 559 nl("}") 560 nl("") 561 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 562 tab("return ${it.fieldName};") 563 } 564 nl("}") 565 } 566 } 567 } 568 569 fun onFieldChange() = kcode("") { 570 nl("@Override") 571 nl("protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {") { 572 tab("switch (localFieldId) {") { 573 model.getObservables().forEach { 574 tab("case ${it.getId()} :") { 575 tab("return ${it.onChangeName}((${it.getResolvedType().toJavaCode()}) object, fieldId);") 576 } 577 } 578 } 579 tab("}") 580 tab("return false;") 581 } 582 nl("}") 583 nl("") 584 585 model.getObservables().forEach { 586 nl("private boolean ${it.onChangeName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}, int fieldId) {") { 587 tab("switch (fieldId) {", { 588 val accessedFields: List<FieldAccessExpr> = it.getParents().filterIsInstance(javaClass<FieldAccessExpr>()) 589 accessedFields.filter { it.canBeInvalidated() } 590 .groupBy { it.getName() } 591 .forEach { 592 tab("case ${it.key.br()}:") { 593 val field = it.value.first() 594 mDirtyFlags.mapOr(field.invalidateFlagSet) { suffix, index -> 595 tab("${mDirtyFlags.localValue(index)} |= ${field.invalidateFlagSet.localValue(index)};") 596 } 597 tab("return true;") 598 } 599 600 } 601 tab("case ${"".br()}:") { 602 val flagSet = it.invalidateFlagSet 603 mDirtyFlags.mapOr(flagSet) { suffix, index -> 604 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 605 } 606 tab("return true;") 607 } 608 609 }) 610 tab("}") 611 tab("return false;") 612 } 613 nl("}") 614 nl("") 615 } 616 } 617 618 fun declareViews() = kcode("// views") { 619 val oneLayout = !layoutBinder.hasVariations(); 620 layoutBinder.getBindingTargets().filter {it.isUsed() && (oneLayout || it.getId() == null)}.forEach { 621 val access : String 622 if (oneLayout && it.getId() != null) { 623 access = "public" 624 } else { 625 access = "private" 626 } 627 nl("${access} final ${it.interfaceType} ${it.fieldName};") 628 } 629 } 630 631 fun declareVariables() = kcode("// variables") { 632 usedVariables.forEach { 633 nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};") 634 } 635 } 636 637 fun declareDirtyFlags() = kcode("// dirty flag") { 638 model.ext.localizedFlags.forEach { flag -> 639 flag.notEmpty { suffix, value -> 640 nl("private") 641 app(" ", if(flag.isDynamic()) null else "static final"); 642 app(" ", " ${flag.type} ${flag.getLocalName()}$suffix = ${longToBinary(value)};") 643 } 644 } 645 } 646 647 fun flagMapping() = kcode("/* flag mapping") { 648 if (model.getFlagMapping() != null) { 649 val mapping = model.getFlagMapping() 650 for (i in mapping.indices) { 651 tab("flag $i: ${mapping[i]}") 652 } 653 } 654 nl("flag mapping end*/") 655 } 656 657 fun executePendingBindings() = kcode("") { 658 nl("@Override") 659 nl("public void executePendingBindings() {") { 660 val tmpDirtyFlags = FlagSet(mDirtyFlags.buckets) 661 tmpDirtyFlags.setLocalName("dirtyFlags"); 662 for (i in (0..mDirtyFlags.buckets.size() - 1)) { 663 tab("${tmpDirtyFlags.type} ${tmpDirtyFlags.localValue(i)} = ${mDirtyFlags.localValue(i)};") 664 tab("${mDirtyFlags.localValue(i)} = 0;") 665 } 666 model.getPendingExpressions().filterNot {it.isVariable()}.forEach { 667 tab("${it.getResolvedType().toJavaCode()} ${it.localName} = ${it.getDefaultValue()};") 668 } 669 670 do { 671 val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList() 672 val mJustRead = arrayListOf<Expr>() 673 while (!batch.none()) { 674 val readNow = batch.filter { it.shouldReadNow(mJustRead) } 675 if (readNow.isEmpty()) { 676 throw IllegalStateException("do not know what I can read. bailing out ${batch.joinToString("\n")}") 677 } 678 Log.d { "new read now. batch size: ${batch.size()}, readNow size: ${readNow.size()}" } 679 680 readNow.forEach { 681 nl(readWithDependants(it, mJustRead, batch, tmpDirtyFlags)) 682 } 683 batch.removeAll(mJustRead) 684 } 685 tab("// batch finished") 686 } while(model.markBitsRead()) 687 688 // 689 layoutBinder.getBindingTargets().filter { it.isUsed() } 690 .flatMap { it.getBindings() } 691 .groupBy { it.getExpr() } 692 .forEach { 693 val flagSet = it.key.dirtyFlagSet 694 tab("if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 695 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 696 }.joinToString(" || ") 697 }) {") { 698 it.value.forEach { binding -> 699 tab("// api target ${binding.getMinApi()}") 700 val fieldName : String 701 if (binding.getTarget().getViewClass(). 702 equals(binding.getTarget().getInterfaceType())) { 703 fieldName = "this.${binding.getTarget().fieldName}" 704 } else { 705 fieldName = "((${binding.getTarget().getViewClass()}) this.${binding.getTarget().fieldName})" 706 } 707 val bindingCode = binding.toJavaCode(fieldName, binding.getExpr().toCode().generate()) 708 if (binding.getMinApi() > 1) { 709 tab("if(getBuildSdkInt() >= ${binding.getMinApi()}) {") { 710 tab("$bindingCode;") 711 } 712 tab("}") 713 } else { 714 tab("$bindingCode;") 715 } 716 } 717 } 718 tab("}") 719 } 720 includedBinders.filter{it.isUsed()}.forEach { binder -> 721 tab("${binder.fieldName}.executePendingBindings();") 722 } 723 layoutBinder.getBindingTargets().filter{ 724 it.isUsed() && it.getResolvedType() != null && it.getResolvedType().extendsViewStub() 725 }.forEach { 726 tab("if (${it.fieldName}.getBinding() != null) {") { 727 tab("${it.fieldName}.getBinding().executePendingBindings();") 728 } 729 tab("}") 730 } 731 } 732 nl("}") 733 } 734 735 fun readWithDependants(expr : Expr, mJustRead : MutableList<Expr>, batch : MutableList<Expr>, 736 tmpDirtyFlags : FlagSet, inheritedFlags : FlagSet? = null) : KCode = kcode("") { 737 mJustRead.add(expr) 738 Log.d { expr.getUniqueKey() } 739 val flagSet = expr.shouldReadFlagSet 740 val needsIfWrapper = inheritedFlags == null || !flagSet.bitsEqual(inheritedFlags) 741 val ifClause = "if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 742 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 743 }.joinToString(" || ") 744 })" 745 746 val readCode = kcode("") { 747 if (!expr.isVariable()) { 748 // it is not a variable read it. 749 tab("// read ${expr.getUniqueKey()}") 750 // create an if case for all dependencies that might be null 751 val nullables = expr.getDependencies().filter { 752 it.isMandatory() && it.getOther().getResolvedType().isNullable() 753 }.map { it.getOther() } 754 if (!expr.isEqualityCheck() && nullables.isNotEmpty()) { 755 tab ("if ( ${nullables.map { "${it.localName} != null" }.joinToString(" && ")}) {") { 756 tab("${expr.localName}").app(" = ", expr.toCode(true)).app(";") 757 } 758 tab("}") 759 } else { 760 tab("${expr.localName}").app(" = ", expr.toCode(true)).app(";") 761 } 762 if (expr.isObservable()) { 763 tab("updateRegistration(${expr.getId()}, ${expr.localName});") 764 } 765 } 766 767 // if I am the condition for an expression, set its flag 768 val conditionals = expr.getDependants().filter { !it.isConditional() 769 && it.getDependant() is TernaryExpr && (it.getDependant() as TernaryExpr).getPred() == expr } 770 .map { it.getDependant() } 771 if (conditionals.isNotEmpty()) { 772 tab("// setting conditional flags") 773 tab("if (${expr.localName}) {") { 774 conditionals.forEach { 775 val set = it.getRequirementFlagSet(true) 776 mDirtyFlags.mapOr(set) { suffix , index -> 777 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 778 } 779 } 780 } 781 tab("} else {") { 782 conditionals.forEach { 783 val set = it.getRequirementFlagSet(false) 784 mDirtyFlags.mapOr(set) { suffix , index -> 785 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 786 } 787 } 788 } tab("}") 789 } 790 791 val chosen = expr.getDependants().filter { 792 val dependant = it.getDependant() 793 batch.contains(dependant) && 794 dependant.shouldReadFlagSet.andNot(flagSet).isEmpty() && 795 dependant.shouldReadNow(mJustRead) 796 } 797 if (chosen.isNotEmpty()) { 798 val nextInheritedFlags = if (needsIfWrapper) flagSet else inheritedFlags 799 chosen.forEach { 800 nl(readWithDependants(it.getDependant(), mJustRead, batch, tmpDirtyFlags, nextInheritedFlags)) 801 } 802 } 803 } 804 if (needsIfWrapper) { 805 tab(ifClause) { 806 app(" {") 807 nl(readCode) 808 } 809 tab("}") 810 } else { 811 nl(readCode) 812 } 813 } 814 815 fun declareFactories() = kcode("") { 816 nl("public static ${baseClassName} inflate(android.view.ViewGroup root) {") { 817 tab("return bind(android.view.LayoutInflater.from(root.getContext()).inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, true));") 818 } 819 nl("}") 820 nl("public static ${baseClassName} inflate(android.content.Context context) {") { 821 tab("return bind(android.view.LayoutInflater.from(context).inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false));") 822 } 823 nl("}") 824 nl("public static ${baseClassName} bind(android.view.View view) {") { 825 tab("if (!\"${layoutBinder.getId()}\".equals(view.getTag())) {") { 826 tab("throw new RuntimeException(\"view tag isn't correct on view\");") 827 } 828 tab("}") 829 tab("return new ${baseClassName}(view);") 830 } 831 nl("}") 832 } 833 834 public fun writeBaseClass() : String = 835 kcode("package ${layoutBinder.getPackage()};") { 836 nl("import android.databinding.Bindable;") 837 nl("import android.databinding.DataBindingUtil;") 838 nl("import android.databinding.ViewDataBinding;") 839 nl("public abstract class ${baseClassName} extends ViewDataBinding {") 840 layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach { 841 tab("public final ${it.interfaceType} ${it.fieldName};") 842 } 843 nl("") 844 tab("protected ${baseClassName}(android.view.View root_, int localFieldCount") { 845 layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach { 846 tab(", ${it.interfaceType} ${it.readableUniqueName}") 847 } 848 } 849 tab(") {") { 850 tab("super(root_, localFieldCount);") 851 layoutBinder.getBindingTargets().filter{it.getId() != null}.forEach { 852 tab("this.${it.fieldName} = ${it.readableUniqueName};") 853 } 854 } 855 tab("}") 856 nl("") 857 variables.forEach { 858 if (it.getUserDefinedType() != null) { 859 //it.getExpandedUserDefinedType(ModelAnalyzer.getInstance()); 860 val type = ModelAnalyzer.getInstance().applyImports(it.getUserDefinedType(), model.getImports()) 861 tab("public abstract void ${it.setterName}(${type} ${it.readableUniqueName});") 862 } 863 } 864 tab("public static ${baseClassName} inflate(android.view.ViewGroup root) {") { 865 tab("return DataBindingUtil.<${baseClassName}>inflate(root.getContext(), ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, true);") 866 } 867 tab("}") 868 tab("public static ${baseClassName} inflate(android.content.Context context) {") { 869 tab("return DataBindingUtil.<${baseClassName}>inflate(context, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false);") 870 } 871 tab("}") 872 tab("public static ${baseClassName} bind(android.view.View view) {") { 873 tab("return (${baseClassName})DataBindingUtil.bindTo(view, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()});") 874 } 875 tab("}") 876 nl("}") 877 }.generate() 878} 879