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