• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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>()
enternull60         fun enter(scope : Scope) {
61             scopeStack.add(currentScope)
62             currentScope = scope
63         }
64 
exitnull65         fun exit() {
66             currentScope = scopeStack.removeAt(scopeStack.size - 1)
67         }
68 
resetnull69         fun reset() {
70             scopeStack.clear()
71             currentScope = GLOBAL
72         }
73     }
74 }
75 
76 class ExprModelExt {
77     val usedFieldNames = hashMapOf<Scope, MutableSet<String>>();
78     init {
<lambda>null79         Scope.values().forEach { usedFieldNames[it] = hashSetOf<String>() }
80     }
81 
82     internal val forceLocalize = hashSetOf<Expr>()
83 
84     val localizedFlags = arrayListOf<FlagSet>()
85 
localizeFlagnull86     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 
getUniqueNamenull93     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 
ModelClassnull112 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 
Exprnull122 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 
BindingTargetnull133 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 
targetnull141 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 
targetnull154 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 
targetnull162 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 
targetnull170 val BindingTarget.constructorParamName by lazyProp { target : BindingTarget ->
171     target.model.getConstructorParamName(target.readableName)
172 }
173 
174 // not necessarily unique. Uniqueness is decided per scope
exprnull175 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 
exprnull181 val Expr.fieldName by lazyProp { expr : Expr ->
182     expr.model.getUniqueFieldName("m${expr.readableName.capitalize()}", false)
183 }
184 
inverseBindingnull185 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 
exprnull191 val Expr.listenerClassName by lazyProp { expr : Expr ->
192     expr.model.getUniqueFieldName("${expr.resolvedType.simpleName}Impl", false)
193 }
194 
exprnull195 val Expr.oldValueName by lazyProp { expr : Expr ->
196     expr.model.getUniqueFieldName("mOld${expr.readableName.capitalize()}", false)
197 }
198 
Exprnull199 fun Expr.scopedName() : String = when(Scope.currentScope) {
200     Scope.CALLBACK -> callbackLocalName
201     else -> executePendingLocalName
202 }
203 
exprnull204 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 
exprnull209 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 
exprnull214 val Expr.setterName by lazyProp { expr : Expr ->
215     expr.model.getUniqueMethodName("set${expr.readableName.capitalize()}", true)
216 }
217 
exprnull218 val Expr.onChangeName by lazyProp { expr : Expr ->
219     expr.model.getUniqueMethodName("onChange${expr.readableName.capitalize()}", false)
220 }
221 
exprnull222 val Expr.getterName by lazyProp { expr : Expr ->
223     expr.model.getUniqueMethodName("get${expr.readableName.capitalize()}", true)
224 }
225 
Exprnull226 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 
exprnull232 val Expr.invalidateFlagSet by lazyProp { expr : Expr ->
233     FlagSet(expr.id)
234 }
235 
exprnull236 val Expr.shouldReadFlagSet by versionedLazy { expr : Expr ->
237     FlagSet(expr.shouldReadFlags, expr.model.flagBucketCount)
238 }
239 
exprnull240 val Expr.shouldReadWithConditionalsFlagSet by versionedLazy { expr : Expr ->
241     FlagSet(expr.shouldReadFlagsWithConditionals, expr.model.flagBucketCount)
242 }
243 
exprnull244 val Expr.conditionalFlags by lazyProp { expr : Expr ->
245     arrayListOf(FlagSet(expr.getRequirementFlagIndex(false)),
246             FlagSet(expr.getRequirementFlagIndex(true)))
247 }
248 
Bindingnull249 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 
layoutBindernull260 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 
getRequirementFlagSetnull272 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 
getWordSuffixnull282 fun getWordSuffix(wordIndex : Int) : String {
283     return if(wordIndex == 0) "" else "_$wordIndex"
284 }
285 
FlagSetnull286 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 
indexFromTagnull307 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>()
<lambda>null320     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 
<lambda>null332     val includedBinders by lazy {
333         layoutBinder.bindingTargets.filter { it.isBinder }
334     }
335 
<lambda>null336     val variables by lazy {
337         model.exprMap.values.filterIsInstance(IdentifierExpr::class.java).filter { it.isVariable() }
338     }
339 
<lambda>null340     val usedVariables by lazy {
341         variables.filter {it.isUsed || it.isIsUsedInCallback }
342     }
343 
<lambda>null344     val callbacks by lazy {
345         model.exprMap.values.filterIsInstance(LambdaExpr::class.java)
346     }
347 
writenull348     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     }
buildImplementsnull404     fun buildImplements() : String {
405         return if (callbacks.isEmpty()) {
406             ""
407         } else {
408             "implements " + callbacks.map { it.callbackWrapper.cannonicalListenerName }.distinct().joinToString(", ")
409         }
410     }
411 
calculateIndicesnull412     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     }
<lambda>null426     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 
<lambda>null568     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 
<lambda>null590     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 
fieldConversionnull633     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 
<lambda>null643     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 
<lambda>null660     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 
declareSetVariablenull686     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 
<lambda>null710     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 
<lambda>null750     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 
<lambda>null804     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 
<lambda>null817     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 
<lambda>null827     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 
<lambda>null839     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 
<lambda>null848     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     }
<lambda>null887     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 
<lambda>null897     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 
<lambda>null907     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 
readWithDependantsnull1003     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 
conditionnull1132     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 
<lambda>null1148     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 
<lambda>null1201     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      */
writeBaseClassnull1230     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