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