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