LayoutBinderWriter.kt revision f80b08430f11515ff944f89006799f21545602b9
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 kotlin.properties.Delegates 18import android.databinding.tool.BindingTarget 19import android.databinding.tool.expr.Expr 20import kotlin.properties.Delegates 21import android.databinding.tool.ext.joinToCamelCaseAsVar 22import android.databinding.tool.BindingTarget 23import android.databinding.tool.expr.IdentifierExpr 24import android.databinding.tool.util.Log 25import java.util.BitSet 26import android.databinding.tool.expr.ExprModel 27import java.util.Arrays 28import android.databinding.tool.expr.BitShiftExpr 29import android.databinding.tool.expr.TernaryExpr 30import android.databinding.tool.expr.FieldAccessExpr 31import android.databinding.tool.expr.ComparisonExpr 32import android.databinding.tool.expr.GroupExpr 33import android.databinding.tool.expr.InstanceOfExpr 34import android.databinding.tool.expr.MathExpr 35import android.databinding.tool.expr.MethodCallExpr 36import android.databinding.tool.expr.StaticIdentifierExpr 37import android.databinding.tool.expr.SymbolExpr 38import android.databinding.tool.expr.UnaryExpr 39import android.databinding.tool.ext.androidId 40import android.databinding.tool.ext.lazy 41import android.databinding.tool.ext.br 42import android.databinding.tool.expr.ResourceExpr 43import android.databinding.tool.expr.BracketExpr 44import android.databinding.tool.ext; 45import android.databinding.tool.util.Log 46import java.util.BitSet 47import java.util.Arrays 48import android.databinding.tool.reflection.Callable 49import android.databinding.tool.reflection.ModelAnalyzer 50import java.util.ArrayList 51import java.util.HashMap 52 53fun String.stripNonJava() = this.split("[^a-zA-Z0-9]").map{ it.trim() }.joinToCamelCaseAsVar() 54 55enum class Scope { 56 FIELD 57 METHOD 58 FLAG 59 EXECUTE_PENDING_METHOD 60 CONSTRUCTOR_PARAM 61} 62 63class ExprModelExt { 64 val usedFieldNames = hashMapOf<Scope, MutableSet<String>>(); 65 { 66 Scope.values().forEach { usedFieldNames[it] = hashSetOf<String>() } 67 } 68 val localizedFlags = arrayListOf<FlagSet>() 69 70 fun localizeFlag(set : FlagSet, name:String) : FlagSet { 71 localizedFlags.add(set) 72 val result = getUniqueName(name, Scope.FLAG) 73 set.setLocalName(result) 74 return set 75 } 76 77 fun getUniqueName(base : String, scope : Scope) : String { 78 var candidate = base 79 var i = 0 80 while (usedFieldNames[scope].contains(candidate)) { 81 i ++ 82 candidate = base + i 83 } 84 usedFieldNames[scope].add(candidate) 85 return candidate 86 } 87} 88 89val ExprModel.ext by Delegates.lazy { target : ExprModel -> 90 ExprModelExt() 91} 92 93fun ExprModel.getUniqueFieldName(base : String) : String = ext.getUniqueName(base, Scope.FIELD) 94fun ExprModel.getUniqueMethodName(base : String) : String = ext.getUniqueName(base, Scope.METHOD) 95fun ExprModel.getUniqueFlagName(base : String) : String = ext.getUniqueName(base, Scope.FLAG) 96fun ExprModel.getConstructorParamName(base : String) : String = ext.getUniqueName(base, Scope.CONSTRUCTOR_PARAM) 97 98fun ExprModel.localizeFlag(set : FlagSet, base : String) : FlagSet = ext.localizeFlag(set, base) 99 100// not necessarily unique. Uniqueness is solved per scope 101val BindingTarget.readableName by Delegates.lazy { target: BindingTarget -> 102 if (target.getId() == null) { 103 "boundView" + indexFromTag(target.getTag()) 104 } else { 105 target.getId().androidId().stripNonJava() 106 } 107} 108 109fun BindingTarget.superConversion(variable : String) : String { 110 if (getResolvedType() != null && getResolvedType().extendsViewStub()) { 111 return "new android.databinding.ViewStubProxy((android.view.ViewStub) ${variable})" 112 } else { 113 return "(${interfaceType}) ${variable}" 114 } 115} 116 117val BindingTarget.fieldName : String by Delegates.lazy { target : BindingTarget -> 118 val name : String 119 if (target.getId() == null) { 120 name = "m${target.readableName}" 121 } else { 122 name = target.readableName 123 } 124 target.getModel().getUniqueFieldName(name) 125} 126 127val BindingTarget.androidId by Delegates.lazy { target : BindingTarget -> 128 "R.id.${target.getId().androidId()}" 129} 130 131val BindingTarget.interfaceType by Delegates.lazy { target : BindingTarget -> 132 if (target.getResolvedType() != null && target.getResolvedType().extendsViewStub()) { 133 "android.databinding.ViewStubProxy" 134 } else { 135 target.getInterfaceType() 136 } 137} 138 139val BindingTarget.constructorParamName by Delegates.lazy { target : BindingTarget -> 140 target.getModel().getConstructorParamName(target.readableName) 141} 142 143// not necessarily unique. Uniqueness is decided per scope 144val Expr.readableName by Delegates.lazy { expr : Expr -> 145 val stripped = "${expr.getUniqueKey().stripNonJava()}" 146 Log.d { "readableUniqueName for [${System.identityHashCode(expr)}] ${expr.getUniqueKey()} is $stripped" } 147 stripped 148} 149 150val Expr.fieldName by Delegates.lazy { expr : Expr -> 151 expr.getModel().getUniqueFieldName("m${expr.readableName.capitalize()}") 152} 153 154val Expr.executePendingLocalName by Delegates.lazy { expr : Expr -> 155 if(expr.isVariable()) expr.fieldName else "${expr.getModel().ext.getUniqueName(expr.readableName, Scope.EXECUTE_PENDING_METHOD)}" 156} 157 158val Expr.setterName by Delegates.lazy { expr : Expr -> 159 expr.getModel().getUniqueMethodName("set${expr.readableName.capitalize()}") 160} 161 162val Expr.onChangeName by Delegates.lazy { expr : Expr -> 163 expr.getModel().getUniqueMethodName("onChange${expr.readableName.capitalize()}") 164} 165 166val Expr.getterName by Delegates.lazy { expr : Expr -> 167 expr.getModel().getUniqueMethodName("get${expr.readableName.capitalize()}") 168} 169 170val Expr.dirtyFlagName by Delegates.lazy { expr : Expr -> 171 expr.getModel().getUniqueFlagName("sFlag${expr.readableName.capitalize()}") 172} 173 174 175fun Expr.toCode(full : Boolean = false) : KCode = CodeGenUtil.toCode(this, full) 176 177fun Expr.isVariable() = this is IdentifierExpr && this.isDynamic() 178 179fun Expr.conditionalFlagName(output : Boolean, suffix : String) = "${dirtyFlagName}_${output}$suffix" 180 181 182val Expr.dirtyFlagSet by Delegates.lazy { expr : Expr -> 183 FlagSet(expr.getInvalidFlags(), expr.getModel().getFlagBucketCount()) 184} 185 186val Expr.invalidateFlagSet by Delegates.lazy { expr : Expr -> 187 FlagSet(expr.getId()) 188} 189 190val Expr.shouldReadFlagSet by Delegates.lazy { expr : Expr -> 191 FlagSet(expr.getShouldReadFlags(), expr.getModel().getFlagBucketCount()) 192} 193 194val Expr.conditionalFlags by Delegates.lazy { expr : Expr -> 195 arrayListOf(FlagSet(expr.getRequirementFlagIndex(false)), 196 FlagSet(expr.getRequirementFlagIndex(true))) 197} 198 199fun Expr.getRequirementFlagSet(expected : Boolean) : FlagSet = conditionalFlags[if(expected) 1 else 0] 200 201fun FlagSet.notEmpty(cb : (suffix : String, value : Long) -> Unit) { 202 buckets.withIndex().forEach { 203 if (it.value != 0L) { 204 cb(getWordSuffix(it.index), buckets[it.index]) 205 } 206 } 207} 208 209fun FlagSet.getWordSuffix(wordIndex : Int) : String { 210 return if(wordIndex == 0) "" else "_${wordIndex}" 211} 212 213fun FlagSet.localValue(bucketIndex : Int) = 214 if (getLocalName() == null) binaryCode(bucketIndex) 215 else "${getLocalName()}${getWordSuffix(bucketIndex)}" 216 217fun FlagSet.binaryCode(bucketIndex : Int) = longToBinary(buckets[bucketIndex]) 218 219 220fun longToBinary(l : Long) = 221 "0b${java.lang.Long.toBinaryString(l)}L" 222 223fun <T> FlagSet.mapOr(other : FlagSet, cb : (suffix : String, index : Int) -> T) : List<T> { 224 val min = Math.min(buckets.size(), other.buckets.size()) 225 val result = arrayListOf<T>() 226 for (i in 0..(min - 1)) { 227 // if these two can match by any chance, call the callback 228 if (intersect(other, i)) { 229 result.add(cb(getWordSuffix(i), i)) 230 } 231 } 232 return result 233} 234 235fun indexFromTag(tag : String) : kotlin.Int { 236 val startIndex : kotlin.Int 237 if (tag.startsWith("binding_")) { 238 startIndex = "binding_".length(); 239 } else { 240 startIndex = tag.lastIndexOf('_') + 1 241 } 242 return Integer.parseInt(tag.substring(startIndex)) 243} 244 245class LayoutBinderWriter(val layoutBinder : LayoutBinder) { 246 val model = layoutBinder.getModel() 247 val indices = HashMap<BindingTarget, kotlin.Int>() 248 val mDirtyFlags by Delegates.lazy { 249 val fs = FlagSet(BitSet(), model.getFlagBucketCount()); 250 Arrays.fill(fs.buckets, -1) 251 fs.setDynamic(true) 252 model.localizeFlag(fs, "mDirtyFlags") 253 fs 254 } 255 256 val dynamics by Delegates.lazy { model.getExprMap().values().filter { it.isDynamic() } } 257 val className = layoutBinder.getImplementationName() 258 259 val baseClassName = "${layoutBinder.getClassName()}" 260 261 val includedBinders by Delegates.lazy { 262 layoutBinder.getBindingTargets().filter { it.isBinder() } 263 } 264 265 val variables by Delegates.lazy { 266 model.getExprMap().values().filterIsInstance(javaClass<IdentifierExpr>()).filter { it.isVariable() } 267 } 268 269 val usedVariables by Delegates.lazy { 270 variables.filter {it.isUsed()} 271 } 272 273 public fun write(minSdk : kotlin.Int) : String { 274 layoutBinder.resolveWhichExpressionsAreUsed() 275 calculateIndices(); 276 return kcode("package ${layoutBinder.getPackage()};") { 277 nl("import ${layoutBinder.getModulePackage()}.R;") 278 nl("import ${layoutBinder.getModulePackage()}.BR;") 279 nl("import android.view.View;") 280 val classDeclaration : String 281 if (layoutBinder.hasVariations()) { 282 classDeclaration = "${className} extends ${baseClassName}" 283 } else { 284 classDeclaration = "${className} extends android.databinding.ViewDataBinding" 285 } 286 nl("public class ${classDeclaration} {") { 287 tab(declareIncludeViews()) 288 tab(declareViews()) 289 tab(declareVariables()) 290 tab(declareConstructor(minSdk)) 291 tab(declareInvalidateAll()) 292 tab(declareLog()) 293 tab(declareSetVariable()) 294 tab(variableSettersAndGetters()) 295 tab(onFieldChange()) 296 297 tab(executePendingBindings()) 298 299 tab(declareDirtyFlags()) 300 if (!layoutBinder.hasVariations()) { 301 tab(declareFactories()) 302 } 303 } 304 nl("}") 305 tab(flagMapping()) 306 tab("//end") 307 }.generate() 308 } 309 fun calculateIndices() : Unit { 310 val taggedViews = layoutBinder.getBindingTargets().filter{ 311 it.isUsed() && it.getTag() != null && !it.isBinder() 312 } 313 taggedViews.forEach { 314 indices.put(it, indexFromTag(it.getTag())) 315 } 316 val indexStart = maxIndex() + 1 317 layoutBinder.getBindingTargets().filter{ 318 it.isUsed() && !taggedViews.contains(it) 319 }.withIndex().forEach { 320 indices.put(it.value, it.index + indexStart) 321 } 322 } 323 fun declareIncludeViews() = kcode("") { 324 nl("private static final android.databinding.ViewDataBinding.IncludedLayoutIndex[][] sIncludes;") 325 nl("private static final android.util.SparseIntArray sViewsWithIds;") 326 nl("static {") { 327 val hasBinders = layoutBinder.getBindingTargets().firstOrNull{ it.isUsed() && it.isBinder()} != null 328 if (!hasBinders) { 329 tab("sIncludes = null;") 330 } else { 331 val numBindings = layoutBinder.getBindingTargets().filter{ it.isUsed() }.count() 332 tab("sIncludes = new android.databinding.ViewDataBinding.IncludedLayoutIndex[${numBindings}][];") 333 val includeMap = HashMap<BindingTarget, ArrayList<BindingTarget>>() 334 layoutBinder.getBindingTargets().filter{ it.isUsed() && it.isBinder() }.forEach { 335 val includeTag = it.getTag(); 336 val parent = layoutBinder.getBindingTargets().firstOrNull { 337 it.isUsed() && !it.isBinder() && includeTag.equals(it.getTag()) 338 } 339 if (parent == null) { 340 throw IllegalStateException("Could not find parent of include file") 341 } 342 var list = includeMap.get(parent) 343 if (list == null) { 344 list = ArrayList<BindingTarget>() 345 includeMap.put(parent, list) 346 } 347 list.add(it) 348 } 349 350 includeMap.keySet().forEach { 351 val index = indices.get(it) 352 tab("sIncludes[${index}] = new android.databinding.ViewDataBinding.IncludedLayoutIndex[] {") { 353 includeMap.get(it).forEach { 354 val bindingIndex = indices.get(it) 355 val layoutName = it.getIncludedLayout() 356 tab("new android.databinding.ViewDataBinding.IncludedLayoutIndex(\"${layoutName}\", ${bindingIndex}, R.layout.${layoutName}),") 357 } 358 } 359 tab("};") 360 } 361 } 362 val viewsWithIds = layoutBinder.getBindingTargets().filter { 363 it.isUsed() && !it.isBinder() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null)) 364 } 365 if (viewsWithIds.isEmpty()) { 366 tab("sViewsWithIds = null;") 367 } else { 368 tab("sViewsWithIds = new android.util.SparseIntArray();") 369 viewsWithIds.forEach { 370 tab("sViewsWithIds.put(${it.androidId}, ${indices.get(it)});") 371 } 372 } 373 } 374 nl("}") 375 } 376 377 fun maxIndex() : kotlin.Int { 378 val maxIndex = indices.values().max() 379 if (maxIndex == null) { 380 return -1 381 } else { 382 return maxIndex 383 } 384 } 385 386 fun declareConstructor(minSdk : kotlin.Int) = kcode("") { 387 val bindingCount = maxIndex() + 1 388 val parameterType : String 389 val superParam : String 390 if (layoutBinder.isMerge()) { 391 parameterType = "View[]" 392 superParam = "root[0]" 393 } else { 394 parameterType = "View" 395 superParam = "root" 396 } 397 val rootTagsSupported = minSdk >= 14 398 if (layoutBinder.hasVariations()) { 399 nl("") 400 nl("public ${className}(${parameterType} root) {") { 401 tab("this(${superParam}, mapBindings(root, ${bindingCount}, sIncludes, sViewsWithIds));") 402 } 403 nl("}") 404 nl("private ${className}(${parameterType} root, Object[] bindings) {") { 405 tab("super(${superParam}, ${model.getObservables().size()}") { 406 layoutBinder.getSortedTargets().filter { it.getId() != null }.forEach { 407 tab(", ${fieldConversion(it)}") 408 } 409 tab(");") 410 } 411 } 412 } else { 413 nl("public ${baseClassName}(${parameterType} root) {") { 414 tab("super(${superParam}, ${model.getObservables().size()});") 415 tab("final Object[] bindings = mapBindings(root, ${bindingCount}, sIncludes, sViewsWithIds);") 416 } 417 } 418 tab("setRootTag(root);") 419 val taggedViews = layoutBinder.getSortedTargets().filter{it.isUsed()} 420 taggedViews.forEach { 421 if (!layoutBinder.hasVariations() || it.getId() == null) { 422 tab("this.${it.fieldName} = ${fieldConversion(it)};") 423 } 424 if (!it.isBinder()) { 425 if (it.getResolvedType() != null && it.getResolvedType().extendsViewStub()) { 426 tab("this.${it.fieldName}.setContainingBinding(this);") 427 } 428 if (it.supportsTag() && it.getTag() != null && 429 (rootTagsSupported || it.getTag().startsWith("binding_"))) { 430 val originalTag = it.getOriginalTag(); 431 var tagValue = "null" 432 if (originalTag != null) { 433 tagValue = "\"${originalTag}\"" 434 if (originalTag.startsWith("@")) { 435 var packageName = layoutBinder.getModulePackage() 436 if (originalTag.startsWith("@android:")) { 437 packageName = "android" 438 } 439 val slashIndex = originalTag.indexOf('/') 440 val resourceId = originalTag.substring(slashIndex + 1) 441 tagValue = "root.getResources().getString(${packageName}.R.string.${resourceId})" 442 } 443 } 444 tab("this.${it.fieldName}.setTag(${tagValue});") 445 } 446 } 447 } 448 tab("invalidateAll();"); 449 nl("}") 450 } 451 452 fun fieldConversion(target : BindingTarget) : String { 453 if (!target.isUsed()) { 454 return "null" 455 } else { 456 val index = indices.get(target) 457 if (index == null) { 458 throw IllegalStateException("Unknown binding target") 459 } 460 val variableName = "bindings[${index}]" 461 return target.superConversion(variableName) 462 } 463 } 464 465 fun declareInvalidateAll() = kcode("") { 466 nl("@Override") 467 nl("public void invalidateAll() {") { 468 val fs = FlagSet(layoutBinder.getModel().getInvalidateAnyBitSet(), 469 layoutBinder.getModel().getFlagBucketCount()); 470 tab("synchronized(this) {") { 471 for (i in (0..(mDirtyFlags.buckets.size() - 1))) { 472 tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};") 473 } 474 } tab("}") 475 includedBinders.filter{it.isUsed()}.forEach { binder -> 476 tab("${binder.fieldName}.invalidateAll();") 477 } 478 } 479 nl("}") 480 } 481 482 fun declareSetVariable() = kcode("") { 483 nl("public boolean setVariable(int variableId, Object variable) {") { 484 tab("switch(variableId) {") { 485 usedVariables.forEach { 486 tab ("case ${it.getName().br()} :") { 487 tab("${it.setterName}((${it.getResolvedType().toJavaCode()}) variable);") 488 tab("return true;") 489 } 490 } 491 } 492 tab("}") 493 tab("return false;") 494 } 495 nl("}") 496 } 497 498 fun declareLog() = kcode("") { 499 nl("private void log(String msg, long i) {") { 500 tab("""android.util.Log.d("BINDER", msg + ":" + Long.toHexString(i));""") 501 } 502 nl("}") 503 } 504 505 fun variableSettersAndGetters() = kcode("") { 506 variables.filterNot{it.isUsed()}.forEach { 507 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableName}) {") { 508 tab("// not used, ignore") 509 } 510 nl("}") 511 nl("") 512 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 513 tab("return ${it.getDefaultValue()};") 514 } 515 nl("}") 516 } 517 usedVariables.forEach { 518 if (it.getUserDefinedType() != null) { 519 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableName}) {") { 520 if (it.isObservable()) { 521 tab("updateRegistration(${it.getId()}, ${it.readableName});"); 522 } 523 tab("this.${it.fieldName} = ${it.readableName};") 524 // set dirty flags! 525 val flagSet = it.invalidateFlagSet 526 tab("synchronized(this) {") { 527 mDirtyFlags.mapOr(flagSet) { suffix, index -> 528 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 529 } 530 } tab ("}") 531 tab("super.requestRebind();") 532 } 533 nl("}") 534 nl("") 535 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 536 tab("return ${it.fieldName};") 537 } 538 nl("}") 539 } 540 } 541 } 542 543 fun onFieldChange() = kcode("") { 544 nl("@Override") 545 nl("protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {") { 546 tab("switch (localFieldId) {") { 547 model.getObservables().forEach { 548 tab("case ${it.getId()} :") { 549 tab("return ${it.onChangeName}((${it.getResolvedType().toJavaCode()}) object, fieldId);") 550 } 551 } 552 } 553 tab("}") 554 tab("return false;") 555 } 556 nl("}") 557 nl("") 558 559 model.getObservables().forEach { 560 nl("private boolean ${it.onChangeName}(${it.getResolvedType().toJavaCode()} ${it.readableName}, int fieldId) {") { 561 tab("switch (fieldId) {", { 562 val accessedFields: List<FieldAccessExpr> = it.getParents().filterIsInstance(javaClass<FieldAccessExpr>()) 563 accessedFields.filter { it.canBeInvalidated() } 564 .groupBy { it.getName() } 565 .forEach { 566 tab("case ${it.key.br()}:") { 567 val field = it.value.first() 568 tab("synchronized(this) {") { 569 mDirtyFlags.mapOr(field.invalidateFlagSet) { suffix, index -> 570 tab("${mDirtyFlags.localValue(index)} |= ${field.invalidateFlagSet.localValue(index)};") 571 } 572 } tab("}") 573 tab("return true;") 574 } 575 576 } 577 tab("case ${"".br()}:") { 578 val flagSet = it.invalidateFlagSet 579 tab("synchronized(this) {") { 580 mDirtyFlags.mapOr(flagSet) { suffix, index -> 581 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 582 } 583 } tab("}") 584 tab("return true;") 585 } 586 587 }) 588 tab("}") 589 tab("return false;") 590 } 591 nl("}") 592 nl("") 593 } 594 } 595 596 fun declareViews() = kcode("// views") { 597 val oneLayout = !layoutBinder.hasVariations(); 598 layoutBinder.getSortedTargets().filter {it.isUsed() && (oneLayout || it.getId() == null)}.forEach { 599 val access : String 600 if (oneLayout && it.getId() != null) { 601 access = "public" 602 } else { 603 access = "private" 604 } 605 nl("${access} final ${it.interfaceType} ${it.fieldName};") 606 } 607 } 608 609 fun declareVariables() = kcode("// variables") { 610 usedVariables.forEach { 611 nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};") 612 } 613 } 614 615 fun declareDirtyFlags() = kcode("// dirty flag") { 616 model.ext.localizedFlags.forEach { flag -> 617 flag.notEmpty { suffix, value -> 618 nl("private") 619 app(" ", if(flag.isDynamic()) null else "static final"); 620 app(" ", " ${flag.type} ${flag.getLocalName()}$suffix = ${longToBinary(value)};") 621 } 622 } 623 } 624 625 fun flagMapping() = kcode("/* flag mapping") { 626 if (model.getFlagMapping() != null) { 627 val mapping = model.getFlagMapping() 628 for (i in mapping.indices) { 629 tab("flag $i: ${mapping[i]}") 630 } 631 } 632 nl("flag mapping end*/") 633 } 634 635 fun executePendingBindings() = kcode("") { 636 nl("@Override") 637 nl("protected void executeBindings() {") { 638 val tmpDirtyFlags = FlagSet(mDirtyFlags.buckets) 639 tmpDirtyFlags.setLocalName("dirtyFlags"); 640 for (i in (0..mDirtyFlags.buckets.size() - 1)) { 641 tab("${tmpDirtyFlags.type} ${tmpDirtyFlags.localValue(i)} = 0;") 642 } 643 tab("synchronized(this) {") { 644 for (i in (0..mDirtyFlags.buckets.size() - 1)) { 645 tab("${tmpDirtyFlags.localValue(i)} = ${mDirtyFlags.localValue(i)};") 646 tab("${mDirtyFlags.localValue(i)} = 0;") 647 } 648 } tab("}") 649 model.getPendingExpressions().filterNot {!it.canBeEvaluatedToAVariable() || it.isVariable()}.forEach { 650 tab("${it.getResolvedType().toJavaCode()} ${it.executePendingLocalName} = ${it.getDefaultValue()};") 651 } 652 Log.d {"writing executePendingBindings for $className"} 653 do { 654 val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList() 655 Log.d {"batch: $batch"} 656 val mJustRead = arrayListOf<Expr>() 657 while (!batch.none()) { 658 val readNow = batch.filter { it.shouldReadNow(mJustRead) } 659 if (readNow.isEmpty()) { 660 throw IllegalStateException("do not know what I can read. bailing out ${batch.joinToString("\n")}") 661 } 662 Log.d { "new read now. batch size: ${batch.size()}, readNow size: ${readNow.size()}" } 663 664 readNow.forEach { 665 nl(readWithDependants(it, mJustRead, batch, tmpDirtyFlags)) 666 } 667 batch.removeAll(mJustRead) 668 } 669 tab("// batch finished") 670 } while(model.markBitsRead()) 671 672 // 673 layoutBinder.getSortedTargets().filter { it.isUsed() } 674 .flatMap { it.getBindings() } 675 .groupBy { it.getExpr() } 676 .forEach { 677 val flagSet = it.key.dirtyFlagSet 678 tab("if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 679 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 680 }.joinToString(" || ") 681 }) {") { 682 it.value.forEach { binding -> 683 tab("// api target ${binding.getMinApi()}") 684 val fieldName : String 685 if (binding.getTarget().getViewClass(). 686 equals(binding.getTarget().getInterfaceType())) { 687 fieldName = "this.${binding.getTarget().fieldName}" 688 } else { 689 fieldName = "((${binding.getTarget().getViewClass()}) this.${binding.getTarget().fieldName})" 690 } 691 val bindingCode = binding.toJavaCode(fieldName) 692 if (binding.getMinApi() > 1) { 693 tab("if(getBuildSdkInt() >= ${binding.getMinApi()}) {") { 694 tab("$bindingCode;") 695 } 696 tab("}") 697 } else { 698 tab("$bindingCode;") 699 } 700 } 701 } 702 tab("}") 703 } 704 includedBinders.filter{it.isUsed()}.forEach { binder -> 705 tab("${binder.fieldName}.executePendingBindings();") 706 } 707 layoutBinder.getSortedTargets().filter{ 708 it.isUsed() && it.getResolvedType() != null && it.getResolvedType().extendsViewStub() 709 }.forEach { 710 tab("if (${it.fieldName}.getBinding() != null) {") { 711 tab("${it.fieldName}.getBinding().executePendingBindings();") 712 } 713 tab("}") 714 } 715 } 716 nl("}") 717 } 718 719 fun readWithDependants(expr : Expr, mJustRead : MutableList<Expr>, batch : MutableList<Expr>, 720 tmpDirtyFlags : FlagSet, inheritedFlags : FlagSet? = null) : KCode = kcode("") { 721 mJustRead.add(expr) 722 Log.d { "$className / readWithDependants: ${expr.getUniqueKey()}" } 723 val flagSet = expr.shouldReadFlagSet 724 val needsIfWrapper = inheritedFlags == null || !flagSet.bitsEqual(inheritedFlags) 725 Log.d { "flag set:$flagSet . inherited flags: $inheritedFlags. need another if: $needsIfWrapper"} 726 val ifClause = "if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 727 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 728 }.joinToString(" || ") 729 })" 730 731 val readCode = kcode("") { 732 if (expr.canBeEvaluatedToAVariable() && !expr.isVariable()) { 733 // it is not a variable read it. 734 tab("// read ${expr.getUniqueKey()}") 735 // create an if case for all dependencies that might be null 736 val nullables = expr.getDependencies().filter { 737 it.isMandatory() && it.getOther().getResolvedType().isNullable() 738 }.map { it.getOther() } 739 if (!expr.isEqualityCheck() && nullables.isNotEmpty()) { 740 tab ("if ( ${nullables.map { "${it.executePendingLocalName} != null" }.joinToString(" && ")}) {") { 741 tab("${expr.executePendingLocalName}").app(" = ", expr.toCode(true)).app(";") 742 } 743 tab("}") 744 } else { 745 tab("${expr.executePendingLocalName}").app(" = ", expr.toCode(true)).app(";") 746 } 747 if (expr.isObservable()) { 748 tab("updateRegistration(${expr.getId()}, ${expr.executePendingLocalName});") 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.executePendingLocalName}) {") { 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 val dependant = it.getDependant() 778 batch.contains(dependant) && 779 dependant.shouldReadFlagSet.andNot(flagSet).isEmpty() && 780 dependant.shouldReadNow(mJustRead) 781 } 782 if (chosen.isNotEmpty()) { 783 val nextInheritedFlags = if (needsIfWrapper) flagSet else inheritedFlags 784 chosen.forEach { 785 nl(readWithDependants(it.getDependant(), mJustRead, batch, tmpDirtyFlags, nextInheritedFlags)) 786 } 787 } 788 } 789 if (needsIfWrapper) { 790 tab(ifClause) { 791 app(" {") 792 nl(readCode) 793 } 794 tab("}") 795 } else { 796 nl(readCode) 797 } 798 } 799 800 fun declareFactories() = kcode("") { 801 if (!layoutBinder.isMerge()) { 802 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") { 803 tab("return bind(inflater.inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, attachToRoot));") 804 } 805 nl("}") 806 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater) {") { 807 tab("return bind(inflater.inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false));") 808 } 809 nl("}") 810 nl("public static ${baseClassName} bind(android.view.View view) {") { 811 tab("if (!\"${layoutBinder.getTag()}_0\".equals(view.getTag())) {") { 812 tab("throw new RuntimeException(\"view tag isn't correct on view:\" + view.getTag());") 813 } 814 tab("}") 815 tab("return new ${baseClassName}(view);") 816 } 817 nl("}") 818 } 819 } 820 821 public fun writeBaseClass() : String = 822 kcode("package ${layoutBinder.getPackage()};") { 823 nl("import android.databinding.Bindable;") 824 nl("import android.databinding.DataBindingUtil;") 825 nl("import android.databinding.ViewDataBinding;") 826 nl("public abstract class ${baseClassName} extends ViewDataBinding {") 827 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 828 tab("public final ${it.interfaceType} ${it.fieldName};") 829 } 830 nl("") 831 tab("protected ${baseClassName}(android.view.View root_, int localFieldCount") { 832 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 833 tab(", ${it.interfaceType} ${it.constructorParamName}") 834 } 835 } 836 tab(") {") { 837 tab("super(root_, localFieldCount);") 838 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 839 tab("this.${it.fieldName} = ${it.constructorParamName};") 840 } 841 } 842 tab("}") 843 nl("") 844 variables.forEach { 845 if (it.getUserDefinedType() != null) { 846 //it.getExpandedUserDefinedType(ModelAnalyzer.getInstance()); 847 val type = ModelAnalyzer.getInstance().applyImports(it.getUserDefinedType(), model.getImports()) 848 tab("public abstract void ${it.setterName}(${type} ${it.readableName});") 849 } 850 } 851 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") { 852 tab("return DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, true);") 853 } 854 tab("}") 855 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater) {") { 856 tab("return DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false);") 857 } 858 tab("}") 859 tab("public static ${baseClassName} bind(android.view.View view) {") { 860 tab("return (${baseClassName})bind(view, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()});") 861 } 862 tab("}") 863 nl("}") 864 }.generate() 865} 866