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