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.Binding
     17 import android.databinding.tool.BindingTarget
     18 import android.databinding.tool.CallbackWrapper
     19 import android.databinding.tool.InverseBinding
     20 import android.databinding.tool.LayoutBinder
     21 import android.databinding.tool.expr.Expr
     22 import android.databinding.tool.expr.ExprModel
     23 import android.databinding.tool.expr.FieldAccessExpr
     24 import android.databinding.tool.expr.IdentifierExpr
     25 import android.databinding.tool.expr.LambdaExpr
     26 import android.databinding.tool.expr.ListenerExpr
     27 import android.databinding.tool.expr.ResourceExpr
     28 import android.databinding.tool.expr.TernaryExpr
     29 import android.databinding.tool.expr.localizeGlobalVariables
     30 import android.databinding.tool.expr.shouldLocalizeInCallbacks
     31 import android.databinding.tool.expr.toCode
     32 import android.databinding.tool.ext.androidId
     33 import android.databinding.tool.ext.br
     34 import android.databinding.tool.ext.joinToCamelCaseAsVar
     35 import android.databinding.tool.ext.lazyProp
     36 import android.databinding.tool.ext.versionedLazy
     37 import android.databinding.tool.processing.ErrorMessages
     38 import android.databinding.tool.reflection.ModelAnalyzer
     39 import android.databinding.tool.reflection.ModelClass
     40 import android.databinding.tool.util.L
     41 import android.databinding.tool.util.Preconditions
     42 import java.util.ArrayList
     43 import java.util.Arrays
     44 import java.util.BitSet
     45 import java.util.HashMap
     46 
     47 fun String.stripNonJava() = this.split("[^a-zA-Z0-9]".toRegex()).map{ it.trim() }.joinToCamelCaseAsVar()
     48 
     49 enum 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 
     76 class 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 
    112 fun ModelClass.defaultValue() = ModelAnalyzer.getInstance().getDefaultValue(toJavaCode())
    113 fun ExprModel.getUniqueFieldName(base : String, isPublic : kotlin.Boolean) : String = ext.getUniqueName(base, Scope.FIELD, isPublic)
    114 fun ExprModel.getUniqueMethodName(base : String, isPublic : kotlin.Boolean) : String = ext.getUniqueName(base, Scope.METHOD, isPublic)
    115 fun ExprModel.getConstructorParamName(base : String) : String = ext.getUniqueName(base, Scope.CONSTRUCTOR_PARAM, false)
    116 fun ExprModel.localizeFlag(set : FlagSet, base : String) : FlagSet = ext.localizeFlag(set, base)
    117 
    118 val Expr.needsLocalField by lazyProp { expr : Expr ->
    119     expr.canBeEvaluatedToAVariable() && !(expr.isVariable() && !expr.isUsed) && (expr.isDynamic || expr is ResourceExpr)
    120 }
    121 
    122 fun Expr.isForcedToLocalize() = model.ext.forceLocalize.contains(this)
    123 
    124 // not necessarily unique. Uniqueness is solved per scope
    125 val 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 
    133 fun 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 
    141 val 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 
    154 val 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 
    162 val 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 
    170 val BindingTarget.constructorParamName by lazyProp { target : BindingTarget ->
    171     target.model.getConstructorParamName(target.readableName)
    172 }
    173 
    174 // not necessarily unique. Uniqueness is decided per scope
    175 val 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 
    181 val Expr.fieldName by lazyProp { expr : Expr ->
    182     expr.model.getUniqueFieldName("m${expr.readableName.capitalize()}", false)
    183 }
    184 
    185 val 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 
    191 val Expr.listenerClassName by lazyProp { expr : Expr ->
    192     expr.model.getUniqueFieldName("${expr.resolvedType.simpleName}Impl", false)
    193 }
    194 
    195 val Expr.oldValueName by lazyProp { expr : Expr ->
    196     expr.model.getUniqueFieldName("mOld${expr.readableName.capitalize()}", false)
    197 }
    198 
    199 fun Expr.scopedName() : String = when(Scope.currentScope) {
    200     Scope.CALLBACK -> callbackLocalName
    201     else -> executePendingLocalName
    202 }
    203 
    204 val 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 
    209 val 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 
    214 val Expr.setterName by lazyProp { expr : Expr ->
    215     expr.model.getUniqueMethodName("set${expr.readableName.capitalize()}", true)
    216 }
    217 
    218 val Expr.onChangeName by lazyProp { expr : Expr ->
    219     expr.model.getUniqueMethodName("onChange${expr.readableName.capitalize()}", false)
    220 }
    221 
    222 val Expr.getterName by lazyProp { expr : Expr ->
    223     expr.model.getUniqueMethodName("get${expr.readableName.capitalize()}", true)
    224 }
    225 
    226 fun Expr.isVariable() = this is IdentifierExpr && this.isDynamic
    227 
    228 val Expr.dirtyFlagSet by lazyProp { expr : Expr ->
    229     FlagSet(expr.invalidFlags, expr.model.flagBucketCount)
    230 }
    231 
    232 val Expr.invalidateFlagSet by lazyProp { expr : Expr ->
    233     FlagSet(expr.id)
    234 }
    235 
    236 val Expr.shouldReadFlagSet by versionedLazy { expr : Expr ->
    237     FlagSet(expr.shouldReadFlags, expr.model.flagBucketCount)
    238 }
    239 
    240 val Expr.shouldReadWithConditionalsFlagSet by versionedLazy { expr : Expr ->
    241     FlagSet(expr.shouldReadFlagsWithConditionals, expr.model.flagBucketCount)
    242 }
    243 
    244 val Expr.conditionalFlags by lazyProp { expr : Expr ->
    245     arrayListOf(FlagSet(expr.getRequirementFlagIndex(false)),
    246             FlagSet(expr.getRequirementFlagIndex(true)))
    247 }
    248 
    249 fun 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 
    260 val 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 
    272 fun Expr.getRequirementFlagSet(expected : Boolean) : FlagSet = conditionalFlags[if(expected) 1 else 0]
    273 
    274 fun 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 
    282 fun getWordSuffix(wordIndex : Int) : String {
    283     return if(wordIndex == 0) "" else "_$wordIndex"
    284 }
    285 
    286 fun FlagSet.localValue(bucketIndex : Int) =
    287         if (localName == null) binaryCode(bucketIndex)
    288         else "$localName${getWordSuffix(bucketIndex)}"
    289 
    290 fun FlagSet.binaryCode(bucketIndex : Int) = longToBinary(buckets[bucketIndex])
    291 
    292 
    293 fun longToBinary(l : Long) = "0x${java.lang.Long.toHexString(l)}L"
    294 
    295 fun <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 
    307 fun 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 
    317 class 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