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