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.Binding 17import android.databinding.tool.BindingTarget 18import android.databinding.tool.CallbackWrapper 19import android.databinding.tool.InverseBinding 20import android.databinding.tool.LayoutBinder 21import android.databinding.tool.expr.Expr 22import android.databinding.tool.expr.ExprModel 23import android.databinding.tool.expr.FieldAccessExpr 24import android.databinding.tool.expr.IdentifierExpr 25import android.databinding.tool.expr.LambdaExpr 26import android.databinding.tool.expr.ListenerExpr 27import android.databinding.tool.expr.ResourceExpr 28import android.databinding.tool.expr.TernaryExpr 29import android.databinding.tool.expr.localizeGlobalVariables 30import android.databinding.tool.expr.shouldLocalizeInCallbacks 31import android.databinding.tool.expr.toCode 32import android.databinding.tool.ext.androidId 33import android.databinding.tool.ext.br 34import android.databinding.tool.ext.joinToCamelCaseAsVar 35import android.databinding.tool.ext.lazyProp 36import android.databinding.tool.ext.versionedLazy 37import android.databinding.tool.processing.ErrorMessages 38import android.databinding.tool.reflection.ModelAnalyzer 39import android.databinding.tool.reflection.ModelClass 40import android.databinding.tool.util.L 41import android.databinding.tool.util.Preconditions 42import java.util.ArrayList 43import java.util.Arrays 44import java.util.BitSet 45import java.util.HashMap 46 47fun String.stripNonJava() = this.split("[^a-zA-Z0-9]".toRegex()).map{ it.trim() }.joinToCamelCaseAsVar() 48 49enum class Scope { 50 GLOBAL, 51 FIELD, 52 METHOD, 53 FLAG, 54 EXECUTE_PENDING_METHOD, 55 CONSTRUCTOR_PARAM, 56 CALLBACK; 57 companion object { 58 var currentScope = GLOBAL; 59 private val scopeStack = arrayListOf<Scope>() 60 fun enter(scope : Scope) { 61 scopeStack.add(currentScope) 62 currentScope = scope 63 } 64 65 fun exit() { 66 currentScope = scopeStack.removeAt(scopeStack.size - 1) 67 } 68 69 fun reset() { 70 scopeStack.clear() 71 currentScope = GLOBAL 72 } 73 } 74} 75 76class ExprModelExt { 77 val usedFieldNames = hashMapOf<Scope, MutableSet<String>>(); 78 init { 79 Scope.values().forEach { usedFieldNames[it] = hashSetOf<String>() } 80 } 81 82 internal val forceLocalize = hashSetOf<Expr>() 83 84 val localizedFlags = arrayListOf<FlagSet>() 85 86 fun localizeFlag(set : FlagSet, name:String) : FlagSet { 87 localizedFlags.add(set) 88 val result = getUniqueName(name, Scope.FLAG, false) 89 set.localName = result 90 return set 91 } 92 93 fun getUniqueName(base : String, scope : Scope, isPublic : kotlin.Boolean) : String { 94 var candidateBase = base 95 if (!isPublic && candidateBase.length > 20) { 96 candidateBase = candidateBase.substring(0, 20); 97 } 98 var candidate = candidateBase 99 if (scope == Scope.CALLBACK || scope == Scope.EXECUTE_PENDING_METHOD) { 100 candidate = candidate.decapitalize() 101 } 102 var i = 0 103 while (usedFieldNames[scope]!!.contains(candidate)) { 104 i ++ 105 candidate = candidateBase + i 106 } 107 usedFieldNames[scope]!!.add(candidate) 108 return candidate 109 } 110} 111 112fun ModelClass.defaultValue() = ModelAnalyzer.getInstance().getDefaultValue(toJavaCode()) 113fun ExprModel.getUniqueFieldName(base : String, isPublic : kotlin.Boolean) : String = ext.getUniqueName(base, Scope.FIELD, isPublic) 114fun ExprModel.getUniqueMethodName(base : String, isPublic : kotlin.Boolean) : String = ext.getUniqueName(base, Scope.METHOD, isPublic) 115fun ExprModel.getConstructorParamName(base : String) : String = ext.getUniqueName(base, Scope.CONSTRUCTOR_PARAM, false) 116fun ExprModel.localizeFlag(set : FlagSet, base : String) : FlagSet = ext.localizeFlag(set, base) 117 118val Expr.needsLocalField by lazyProp { expr : Expr -> 119 expr.canBeEvaluatedToAVariable() && !(expr.isVariable() && !expr.isUsed) && (expr.isDynamic || expr is ResourceExpr) 120} 121 122fun Expr.isForcedToLocalize() = model.ext.forceLocalize.contains(this) 123 124// not necessarily unique. Uniqueness is solved per scope 125val BindingTarget.readableName by lazyProp { target: BindingTarget -> 126 if (target.id == null) { 127 "boundView" + indexFromTag(target.tag) 128 } else { 129 target.id.androidId().stripNonJava() 130 } 131} 132 133fun BindingTarget.superConversion(variable : String) : String { 134 if (resolvedType != null && resolvedType.extendsViewStub()) { 135 return "new android.databinding.ViewStubProxy((android.view.ViewStub) $variable)" 136 } else { 137 return "($interfaceClass) $variable" 138 } 139} 140 141val BindingTarget.fieldName : String by lazyProp { target : BindingTarget -> 142 val name : String 143 val isPublic : kotlin.Boolean 144 if (target.id == null) { 145 name = "m${target.readableName}" 146 isPublic = false 147 } else { 148 name = target.readableName 149 isPublic = true 150 } 151 target.model.getUniqueFieldName(name, isPublic) 152} 153 154val BindingTarget.androidId by lazyProp { target : BindingTarget -> 155 if (target.id.startsWith("@android:id/")) { 156 "android.R.id.${target.id.androidId()}" 157 } else { 158 "R.id.${target.id.androidId()}" 159 } 160} 161 162val BindingTarget.interfaceClass by lazyProp { target : BindingTarget -> 163 if (target.resolvedType != null && target.resolvedType.extendsViewStub()) { 164 "android.databinding.ViewStubProxy" 165 } else { 166 target.interfaceType 167 } 168} 169 170val BindingTarget.constructorParamName by lazyProp { target : BindingTarget -> 171 target.model.getConstructorParamName(target.readableName) 172} 173 174// not necessarily unique. Uniqueness is decided per scope 175val Expr.readableName by lazyProp { expr : Expr -> 176 val stripped = expr.uniqueKey.stripNonJava() 177 L.d("readableUniqueName for [%s] %s is %s", System.identityHashCode(expr), expr.uniqueKey, stripped) 178 stripped 179} 180 181val Expr.fieldName by lazyProp { expr : Expr -> 182 expr.model.getUniqueFieldName("m${expr.readableName.capitalize()}", false) 183} 184 185val InverseBinding.fieldName by lazyProp { inverseBinding : InverseBinding -> 186 val targetName = inverseBinding.target.fieldName; 187 val eventName = inverseBinding.eventAttribute.stripNonJava() 188 inverseBinding.model.getUniqueFieldName("$targetName$eventName", false) 189} 190 191val Expr.listenerClassName by lazyProp { expr : Expr -> 192 expr.model.getUniqueFieldName("${expr.resolvedType.simpleName}Impl", false) 193} 194 195val Expr.oldValueName by lazyProp { expr : Expr -> 196 expr.model.getUniqueFieldName("mOld${expr.readableName.capitalize()}", false) 197} 198 199fun Expr.scopedName() : String = when(Scope.currentScope) { 200 Scope.CALLBACK -> callbackLocalName 201 else -> executePendingLocalName 202} 203 204val Expr.callbackLocalName by lazyProp { expr : Expr -> 205 if(expr.shouldLocalizeInCallbacks()) "${expr.model.ext.getUniqueName(expr.readableName, Scope.CALLBACK, false)}" 206 else expr.toCode().generate() 207} 208 209val Expr.executePendingLocalName by lazyProp { expr : Expr -> 210 if(expr.isDynamic || expr.needsLocalField) "${expr.model.ext.getUniqueName(expr.readableName, Scope.EXECUTE_PENDING_METHOD, false)}" 211 else expr.toCode().generate() 212} 213 214val Expr.setterName by lazyProp { expr : Expr -> 215 expr.model.getUniqueMethodName("set${expr.readableName.capitalize()}", true) 216} 217 218val Expr.onChangeName by lazyProp { expr : Expr -> 219 expr.model.getUniqueMethodName("onChange${expr.readableName.capitalize()}", false) 220} 221 222val Expr.getterName by lazyProp { expr : Expr -> 223 expr.model.getUniqueMethodName("get${expr.readableName.capitalize()}", true) 224} 225 226fun Expr.isVariable() = this is IdentifierExpr && this.isDynamic 227 228val Expr.dirtyFlagSet by lazyProp { expr : Expr -> 229 FlagSet(expr.invalidFlags, expr.model.flagBucketCount) 230} 231 232val Expr.invalidateFlagSet by lazyProp { expr : Expr -> 233 FlagSet(expr.id) 234} 235 236val Expr.shouldReadFlagSet by versionedLazy { expr : Expr -> 237 FlagSet(expr.shouldReadFlags, expr.model.flagBucketCount) 238} 239 240val Expr.shouldReadWithConditionalsFlagSet by versionedLazy { expr : Expr -> 241 FlagSet(expr.shouldReadFlagsWithConditionals, expr.model.flagBucketCount) 242} 243 244val Expr.conditionalFlags by lazyProp { expr : Expr -> 245 arrayListOf(FlagSet(expr.getRequirementFlagIndex(false)), 246 FlagSet(expr.getRequirementFlagIndex(true))) 247} 248 249fun Binding.toAssignmentCode() : String { 250 val fieldName: String 251 if (this.target.viewClass. 252 equals(this.target.interfaceType)) { 253 fieldName = "this.${this.target.fieldName}" 254 } else { 255 fieldName = "((${this.target.viewClass}) this.${this.target.fieldName})" 256 } 257 return this.toJavaCode(fieldName, "this.mBindingComponent") 258} 259 260val LayoutBinder.requiredComponent by lazyProp { layoutBinder: LayoutBinder -> 261 val requiredFromBindings = layoutBinder. 262 bindingTargets. 263 flatMap { it.bindings }. 264 firstOrNull { it.bindingAdapterInstanceClass != null }?.bindingAdapterInstanceClass 265 val requiredFromInverse = layoutBinder. 266 bindingTargets. 267 flatMap { it.inverseBindings }. 268 firstOrNull { it.bindingAdapterInstanceClass != null }?.bindingAdapterInstanceClass 269 requiredFromBindings ?: requiredFromInverse 270} 271 272fun Expr.getRequirementFlagSet(expected : Boolean) : FlagSet = conditionalFlags[if(expected) 1 else 0] 273 274fun FlagSet.notEmpty(cb : (suffix : String, value : Long) -> Unit) { 275 buckets.withIndex().forEach { 276 if (it.value != 0L) { 277 cb(getWordSuffix(it.index), buckets[it.index]) 278 } 279 } 280} 281 282fun getWordSuffix(wordIndex : Int) : String { 283 return if(wordIndex == 0) "" else "_$wordIndex" 284} 285 286fun FlagSet.localValue(bucketIndex : Int) = 287 if (localName == null) binaryCode(bucketIndex) 288 else "$localName${getWordSuffix(bucketIndex)}" 289 290fun FlagSet.binaryCode(bucketIndex : Int) = longToBinary(buckets[bucketIndex]) 291 292 293fun longToBinary(l : Long) = "0x${java.lang.Long.toHexString(l)}L" 294 295fun <T> FlagSet.mapOr(other : FlagSet, cb : (suffix : String, index : Int) -> T) : List<T> { 296 val min = Math.min(buckets.size, other.buckets.size) 297 val result = arrayListOf<T>() 298 for (i in 0..(min - 1)) { 299 // if these two can match by any chance, call the callback 300 if (intersect(other, i)) { 301 result.add(cb(getWordSuffix(i), i)) 302 } 303 } 304 return result 305} 306 307fun indexFromTag(tag : String) : kotlin.Int { 308 val startIndex : kotlin.Int 309 if (tag.startsWith("binding_")) { 310 startIndex = "binding_".length; 311 } else { 312 startIndex = tag.lastIndexOf('_') + 1 313 } 314 return Integer.parseInt(tag.substring(startIndex)) 315} 316 317class LayoutBinderWriter(val layoutBinder : LayoutBinder) { 318 val model = layoutBinder.model 319 val indices = HashMap<BindingTarget, kotlin.Int>() 320 val mDirtyFlags by lazy { 321 val fs = FlagSet(BitSet(), model.flagBucketCount); 322 Arrays.fill(fs.buckets, -1) 323 fs.isDynamic = true 324 model.localizeFlag(fs, "mDirtyFlags") 325 fs 326 } 327 328 val className = layoutBinder.implementationName 329 330 val baseClassName = "${layoutBinder.className}" 331 332 val includedBinders by lazy { 333 layoutBinder.bindingTargets.filter { it.isBinder } 334 } 335 336 val variables by lazy { 337 model.exprMap.values.filterIsInstance(IdentifierExpr::class.java).filter { it.isVariable() } 338 } 339 340 val usedVariables by lazy { 341 variables.filter {it.isUsed || it.isIsUsedInCallback } 342 } 343 344 val callbacks by lazy { 345 model.exprMap.values.filterIsInstance(LambdaExpr::class.java) 346 } 347 348 public fun write(minSdk : kotlin.Int) : String { 349 Scope.reset() 350 layoutBinder.resolveWhichExpressionsAreUsed() 351 calculateIndices(); 352 return kcode("package ${layoutBinder.`package`};") { 353 nl("import ${layoutBinder.modulePackage}.R;") 354 nl("import ${layoutBinder.modulePackage}.BR;") 355 nl("import android.view.View;") 356 val classDeclaration : String 357 if (layoutBinder.hasVariations()) { 358 classDeclaration = "$className extends $baseClassName" 359 } else { 360 classDeclaration = "$className extends android.databinding.ViewDataBinding" 361 } 362 block("public class $classDeclaration ${buildImplements()}") { 363 nl(declareIncludeViews()) 364 nl(declareViews()) 365 nl(declareVariables()) 366 nl(declareBoundValues()) 367 nl(declareListeners()) 368 try { 369 Scope.enter(Scope.GLOBAL) 370 nl(declareInverseBindingImpls()); 371 } finally { 372 Scope.exit() 373 } 374 nl(declareConstructor(minSdk)) 375 nl(declareInvalidateAll()) 376 nl(declareHasPendingBindings()) 377 nl(declareSetVariable()) 378 nl(variableSettersAndGetters()) 379 nl(onFieldChange()) 380 try { 381 Scope.enter(Scope.GLOBAL) 382 nl(executePendingBindings()) 383 } finally { 384 Scope.exit() 385 } 386 387 nl(declareListenerImpls()) 388 try { 389 Scope.enter(Scope.CALLBACK) 390 nl(declareCallbackImplementations()) 391 } finally { 392 Scope.exit() 393 } 394 395 nl(declareDirtyFlags()) 396 if (!layoutBinder.hasVariations()) { 397 nl(declareFactories()) 398 } 399 nl(flagMapping()) 400 nl("//end") 401 } 402 }.generate() 403 } 404 fun buildImplements() : String { 405 return if (callbacks.isEmpty()) { 406 "" 407 } else { 408 "implements " + callbacks.map { it.callbackWrapper.cannonicalListenerName }.distinct().joinToString(", ") 409 } 410 } 411 412 fun calculateIndices() : Unit { 413 val taggedViews = layoutBinder.bindingTargets.filter{ 414 it.isUsed && it.tag != null && !it.isBinder 415 } 416 taggedViews.forEach { 417 indices.put(it, indexFromTag(it.tag)) 418 } 419 val indexStart = maxIndex() + 1 420 layoutBinder.bindingTargets.filter{ 421 it.isUsed && !taggedViews.contains(it) 422 }.withIndex().forEach { 423 indices.put(it.value, it.index + indexStart) 424 } 425 } 426 fun declareIncludeViews() = kcode("") { 427 nl("private static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes;") 428 nl("private static final android.util.SparseIntArray sViewsWithIds;") 429 nl("static {") { 430 val hasBinders = layoutBinder.bindingTargets.firstOrNull{ it.isUsed && it.isBinder } != null 431 if (!hasBinders) { 432 tab("sIncludes = null;") 433 } else { 434 val numBindings = layoutBinder.bindingTargets.filter{ it.isUsed }.count() 435 tab("sIncludes = new android.databinding.ViewDataBinding.IncludedLayouts($numBindings);") 436 val includeMap = HashMap<BindingTarget, ArrayList<BindingTarget>>() 437 layoutBinder.bindingTargets.filter{ it.isUsed && it.isBinder }.forEach { 438 val includeTag = it.tag; 439 val parent = layoutBinder.bindingTargets.firstOrNull { 440 it.isUsed && !it.isBinder && includeTag.equals(it.tag) 441 } ?: throw IllegalStateException("Could not find parent of include file") 442 var list = includeMap[parent] 443 if (list == null) { 444 list = ArrayList<BindingTarget>() 445 includeMap.put(parent, list) 446 } 447 list.add(it) 448 } 449 450 includeMap.keys.forEach { 451 val index = indices[it] 452 tab("sIncludes.setIncludes($index, ") { 453 tab ("new String[] {${ 454 includeMap[it]!!.map { 455 "\"${it.includedLayout}\"" 456 }.joinToString(", ") 457 }},") 458 tab("new int[] {${ 459 includeMap[it]!!.map { 460 "${indices[it]}" 461 }.joinToString(", ") 462 }},") 463 tab("new int[] {${ 464 includeMap[it]!!.map { 465 "R.layout.${it.includedLayout}" 466 }.joinToString(", ") 467 }});") 468 } 469 } 470 } 471 val viewsWithIds = layoutBinder.bindingTargets.filter { 472 it.isUsed && !it.isBinder && (!it.supportsTag() || (it.id != null && it.tag == null)) 473 } 474 if (viewsWithIds.isEmpty()) { 475 tab("sViewsWithIds = null;") 476 } else { 477 tab("sViewsWithIds = new android.util.SparseIntArray();") 478 viewsWithIds.forEach { 479 tab("sViewsWithIds.put(${it.androidId}, ${indices[it]});") 480 } 481 } 482 } 483 nl("}") 484 } 485 486 fun maxIndex() : kotlin.Int { 487 val maxIndex = indices.values.max() 488 if (maxIndex == null) { 489 return -1 490 } else { 491 return maxIndex 492 } 493 } 494 495 fun declareConstructor(minSdk : kotlin.Int) = kcode("") { 496 val bindingCount = maxIndex() + 1 497 val parameterType : String 498 val superParam : String 499 if (layoutBinder.isMerge) { 500 parameterType = "View[]" 501 superParam = "root[0]" 502 } else { 503 parameterType = "View" 504 superParam = "root" 505 } 506 val rootTagsSupported = minSdk >= 14 507 if (layoutBinder.hasVariations()) { 508 nl("") 509 nl("public $className(android.databinding.DataBindingComponent bindingComponent, $parameterType root) {") { 510 tab("this(bindingComponent, $superParam, mapBindings(bindingComponent, root, $bindingCount, sIncludes, sViewsWithIds));") 511 } 512 nl("}") 513 nl("private $className(android.databinding.DataBindingComponent bindingComponent, $parameterType root, Object[] bindings) {") { 514 tab("super(bindingComponent, $superParam, ${model.observables.size}") { 515 layoutBinder.sortedTargets.filter { it.id != null }.forEach { 516 tab(", ${fieldConversion(it)}") 517 } 518 tab(");") 519 } 520 } 521 } else { 522 nl("public $baseClassName(android.databinding.DataBindingComponent bindingComponent, $parameterType root) {") { 523 tab("super(bindingComponent, $superParam, ${model.observables.size});") 524 tab("final Object[] bindings = mapBindings(bindingComponent, root, $bindingCount, sIncludes, sViewsWithIds);") 525 } 526 } 527 if (layoutBinder.requiredComponent != null) { 528 tab("ensureBindingComponentIsNotNull(${layoutBinder.requiredComponent}.class);") 529 } 530 val taggedViews = layoutBinder.sortedTargets.filter{it.isUsed } 531 taggedViews.forEach { 532 if (!layoutBinder.hasVariations() || it.id == null) { 533 tab("this.${it.fieldName} = ${fieldConversion(it)};") 534 } 535 if (!it.isBinder) { 536 if (it.resolvedType != null && it.resolvedType.extendsViewStub()) { 537 tab("this.${it.fieldName}.setContainingBinding(this);") 538 } 539 if (it.supportsTag() && it.tag != null && 540 (rootTagsSupported || it.tag.startsWith("binding_"))) { 541 val originalTag = it.originalTag; 542 var tagValue = "null" 543 if (originalTag != null && !originalTag.startsWith("@{")) { 544 tagValue = "\"$originalTag\"" 545 if (originalTag.startsWith("@")) { 546 var packageName = layoutBinder.modulePackage 547 if (originalTag.startsWith("@android:")) { 548 packageName = "android" 549 } 550 val slashIndex = originalTag.indexOf('/') 551 val resourceId = originalTag.substring(slashIndex + 1) 552 tagValue = "root.getResources().getString($packageName.R.string.$resourceId)" 553 } 554 } 555 tab("this.${it.fieldName}.setTag($tagValue);") 556 } else if (it.tag != null && !it.tag.startsWith("binding_") && 557 it.originalTag != null) { 558 L.e(ErrorMessages.ROOT_TAG_NOT_SUPPORTED, it.originalTag) 559 } 560 } 561 } 562 tab("setRootTag(root);") 563 tab(declareCallbackInstances()) 564 tab("invalidateAll();"); 565 nl("}") 566 } 567 568 fun declareCallbackInstances() = kcode("// listeners") { 569 callbacks.groupBy { it.callbackWrapper.minApi } 570 .forEach { 571 if (it.key > 1) { 572 block("if(getBuildSdkInt() < ${it.key})") { 573 it.value.forEach { lambda -> 574 nl("${lambda.fieldName} = null;") 575 } 576 } 577 block("else") { 578 it.value.forEach { lambda -> 579 nl("${lambda.fieldName} = ${lambda.generateConstructor()};") 580 } 581 } 582 } else { 583 it.value.forEach { lambda -> 584 nl("${lambda.fieldName} = ${lambda.generateConstructor()};") 585 } 586 } 587 } 588 } 589 590 fun declareCallbackImplementations() = kcode("// callback impls") { 591 callbacks.groupBy { it.callbackWrapper }.forEach { 592 val wrapper = it.key 593 val lambdas = it.value 594 val shouldReturn = !wrapper.method.returnType.isVoid 595 if (shouldReturn) { 596 lambdas.forEach { 597 it.callbackExprModel.ext.forceLocalize.add(it.expr) 598 } 599 } 600 block("public final ${wrapper.method.returnType.canonicalName} ${wrapper.listenerMethodName}(${wrapper.allArgsWithTypes()})") { 601 Preconditions.check(lambdas.size > 0, "bindings list should not be empty") 602 if (lambdas.size == 1) { 603 val lambda = lambdas[0] 604 nl(lambda.callbackExprModel.localizeGlobalVariables(lambda)) 605 nl(lambda.executionPath.toCode()) 606 if (shouldReturn) { 607 nl("return ${lambda.expr.scopedName()};") 608 } 609 } else { 610 block("switch(${CallbackWrapper.SOURCE_ID})") { 611 lambdas.forEach { lambda -> 612 block("case ${lambda.callbackId}:") { 613 nl(lambda.callbackExprModel.localizeGlobalVariables(lambda)) 614 nl(lambda.executionPath.toCode()) 615 if (shouldReturn) { 616 nl("return ${lambda.expr.scopedName()};") 617 } else { 618 nl("break;") 619 } 620 } 621 } 622 if (shouldReturn) { 623 block("default:") { 624 nl("return ${wrapper.method.returnType.defaultValue()};") 625 } 626 } 627 } 628 } 629 } 630 } 631 } 632 633 fun fieldConversion(target : BindingTarget) : String { 634 if (!target.isUsed) { 635 return "null" 636 } else { 637 val index = indices[target] ?: throw IllegalStateException("Unknown binding target") 638 val variableName = "bindings[$index]" 639 return target.superConversion(variableName) 640 } 641 } 642 643 fun declareInvalidateAll() = kcode("") { 644 nl("@Override") 645 block("public void invalidateAll()") { 646 val fs = FlagSet(layoutBinder.model.invalidateAnyBitSet, 647 layoutBinder.model.flagBucketCount); 648 block("synchronized(this)") { 649 for (i in (0..(mDirtyFlags.buckets.size - 1))) { 650 tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};") 651 } 652 } 653 includedBinders.filter{it.isUsed }.forEach { binder -> 654 nl("${binder.fieldName}.invalidateAll();") 655 } 656 nl("requestRebind();"); 657 } 658 } 659 660 fun declareHasPendingBindings() = kcode("") { 661 nl("@Override") 662 nl("public boolean hasPendingBindings() {") { 663 if (mDirtyFlags.buckets.size > 0) { 664 tab("synchronized(this) {") { 665 val flagCheck = 0.rangeTo(mDirtyFlags.buckets.size - 1).map { 666 "${mDirtyFlags.localValue(it)} != 0" 667 }.joinToString(" || ") 668 tab("if ($flagCheck) {") { 669 tab("return true;") 670 } 671 tab("}") 672 } 673 tab("}") 674 } 675 includedBinders.filter{it.isUsed }.forEach { binder -> 676 tab("if (${binder.fieldName}.hasPendingBindings()) {") { 677 tab("return true;") 678 } 679 tab("}") 680 } 681 tab("return false;") 682 } 683 nl("}") 684 } 685 686 fun declareSetVariable() = kcode("") { 687 nl("public boolean setVariable(int variableId, Object variable) {") { 688 tab("switch(variableId) {") { 689 usedVariables.forEach { 690 tab ("case ${it.name.br()} :") { 691 tab("${it.setterName}((${it.resolvedType.toJavaCode()}) variable);") 692 tab("return true;") 693 } 694 } 695 val declaredOnly = variables.filter { !it.isUsed && !it.isIsUsedInCallback && it.isDeclared }; 696 declaredOnly.forEachIndexed { i, identifierExpr -> 697 tab ("case ${identifierExpr.name.br()} :") { 698 if (i == declaredOnly.size - 1) { 699 tab("return true;") 700 } 701 } 702 } 703 } 704 tab("}") 705 tab("return false;") 706 } 707 nl("}") 708 } 709 710 fun variableSettersAndGetters() = kcode("") { 711 variables.filterNot{ usedVariables.contains(it) }.forEach { 712 nl("public void ${it.setterName}(${it.resolvedType.toJavaCode()} ${it.readableName}) {") { 713 tab("// not used, ignore") 714 } 715 nl("}") 716 nl("") 717 nl("public ${it.resolvedType.toJavaCode()} ${it.getterName}() {") { 718 tab("return ${it.defaultValue};") 719 } 720 nl("}") 721 } 722 usedVariables.forEach { 723 if (it.userDefinedType != null) { 724 block("public void ${it.setterName}(${it.resolvedType.toJavaCode()} ${it.readableName})") { 725 if (it.isObservable) { 726 nl("updateRegistration(${it.id}, ${it.readableName});"); 727 } 728 nl("this.${it.fieldName} = ${it.readableName};") 729 // set dirty flags! 730 val flagSet = it.invalidateFlagSet 731 block("synchronized(this)") { 732 mDirtyFlags.mapOr(flagSet) { suffix, index -> 733 nl("${mDirtyFlags.localName}$suffix |= ${flagSet.localValue(index)};") 734 } 735 } 736 // TODO: Remove this condition after releasing version 1.1 of SDK 737 if (ModelAnalyzer.getInstance().findClass("android.databinding.ViewDataBinding", null).isObservable) { 738 nl("notifyPropertyChanged(${it.name.br()});") 739 } 740 nl("super.requestRebind();") 741 } 742 nl("") 743 block("public ${it.resolvedType.toJavaCode()} ${it.getterName}()") { 744 nl("return ${it.fieldName};") 745 } 746 } 747 } 748 } 749 750 fun onFieldChange() = kcode("") { 751 nl("@Override") 752 nl("protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {") { 753 tab("switch (localFieldId) {") { 754 model.observables.forEach { 755 tab("case ${it.id} :") { 756 tab("return ${it.onChangeName}((${it.resolvedType.toJavaCode()}) object, fieldId);") 757 } 758 } 759 } 760 tab("}") 761 tab("return false;") 762 } 763 nl("}") 764 nl("") 765 766 model.observables.forEach { 767 block("private boolean ${it.onChangeName}(${it.resolvedType.toJavaCode()} ${it.readableName}, int fieldId)") { 768 block("switch (fieldId)", { 769 val accessedFields: List<FieldAccessExpr> = it.parents.filterIsInstance(FieldAccessExpr::class.java) 770 accessedFields.filter { it.isUsed && it.hasBindableAnnotations() } 771 .groupBy { it.brName } 772 .forEach { 773 // If two expressions look different but resolve to the same method, 774 // we are not yet able to merge them. This is why we merge their 775 // flags below. 776 block("case ${it.key}:") { 777 block("synchronized(this)") { 778 val flagSet = it.value.foldRight(FlagSet()) { l, r -> l.invalidateFlagSet.or(r) } 779 780 mDirtyFlags.mapOr(flagSet) { suffix, index -> 781 tab("${mDirtyFlags.localValue(index)} |= ${flagSet.localValue(index)};") 782 } 783 } 784 nl("return true;") 785 } 786 787 } 788 block("case ${"".br()}:") { 789 val flagSet = it.invalidateFlagSet 790 block("synchronized(this)") { 791 mDirtyFlags.mapOr(flagSet) { suffix, index -> 792 tab("${mDirtyFlags.localName}$suffix |= ${flagSet.localValue(index)};") 793 } 794 } 795 nl("return true;") 796 } 797 }) 798 nl("return false;") 799 } 800 nl("") 801 } 802 } 803 804 fun declareViews() = kcode("// views") { 805 val oneLayout = !layoutBinder.hasVariations(); 806 layoutBinder.sortedTargets.filter {it.isUsed && (oneLayout || it.id == null)}.forEach { 807 val access : String 808 if (oneLayout && it.id != null) { 809 access = "public" 810 } else { 811 access = "private" 812 } 813 nl("$access final ${it.interfaceClass} ${it.fieldName};") 814 } 815 } 816 817 fun declareVariables() = kcode("// variables") { 818 usedVariables.forEach { 819 nl("private ${it.resolvedType.toJavaCode()} ${it.fieldName};") 820 } 821 callbacks.forEach { 822 val wrapper = it.callbackWrapper 823 nl("private final ${wrapper.klass.canonicalName} ${it.fieldName}").app(";") 824 } 825 } 826 827 fun declareBoundValues() = kcode("// values") { 828 layoutBinder.sortedTargets.filter { it.isUsed } 829 .flatMap { it.bindings } 830 .filter { it.requiresOldValue() } 831 .flatMap{ it.componentExpressions.toList() } 832 .groupBy { it } 833 .forEach { 834 val expr = it.key 835 nl("private ${expr.resolvedType.toJavaCode()} ${expr.oldValueName};") 836 } 837 } 838 839 fun declareListeners() = kcode("// listeners") { 840 model.exprMap.values.filter { 841 it is ListenerExpr 842 }.groupBy { it }.forEach { 843 val expr = it.key as ListenerExpr 844 nl("private ${expr.listenerClassName} ${expr.fieldName};") 845 } 846 } 847 848 fun declareInverseBindingImpls() = kcode("// Inverse Binding Event Handlers") { 849 layoutBinder.sortedTargets.filter { it.isUsed }.forEach { target -> 850 target.inverseBindings.forEach { inverseBinding -> 851 val className : String 852 val param : String 853 if (inverseBinding.isOnBinder) { 854 className = "android.databinding.ViewDataBinding.PropertyChangedInverseListener" 855 param = "BR.${inverseBinding.eventAttribute}" 856 } else { 857 className = "android.databinding.InverseBindingListener" 858 param = "" 859 } 860 block("private $className ${inverseBinding.fieldName} = new $className($param)") { 861 nl("@Override") 862 block("public void onChange()") { 863 if (inverseBinding.inverseExpr != null) { 864 val valueExpr = inverseBinding.variableExpr 865 val getterCall = inverseBinding.getterCall 866 nl("// Inverse of ${inverseBinding.expr}") 867 nl("// is ${inverseBinding.inverseExpr}") 868 nl("${valueExpr.resolvedType.toJavaCode()} ${valueExpr.name} = ${getterCall.toJava("mBindingComponent", target.fieldName)};") 869 nl(inverseBinding.callbackExprModel.localizeGlobalVariables(valueExpr)) 870 nl(inverseBinding.executionPath.toCode()) 871 } else { 872 block("synchronized(this)") { 873 val flagSet = inverseBinding.chainedExpressions.fold(FlagSet(), { initial, expr -> 874 initial.or(FlagSet(expr.id)) 875 }) 876 mDirtyFlags.mapOr(flagSet) { suffix, index -> 877 tab("${mDirtyFlags.localValue(index)} |= ${flagSet.binaryCode(index)};") 878 } 879 } 880 nl("requestRebind();") 881 } 882 } 883 }.app(";") 884 } 885 } 886 } 887 fun declareDirtyFlags() = kcode("// dirty flag") { 888 model.ext.localizedFlags.forEach { flag -> 889 flag.notEmpty { suffix, value -> 890 nl("private") 891 app(" ", if(flag.isDynamic) null else "static final"); 892 app(" ", " ${flag.type} ${flag.localName}$suffix = ${longToBinary(value)};") 893 } 894 } 895 } 896 897 fun flagMapping() = kcode("/* flag mapping") { 898 if (model.flagMapping != null) { 899 val mapping = model.flagMapping 900 for (i in mapping.indices) { 901 tab("flag $i (${longToBinary(1L + i)}): ${model.findFlagExpression(i)}") 902 } 903 } 904 nl("flag mapping end*/") 905 } 906 907 fun executePendingBindings() = kcode("") { 908 nl("@Override") 909 block("protected void executeBindings()") { 910 val tmpDirtyFlags = FlagSet(mDirtyFlags.buckets) 911 tmpDirtyFlags.localName = "dirtyFlags"; 912 for (i in (0..mDirtyFlags.buckets.size - 1)) { 913 nl("${tmpDirtyFlags.type} ${tmpDirtyFlags.localValue(i)} = 0;") 914 } 915 block("synchronized(this)") { 916 for (i in (0..mDirtyFlags.buckets.size - 1)) { 917 nl("${tmpDirtyFlags.localValue(i)} = ${mDirtyFlags.localValue(i)};") 918 nl("${mDirtyFlags.localValue(i)} = 0;") 919 } 920 } 921 model.pendingExpressions.filter { it.needsLocalField }.forEach { 922 nl("${it.resolvedType.toJavaCode()} ${it.executePendingLocalName} = ${if (it.isVariable()) it.fieldName else it.defaultValue};") 923 } 924 L.d("writing executePendingBindings for %s", className) 925 do { 926 val batch = ExprModel.filterShouldRead(model.pendingExpressions) 927 val justRead = arrayListOf<Expr>() 928 L.d("batch: %s", batch) 929 while (!batch.none()) { 930 val readNow = batch.filter { it.shouldReadNow(justRead) } 931 if (readNow.isEmpty()) { 932 throw IllegalStateException("do not know what I can read. bailing out ${batch.joinToString("\n")}") 933 } 934 L.d("new read now. batch size: %d, readNow size: %d", batch.size, readNow.size) 935 nl(readWithDependants(readNow, justRead, batch, tmpDirtyFlags)) 936 batch.removeAll(justRead) 937 } 938 nl("// batch finished") 939 } while (model.markBitsRead()) 940 // verify everything is read. 941 val batch = ExprModel.filterShouldRead(model.pendingExpressions) 942 if (batch.isNotEmpty()) { 943 L.e("could not generate code for %s. This might be caused by circular dependencies." 944 + "Please report on b.android.com. %d %s %s", layoutBinder.layoutname, 945 batch.size, batch[0], batch[0].toCode().generate()) 946 } 947 // 948 layoutBinder.sortedTargets.filter { it.isUsed } 949 .flatMap { it.bindings } 950 .groupBy { 951 "${tmpDirtyFlags.mapOr(it.expr.dirtyFlagSet) { suffix, index -> 952 "(${tmpDirtyFlags.localValue(index)} & ${it.expr.dirtyFlagSet.localValue(index)}) != 0" 953 }.joinToString(" || ") }" 954 }.forEach { 955 block("if (${it.key})") { 956 it.value.groupBy { Math.max(1, it.minApi) }.forEach { 957 val setterValues = kcode("") { 958 it.value.forEach { binding -> 959 nl(binding.toAssignmentCode()).app(";") 960 } 961 } 962 nl("// api target ${it.key}") 963 if (it.key > 1) { 964 block("if(getBuildSdkInt() >= ${it.key})") { 965 nl(setterValues) 966 } 967 } else { 968 nl(setterValues) 969 } 970 } 971 } 972 } 973 974 975 layoutBinder.sortedTargets.filter { it.isUsed } 976 .flatMap { it.bindings } 977 .filter { it.requiresOldValue() } 978 .groupBy {"${tmpDirtyFlags.mapOr(it.expr.dirtyFlagSet) { suffix, index -> 979 "(${tmpDirtyFlags.localValue(index)} & ${it.expr.dirtyFlagSet.localValue(index)}) != 0" 980 }.joinToString(" || ") 981 }"}.forEach { 982 block("if (${it.key})") { 983 it.value.groupBy { it.expr }.map { it.value.first() }.forEach { 984 it.componentExpressions.forEach { expr -> 985 nl("this.${expr.oldValueName} = ${expr.toCode().generate()};") 986 } 987 } 988 } 989 } 990 includedBinders.filter{it.isUsed }.forEach { binder -> 991 nl("${binder.fieldName}.executePendingBindings();") 992 } 993 layoutBinder.sortedTargets.filter{ 994 it.isUsed && it.resolvedType != null && it.resolvedType.extendsViewStub() 995 }.forEach { 996 block("if (${it.fieldName}.getBinding() != null)") { 997 nl("${it.fieldName}.getBinding().executePendingBindings();") 998 } 999 } 1000 } 1001 } 1002 1003 fun readWithDependants(expressionList: List<Expr>, justRead: MutableList<Expr>, 1004 batch: MutableList<Expr>, tmpDirtyFlags: FlagSet, 1005 inheritedFlags: FlagSet? = null) : KCode = kcode("") { 1006 expressionList.groupBy { it.shouldReadFlagSet }.forEach { 1007 val flagSet = it.key 1008 val needsIfWrapper = inheritedFlags == null || !flagSet.bitsEqual(inheritedFlags) 1009 val expressions = it.value 1010 val ifClause = "if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 1011 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 1012 }.joinToString(" || ") 1013 })" 1014 val readCode = kcode("") { 1015 val dependants = ArrayList<Expr>() 1016 expressions.groupBy { condition(it) }.forEach { 1017 val condition = it.key 1018 val assignedValues = it.value.filter { it.needsLocalField && !it.isVariable() } 1019 if (!assignedValues.isEmpty()) { 1020 val assignment = kcode("") { 1021 assignedValues.forEach { expr: Expr -> 1022 tab("// read ${expr}") 1023 tab("${expr.executePendingLocalName}").app(" = ", expr.toFullCode()).app(";") 1024 } 1025 } 1026 if (condition != null) { 1027 tab("if ($condition) {") { 1028 app("", assignment) 1029 } 1030 tab ("}") 1031 } else { 1032 app("", assignment) 1033 } 1034 it.value.filter { it.isObservable }.forEach { expr: Expr -> 1035 tab("updateRegistration(${expr.id}, ${expr.executePendingLocalName});") 1036 } 1037 } 1038 1039 it.value.forEach { expr: Expr -> 1040 justRead.add(expr) 1041 L.d("%s / readWithDependants %s", className, expr); 1042 L.d("flag set:%s . inherited flags: %s. need another if: %s", flagSet, inheritedFlags, needsIfWrapper); 1043 1044 // if I am the condition for an expression, set its flag 1045 expr.dependants.filter { 1046 !it.isConditional && it.dependant is TernaryExpr && 1047 (it.dependant as TernaryExpr).pred == expr 1048 }.map { it.dependant }.groupBy { 1049 // group by when those ternaries will be evaluated (e.g. don't set conditional flags for no reason) 1050 val ternaryBitSet = it.shouldReadFlagsWithConditionals 1051 val isBehindTernary = ternaryBitSet.nextSetBit(model.invalidateAnyFlagIndex) == -1 1052 if (!isBehindTernary) { 1053 val ternaryFlags = it.shouldReadWithConditionalsFlagSet 1054 "if(${tmpDirtyFlags.mapOr(ternaryFlags){ suffix, index -> 1055 "(${tmpDirtyFlags.localValue(index)} & ${ternaryFlags.localValue(index)}) != 0" 1056 }.joinToString(" || ")}) {" 1057 } else { 1058 // TODO if it is behind a ternary, we should set it when its predicate is elevated 1059 // Normally, this would mean that there is another code path to re-read our current expression. 1060 // Unfortunately, this may not be true due to the coverage detection in `expr#markAsReadIfDone`, this may never happen. 1061 // for v1.0, we'll go with always setting it and suffering an unnecessary calculation for this edge case. 1062 // we can solve this by listening to elevation events from the model. 1063 "" 1064 } 1065 }.forEach { 1066 val hasAnotherIf = it.key != "" 1067 if (hasAnotherIf) { 1068 tab(it.key) { 1069 tab("if (${expr.executePendingLocalName}) {") { 1070 it.value.forEach { 1071 val set = it.getRequirementFlagSet(true) 1072 mDirtyFlags.mapOr(set) { suffix, index -> 1073 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 1074 } 1075 } 1076 } 1077 tab("} else {") { 1078 it.value.forEach { 1079 val set = it.getRequirementFlagSet(false) 1080 mDirtyFlags.mapOr(set) { suffix, index -> 1081 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 1082 } 1083 } 1084 }.tab("}") 1085 }.app("}") 1086 } else { 1087 tab("if (${expr.executePendingLocalName}) {") { 1088 it.value.forEach { 1089 val set = it.getRequirementFlagSet(true) 1090 mDirtyFlags.mapOr(set) { suffix, index -> 1091 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 1092 } 1093 } 1094 } 1095 tab("} else {") { 1096 it.value.forEach { 1097 val set = it.getRequirementFlagSet(false) 1098 mDirtyFlags.mapOr(set) { suffix, index -> 1099 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 1100 } 1101 } 1102 } app("}") 1103 } 1104 } 1105 val chosen = expr.dependants.filter { 1106 val dependant = it.dependant 1107 batch.contains(dependant) && 1108 dependant.shouldReadFlagSet.andNot(flagSet).isEmpty && 1109 dependant.shouldReadNow(justRead) 1110 } 1111 if (chosen.isNotEmpty()) { 1112 dependants.addAll(chosen.map { it.dependant }) 1113 } 1114 } 1115 } 1116 if (dependants.isNotEmpty()) { 1117 val nextInheritedFlags = if (needsIfWrapper) flagSet else inheritedFlags 1118 nl(readWithDependants(dependants, justRead, batch, tmpDirtyFlags, nextInheritedFlags)) 1119 } 1120 } 1121 1122 if (needsIfWrapper) { 1123 block(ifClause) { 1124 nl(readCode) 1125 } 1126 } else { 1127 nl(readCode) 1128 } 1129 } 1130 } 1131 1132 fun condition(expr : Expr) : String? { 1133 if (expr.canBeEvaluatedToAVariable() && !expr.isVariable()) { 1134 // create an if case for all dependencies that might be null 1135 val nullables = expr.dependencies.filter { 1136 it.isMandatory && it.other.resolvedType.isNullable 1137 }.map { it.other } 1138 if (!expr.isEqualityCheck && nullables.isNotEmpty()) { 1139 return "${nullables.map { "${it.executePendingLocalName} != null" }.joinToString(" && ")}" 1140 } else { 1141 return null 1142 } 1143 } else { 1144 return null 1145 } 1146 } 1147 1148 fun declareListenerImpls() = kcode("// Listener Stub Implementations") { 1149 model.exprMap.values.filter { 1150 it.isUsed && it is ListenerExpr 1151 }.groupBy { it }.forEach { 1152 val expr = it.key as ListenerExpr 1153 val listenerType = expr.resolvedType; 1154 val extendsImplements : String 1155 if (listenerType.isInterface) { 1156 extendsImplements = "implements" 1157 } else { 1158 extendsImplements = "extends" 1159 } 1160 nl("public static class ${expr.listenerClassName} $extendsImplements ${listenerType.canonicalName}{") { 1161 if (expr.target.isDynamic) { 1162 tab("private ${expr.target.resolvedType.toJavaCode()} value;") 1163 tab("public ${expr.listenerClassName} setValue(${expr.target.resolvedType.toJavaCode()} value) {") { 1164 tab("this.value = value;") 1165 tab("return value == null ? null : this;") 1166 } 1167 tab("}") 1168 } 1169 val listenerMethod = expr.method 1170 val parameterTypes = listenerMethod.parameterTypes 1171 val returnType = listenerMethod.getReturnType(parameterTypes.toList()) 1172 tab("@Override") 1173 tab("public $returnType ${listenerMethod.name}(${ 1174 parameterTypes.withIndex().map { 1175 "${it.value.toJavaCode()} arg${it.index}" 1176 }.joinToString(", ") 1177 }) {") { 1178 val obj : String 1179 if (expr.target.isDynamic) { 1180 obj = "this.value" 1181 } else { 1182 obj = expr.target.toCode().generate(); 1183 } 1184 val returnStr : String 1185 if (!returnType.isVoid) { 1186 returnStr = "return " 1187 } else { 1188 returnStr = "" 1189 } 1190 val args = parameterTypes.withIndex().map { 1191 "arg${it.index}" 1192 }.joinToString(", ") 1193 tab("$returnStr$obj.${expr.name}($args);") 1194 } 1195 tab("}") 1196 } 1197 nl("}") 1198 } 1199 } 1200 1201 fun declareFactories() = kcode("") { 1202 block("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot)") { 1203 nl("return inflate(inflater, root, attachToRoot, android.databinding.DataBindingUtil.getDefaultComponent());") 1204 } 1205 block("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot, android.databinding.DataBindingComponent bindingComponent)") { 1206 nl("return android.databinding.DataBindingUtil.<$baseClassName>inflate(inflater, ${layoutBinder.modulePackage}.R.layout.${layoutBinder.layoutname}, root, attachToRoot, bindingComponent);") 1207 } 1208 if (!layoutBinder.isMerge) { 1209 block("public static $baseClassName inflate(android.view.LayoutInflater inflater)") { 1210 nl("return inflate(inflater, android.databinding.DataBindingUtil.getDefaultComponent());") 1211 } 1212 block("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.databinding.DataBindingComponent bindingComponent)") { 1213 nl("return bind(inflater.inflate(${layoutBinder.modulePackage}.R.layout.${layoutBinder.layoutname}, null, false), bindingComponent);") 1214 } 1215 block("public static $baseClassName bind(android.view.View view)") { 1216 nl("return bind(view, android.databinding.DataBindingUtil.getDefaultComponent());") 1217 } 1218 block("public static $baseClassName bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent)") { 1219 block("if (!\"${layoutBinder.tag}_0\".equals(view.getTag()))") { 1220 nl("throw new RuntimeException(\"view tag isn't correct on view:\" + view.getTag());") 1221 } 1222 nl("return new $baseClassName(bindingComponent, view);") 1223 } 1224 } 1225 } 1226 1227 /** 1228 * When called for a library compilation, we do not generate real implementations 1229 */ 1230 public fun writeBaseClass(forLibrary : Boolean) : String = 1231 kcode("package ${layoutBinder.`package`};") { 1232 Scope.reset() 1233 nl("import android.databinding.Bindable;") 1234 nl("import android.databinding.DataBindingUtil;") 1235 nl("import android.databinding.ViewDataBinding;") 1236 nl("public abstract class $baseClassName extends ViewDataBinding {") 1237 layoutBinder.sortedTargets.filter{it.id != null}.forEach { 1238 tab("public final ${it.interfaceClass} ${it.fieldName};") 1239 } 1240 nl("") 1241 tab("protected $baseClassName(android.databinding.DataBindingComponent bindingComponent, android.view.View root_, int localFieldCount") { 1242 layoutBinder.sortedTargets.filter{it.id != null}.forEach { 1243 tab(", ${it.interfaceClass} ${it.constructorParamName}") 1244 } 1245 } 1246 tab(") {") { 1247 tab("super(bindingComponent, root_, localFieldCount);") 1248 layoutBinder.sortedTargets.filter{it.id != null}.forEach { 1249 tab("this.${it.fieldName} = ${it.constructorParamName};") 1250 } 1251 } 1252 tab("}") 1253 nl("") 1254 variables.forEach { 1255 if (it.userDefinedType != null) { 1256 val type = ModelAnalyzer.getInstance().applyImports(it.userDefinedType, model.imports) 1257 tab("public abstract void ${it.setterName}($type ${it.readableName});") 1258 } 1259 } 1260 tab("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") { 1261 tab("return inflate(inflater, root, attachToRoot, android.databinding.DataBindingUtil.getDefaultComponent());") 1262 } 1263 tab("}") 1264 tab("public static $baseClassName inflate(android.view.LayoutInflater inflater) {") { 1265 tab("return inflate(inflater, android.databinding.DataBindingUtil.getDefaultComponent());") 1266 } 1267 tab("}") 1268 tab("public static $baseClassName bind(android.view.View view) {") { 1269 if (forLibrary) { 1270 tab("return null;") 1271 } else { 1272 tab("return bind(view, android.databinding.DataBindingUtil.getDefaultComponent());") 1273 } 1274 } 1275 tab("}") 1276 tab("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot, android.databinding.DataBindingComponent bindingComponent) {") { 1277 if (forLibrary) { 1278 tab("return null;") 1279 } else { 1280 tab("return DataBindingUtil.<$baseClassName>inflate(inflater, ${layoutBinder.modulePackage}.R.layout.${layoutBinder.layoutname}, root, attachToRoot, bindingComponent);") 1281 } 1282 } 1283 tab("}") 1284 tab("public static $baseClassName inflate(android.view.LayoutInflater inflater, android.databinding.DataBindingComponent bindingComponent) {") { 1285 if (forLibrary) { 1286 tab("return null;") 1287 } else { 1288 tab("return DataBindingUtil.<$baseClassName>inflate(inflater, ${layoutBinder.modulePackage}.R.layout.${layoutBinder.layoutname}, null, false, bindingComponent);") 1289 } 1290 } 1291 tab("}") 1292 tab("public static $baseClassName bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) {") { 1293 if (forLibrary) { 1294 tab("return null;") 1295 } else { 1296 tab("return ($baseClassName)bind(bindingComponent, view, ${layoutBinder.modulePackage}.R.layout.${layoutBinder.layoutname});") 1297 } 1298 } 1299 tab("}") 1300 nl("}") 1301 }.generate() 1302} 1303