LayoutBinderWriter.kt revision 7551861a29997eac7eaf6318e4d9f1cebd8b81d6
123b797ab5151eb2474f3bdd679f2f07bfd723042John Reck/*
223b797ab5151eb2474f3bdd679f2f07bfd723042John Reck * Copyright (C) 2015 The Android Open Source Project
323b797ab5151eb2474f3bdd679f2f07bfd723042John Reck * Licensed under the Apache License, Version 2.0 (the "License");
423b797ab5151eb2474f3bdd679f2f07bfd723042John Reck * you may not use this file except in compliance with the License.
523b797ab5151eb2474f3bdd679f2f07bfd723042John Reck * You may obtain a copy of the License at
623b797ab5151eb2474f3bdd679f2f07bfd723042John Reck *      http://www.apache.org/licenses/LICENSE-2.0
723b797ab5151eb2474f3bdd679f2f07bfd723042John Reck * Unless required by applicable law or agreed to in writing, software
823b797ab5151eb2474f3bdd679f2f07bfd723042John Reck * distributed under the License is distributed on an "AS IS" BASIS,
923b797ab5151eb2474f3bdd679f2f07bfd723042John Reck * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1023b797ab5151eb2474f3bdd679f2f07bfd723042John Reck * See the License for the specific language governing permissions and
1123b797ab5151eb2474f3bdd679f2f07bfd723042John Reck * limitations under the License.
1223b797ab5151eb2474f3bdd679f2f07bfd723042John Reck */
1323b797ab5151eb2474f3bdd679f2f07bfd723042John Reck
1423b797ab5151eb2474f3bdd679f2f07bfd723042John Reckpackage com.android.databinding.writer
1523b797ab5151eb2474f3bdd679f2f07bfd723042John Reck
1623b797ab5151eb2474f3bdd679f2f07bfd723042John Reckimport com.android.databinding.LayoutBinder
1723b797ab5151eb2474f3bdd679f2f07bfd723042John Reckimport com.android.databinding.expr.Expr
1823b797ab5151eb2474f3bdd679f2f07bfd723042John Reckimport com.android.databinding.ClassAnalyzer
193b20251a355c88193c439f928a84ae69483fb488John Reckimport kotlin.properties.Delegates
204f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckimport com.android.databinding.ext.joinToCamelCaseAsVar
21119907cd2575c56b1ebf66348b52e67aaf6a88d8John Reckimport com.android.databinding.BindingTarget
2223b797ab5151eb2474f3bdd679f2f07bfd723042John Reckimport com.android.databinding.expr.IdentifierExpr
2319b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reckimport com.android.databinding.util.Log
2465fe5eeb19e2e15c8b1ee91e8a2dcf0c25e48ca6Chris Craikimport java.util.BitSet
2596a5c4c7bab6718524de7253da8309143ab48befChris Craikimport java.util.HashSet
2619b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reckimport com.android.databinding.util.L
274f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckimport org.apache.commons.lang3.NotImplementedException
2823b797ab5151eb2474f3bdd679f2f07bfd723042John Reckimport com.android.databinding.expr.ExprModel
2965fe5eeb19e2e15c8b1ee91e8a2dcf0c25e48ca6Chris Craikimport com.android.databinding.writer.FlagSet
3065fe5eeb19e2e15c8b1ee91e8a2dcf0c25e48ca6Chris Craikimport java.util.Arrays
3165fe5eeb19e2e15c8b1ee91e8a2dcf0c25e48ca6Chris Craikimport java.lang
3265fe5eeb19e2e15c8b1ee91e8a2dcf0c25e48ca6Chris Craikimport com.android.databinding.expr.TernaryExpr
33f47a594f5250b1914c36423ee6b371f0b8db09d0John Reckimport com.android.databinding.ext.androidId
34f47a594f5250b1914c36423ee6b371f0b8db09d0John Reckimport com.android.databinding.expr.FieldAccessExpr
35f47a594f5250b1914c36423ee6b371f0b8db09d0John Reckimport com.android.databinding.expr.ComparisonExpr
3623b797ab5151eb2474f3bdd679f2f07bfd723042John Reckimport com.android.databinding.expr.GroupExpr
3723b797ab5151eb2474f3bdd679f2f07bfd723042John Reckimport com.android.databinding.expr.MathExpr
3823b797ab5151eb2474f3bdd679f2f07bfd723042John Reckimport com.android.databinding.expr.MethodCallExpr
3923b797ab5151eb2474f3bdd679f2f07bfd723042John Reckimport com.android.databinding.expr.StaticIdentifierExpr
40119907cd2575c56b1ebf66348b52e67aaf6a88d8John Reckimport com.android.databinding.expr.SymbolExpr
41119907cd2575c56b1ebf66348b52e67aaf6a88d8John Reckimport com.android.databinding.ext.androidId
423b20251a355c88193c439f928a84ae69483fb488John Reckimport com.android.databinding.ext.lazy
433b20251a355c88193c439f928a84ae69483fb488John Reckimport com.android.databinding.ext.br
444f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckimport com.android.databinding.ext.toJavaCode
451125d1fa92ab9f3b8315bbfb72e038b62dfd454bJohn Reckimport com.android.databinding.ext.isObservable
461125d1fa92ab9f3b8315bbfb72e038b62dfd454bJohn Reckimport com.android.databinding.expr.ResourceExpr
474f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckimport com.android.databinding.expr.BracketExpr
48d41c4d8c732095ae99c955b6b82f7306633004b1Chris Craik
49e45b1fd03b524d2b57cc6c222d89076a31a08beaJohn Reckfun Expr.isObservable() = com.android.databinding.ClassAnalyzer.getInstance().isObservable(this.getResolvedType())
5051d6a3db97bdd5315f1a17a4b447d10a92217b98Chris Craik
51ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reckfun String.stripNonJava() = this.split("[^a-zA-Z0-9]").map{ it.trim() }.joinToCamelCaseAsVar()
52ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck
53443a714fa7c0dd07fee3527cc5bc3d3ca1fb7d44John Reckclass ExprModelExt {
5423b797ab5151eb2474f3bdd679f2f07bfd723042John Reck    val usedFieldNames = hashSetOf<String>()
5523b797ab5151eb2474f3bdd679f2f07bfd723042John Reck    val localizedFlags = arrayListOf<FlagSet>()
5623b797ab5151eb2474f3bdd679f2f07bfd723042John Reck
5717035b0211a3c9d45ea46a99217a6acbe76e8fbeJohn Reck    fun localizeFlag(set : FlagSet, name:String) : FlagSet {
58443a714fa7c0dd07fee3527cc5bc3d3ca1fb7d44John Reck        localizedFlags.add(set)
594f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck        val result = getUniqueFieldName(name)
604f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck        set.setLocalName(result)
6117035b0211a3c9d45ea46a99217a6acbe76e8fbeJohn Reck        return set
6217035b0211a3c9d45ea46a99217a6acbe76e8fbeJohn Reck    }
63d41c4d8c732095ae99c955b6b82f7306633004b1Chris Craik
6417035b0211a3c9d45ea46a99217a6acbe76e8fbeJohn Reck    fun getUniqueFieldName(base : String) : String {
6517035b0211a3c9d45ea46a99217a6acbe76e8fbeJohn Reck        var candidate = base
66e2478d45ccbe5b6abb360ac9d44771b5f4a50bdeJohn Reck        var i = 0
674f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck        while (usedFieldNames.contains(candidate)) {
684f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck            i ++
69d41c4d8c732095ae99c955b6b82f7306633004b1Chris Craik            candidate = base + i
704f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck        }
7123b797ab5151eb2474f3bdd679f2f07bfd723042John Reck        usedFieldNames.add(candidate)
7223b797ab5151eb2474f3bdd679f2f07bfd723042John Reck        return candidate
73a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck    }
74fbc8df03e498baf47ff1a5e05e182f1bcd60c770John Reck}
75fbc8df03e498baf47ff1a5e05e182f1bcd60c770John Reck
76a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reckval ExprModel.ext by Delegates.lazy { (target : ExprModel) ->
77a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck    ExprModelExt()
7823b797ab5151eb2474f3bdd679f2f07bfd723042John Reck}
793b20251a355c88193c439f928a84ae69483fb488John Reck
8023b797ab5151eb2474f3bdd679f2f07bfd723042John Reckfun ExprModel.getUniqueFieldName(base : String) : String = ext.getUniqueFieldName(base)
8123b797ab5151eb2474f3bdd679f2f07bfd723042John Reck
8223b797ab5151eb2474f3bdd679f2f07bfd723042John Reckfun ExprModel.localizeFlag(set : FlagSet, base : String) : FlagSet = ext.localizeFlag(set, base)
8323b797ab5151eb2474f3bdd679f2f07bfd723042John Reck
843b20251a355c88193c439f928a84ae69483fb488John Reckval BindingTarget.readableUniqueName by Delegates.lazy {(target: BindingTarget) ->
8523b797ab5151eb2474f3bdd679f2f07bfd723042John Reck    val stripped = target.getId().androidId().stripNonJava()
8623b797ab5151eb2474f3bdd679f2f07bfd723042John Reck    target.getModel().ext.getUniqueFieldName(stripped)
8723b797ab5151eb2474f3bdd679f2f07bfd723042John Reck}
881125d1fa92ab9f3b8315bbfb72e038b62dfd454bJohn Reck
891125d1fa92ab9f3b8315bbfb72e038b62dfd454bJohn Reckval BindingTarget.fieldName by Delegates.lazy { (target : BindingTarget) ->
904f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck    "m${target.readableUniqueName.capitalize()}"
91dbc9a86d05e5e835051de22f6cb30ec1921e9705John Reck}
92368cdd85268999997fb495cf90c4417221797de0John Reck
93368cdd85268999997fb495cf90c4417221797de0John Reckval BindingTarget.getterName by Delegates.lazy { (target : BindingTarget) ->
944f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck    "get${target.readableUniqueName.capitalize()}"
954f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck}
964f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck
974f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckval BindingTarget.androidId by Delegates.lazy { (target : BindingTarget) ->
982cdbc7d2283aae3d77b12c8fdbba8ca4bd3db5eaJohn Reck    "R.id.${target.getId().androidId()}"
99d41c4d8c732095ae99c955b6b82f7306633004b1Chris Craik}
1002cdbc7d2283aae3d77b12c8fdbba8ca4bd3db5eaJohn Reck
1014f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckval Expr.readableUniqueName by Delegates.lazy { (expr : Expr) ->
1024f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck    Log.d { "readableUniqueName for ${expr.getUniqueKey()}" }
1034f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck    val stripped = "${expr.getUniqueKey().stripNonJava()}"
104f7d9c1dc84671d4e99657ef071d275700d85bb11John Reck    expr.getModel().ext.getUniqueFieldName(stripped)
105f7d9c1dc84671d4e99657ef071d275700d85bb11John Reck}
106f7d9c1dc84671d4e99657ef071d275700d85bb11John Reck
107dbc9a86d05e5e835051de22f6cb30ec1921e9705John Reckval Expr.fieldName by Delegates.lazy { (expr : Expr) ->
1084f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck    "m${expr.readableUniqueName.capitalize()}"
1094f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck}
1101125d1fa92ab9f3b8315bbfb72e038b62dfd454bJohn Reck
1111125d1fa92ab9f3b8315bbfb72e038b62dfd454bJohn Reckval Expr.hasFlag by Delegates.lazy { (expr : Expr) ->
1121125d1fa92ab9f3b8315bbfb72e038b62dfd454bJohn Reck    expr.getId() < expr.getModel().getInvalidateableFieldLimit()
1131125d1fa92ab9f3b8315bbfb72e038b62dfd454bJohn Reck}
114a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck
1154f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckval Expr.localName by Delegates.lazy { (expr : Expr) ->
1162cdbc7d2283aae3d77b12c8fdbba8ca4bd3db5eaJohn Reck    if(expr.isVariable()) expr.fieldName else "${expr.readableUniqueName}"
1173b20251a355c88193c439f928a84ae69483fb488John Reck}
1184f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck
1194f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckval Expr.setterName by Delegates.lazy { (expr : Expr) ->
1204f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck    "set${expr.readableUniqueName.capitalize()}"
1214f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck}
122a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck
1234f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckval Expr.onChangeName by Delegates.lazy { (expr : Expr) ->
124f7d9c1dc84671d4e99657ef071d275700d85bb11John Reck    "onChange${expr.readableUniqueName.capitalize()}"
125f7d9c1dc84671d4e99657ef071d275700d85bb11John Reck}
1269fb42f07784ac9e1ab29fa7d5bcda6c3081d238fJohn Reck
12701a5ea35fbba4c5bb1d7790ae1677a2fa752e042John Reckval Expr.getterName by Delegates.lazy { (expr : Expr) ->
1284f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck    "get${expr.readableUniqueName.capitalize()}"
1294f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck}
130284b24358410cb0200e525a5ba36994090c83f20Chris Craik
13164bb413a664001c95c8439cf097dc3033f4ed733Andreas Gampeval Expr.staticFieldName by Delegates.lazy { (expr : Expr) ->
13264bb413a664001c95c8439cf097dc3033f4ed733Andreas Gampe    "s${expr.readableUniqueName.capitalize()}"
1334f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck}
134058fc640017c90120c599d378a4cbc55668b05b7Chris Craik
1354f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckval Expr.dirtyFlagName by Delegates.lazy { (expr : Expr) ->
1364f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck    "sFlag${expr.readableUniqueName.capitalize()}"
13763a06673253914510bbeebd500655008682dade1John Reck}
13863a06673253914510bbeebd500655008682dade1John Reck
13963a06673253914510bbeebd500655008682dade1John Reckval Expr.shouldReadFlagName by Delegates.lazy { (expr : Expr) ->
14063a06673253914510bbeebd500655008682dade1John Reck    "sFlagRead${expr.readableUniqueName.capitalize()}"
141860d155f866cc15a725e7ce03763280987f24901John Reck}
142dbc9a86d05e5e835051de22f6cb30ec1921e9705John Reck
143dbc9a86d05e5e835051de22f6cb30ec1921e9705John Reckval Expr.invalidateFlagName by Delegates.lazy { (expr : Expr) ->
1443b20251a355c88193c439f928a84ae69483fb488John Reck    "sFlag${expr.readableUniqueName.capitalize()}Invalid"
145860d155f866cc15a725e7ce03763280987f24901John Reck}
146860d155f866cc15a725e7ce03763280987f24901John Reck
14768bfe0a37a0dcef52abd81688d8520c5d16e1a85John Reckval Expr.conditionalFlagPrefix by Delegates.lazy { (expr : Expr) ->
14868bfe0a37a0dcef52abd81688d8520c5d16e1a85John Reck    "sFlag${expr.readableUniqueName.capitalize()}Is"
149d72e0a339b54af0c4e731513bbad120dff694723John Reck}
150d72e0a339b54af0c4e731513bbad120dff694723John Reck
151d72e0a339b54af0c4e731513bbad120dff694723John Reck
15219b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reckfun Expr.toCode(full : Boolean = false) : KCode {
15319b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reck    val it = this
15419b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reck    if (isDynamic() && !full) {
155ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        return kcode(localName)
156f9be77940e365036fecd8cc0e491e8545c34e79bJohn Reck    }
15718f16e6fba74eda173e1e7c869e6e2e2acc073ffJohn Reck    return when (it) {
158ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        is ComparisonExpr -> kcode("") {
159ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck            app("", it.getLeft().toCode())
160ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck            app(it.getOp())
161ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck            app("", it.getRight().toCode())
162e4267ea4f20740c37c01bfb6aefcf61fddc4566aJohn Reck        }
16325fbb3fa1138675379102a44405852555cefccbdJohn Reck        is FieldAccessExpr -> kcode("") {
164998a6d81896df8b662cc10ddeb35087b78b38d72John Reck            app("", it.getParent().toCode())
165998a6d81896df8b662cc10ddeb35087b78b38d72John Reck            if (it.getGetter().type == com.android.databinding.ClassAnalyzer.Callable.Type.FIELD) {
166998a6d81896df8b662cc10ddeb35087b78b38d72John Reck                app(".", it.getGetter().name)
167ec845a215e343cdb3b2e4c7b6aff7b24beb0236bJohn Reck            } else {
168e45b1fd03b524d2b57cc6c222d89076a31a08beaJohn Reck                app(".", it.getGetter().name).app("()")
169119907cd2575c56b1ebf66348b52e67aaf6a88d8John Reck            }
170e45b1fd03b524d2b57cc6c222d89076a31a08beaJohn Reck        }
171998a6d81896df8b662cc10ddeb35087b78b38d72John Reck        is GroupExpr -> kcode("(").app("", it.getWrapped().toCode()).app(")")
172998a6d81896df8b662cc10ddeb35087b78b38d72John Reck        is StaticIdentifierExpr -> kcode(it.getResolvedType().toJavaCode())
173998a6d81896df8b662cc10ddeb35087b78b38d72John Reck        is IdentifierExpr -> kcode(it.localName)
174998a6d81896df8b662cc10ddeb35087b78b38d72John Reck        is MathExpr -> kcode("") {
175aa95a88327d9a3ac8a4a00b065b78ac0f28b3a19John Reck            app("", it.getLeft().toCode())
176aa95a88327d9a3ac8a4a00b065b78ac0f28b3a19John Reck            app(it.getOp())
177aa95a88327d9a3ac8a4a00b065b78ac0f28b3a19John Reck            app("", it.getRight().toCode())
178aa95a88327d9a3ac8a4a00b065b78ac0f28b3a19John Reck        }
179aa95a88327d9a3ac8a4a00b065b78ac0f28b3a19John Reck        is MethodCallExpr -> kcode("") {
180a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck            app("", it.getTarget().toCode())
181a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck            app(".", it.getGetter().name)
182a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck            app("(")
183a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck            var first = true
184a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck            it.getArgs().forEach {
185a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck                apps(if (first) "" else ",", it.toCode())
186a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck                first = false
187a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck            }
188a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck            app(")")
189cd028f336e36b22dbe8cf623eb5bd2361314495cJohn Reck        }
190f9be77940e365036fecd8cc0e491e8545c34e79bJohn Reck        is SymbolExpr -> kcode(it.getText()) // TODO
191f9be77940e365036fecd8cc0e491e8545c34e79bJohn Reck        is TernaryExpr -> kcode("") {
192f9be77940e365036fecd8cc0e491e8545c34e79bJohn Reck            app("", it.getPred().toCode())
193f9be77940e365036fecd8cc0e491e8545c34e79bJohn Reck            app("?", it.getIfTrue().toCode())
194e45b1fd03b524d2b57cc6c222d89076a31a08beaJohn Reck            app(":", it.getIfFalse().toCode())
195e45b1fd03b524d2b57cc6c222d89076a31a08beaJohn Reck        }
196e45b1fd03b524d2b57cc6c222d89076a31a08beaJohn Reck        is ResourceExpr -> kcode("") {
197f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck            app("", it.toJava())
198f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck        }
199f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck        is BracketExpr -> kcode("") {
200f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck            app("", it.getTarget().toCode())
201a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck            val bracketType = it.getAccessor();
202a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck            when (bracketType) {
203a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck                BracketExpr.BracketAccessor.ARRAY -> {
204a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck                    app("[", it.getArg().toCode())
205a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck                    app("]")
206e4267ea4f20740c37c01bfb6aefcf61fddc4566aJohn Reck                }
2074f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck                BracketExpr.BracketAccessor.LIST -> {
208a7090e0cfd7c719a6d4c03aae34f5db98754cbddChris Craik                    app(".get(")
2094f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck                    if (it.argCastsInteger()) {
210fe5e7b7346a54537b980796ceeca66bfdbd05561John Reck                        app("(Integer)")
211ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck                    }
212fe5e7b7346a54537b980796ceeca66bfdbd05561John Reck                    app("", it.getArg().toCode())
213e4267ea4f20740c37c01bfb6aefcf61fddc4566aJohn Reck                    app(")")
214e4267ea4f20740c37c01bfb6aefcf61fddc4566aJohn Reck                }
215e4267ea4f20740c37c01bfb6aefcf61fddc4566aJohn Reck                BracketExpr.BracketAccessor.MAP -> {
2164f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck                    app(".get(", it.getArg().toCode())
2173b20251a355c88193c439f928a84ae69483fb488John Reck                    app(")")
2184f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck                }
2194f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck            }
220e4267ea4f20740c37c01bfb6aefcf61fddc4566aJohn Reck        }
2211125d1fa92ab9f3b8315bbfb72e038b62dfd454bJohn Reck        else -> kcode("//NOT IMPLEMENTED YET")
222e4267ea4f20740c37c01bfb6aefcf61fddc4566aJohn Reck    }
223fe5e7b7346a54537b980796ceeca66bfdbd05561John Reck
2245cdb8f998c58a2226112b36e4c391866346e5e17John Reck}
2250a97330b98dd633b58dcfff405d94476c89e867dJohn Reck
2260a97330b98dd633b58dcfff405d94476c89e867dJohn Reckfun Expr.isVariable() = this is IdentifierExpr && this.isDynamic()
2270a97330b98dd633b58dcfff405d94476c89e867dJohn Reck
2280a97330b98dd633b58dcfff405d94476c89e867dJohn Reckfun Expr.conditionalFlagName(output : Boolean, suffix : String) = "${dirtyFlagName}_${output}$suffix"
229e4267ea4f20740c37c01bfb6aefcf61fddc4566aJohn Reck
2304f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck
2314f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckval Expr.dirtyFlagSet by Delegates.lazy { (expr : Expr) ->
232e4267ea4f20740c37c01bfb6aefcf61fddc4566aJohn Reck    val fs = FlagSet(expr.getInvalidFlags(), expr.getModel().getFlagBucketCount())
233107843de4507b3511006cb9c77b8d0364374385aTom Hudson    expr.getModel().localizeFlag(fs, expr.dirtyFlagName)
234e4267ea4f20740c37c01bfb6aefcf61fddc4566aJohn Reck}
2354f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck
236107843de4507b3511006cb9c77b8d0364374385aTom Hudsonval Expr.invalidateFlagSet by Delegates.lazy { (expr : Expr) ->
2374f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck    val fs = FlagSet(expr.getId())
2384f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck    expr.getModel().localizeFlag(fs, expr.invalidateFlagName)
2394f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck}
240107843de4507b3511006cb9c77b8d0364374385aTom Hudson
2414f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reckval Expr.shouldReadFlagSet by Delegates.lazy { (expr : Expr) ->
242fe5e7b7346a54537b980796ceeca66bfdbd05561John Reck    val fs = FlagSet(expr.getShouldReadFlags(), expr.getModel().getFlagBucketCount())
2434f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck    expr.getModel().localizeFlag(fs, expr.shouldReadFlagName)
244107843de4507b3511006cb9c77b8d0364374385aTom Hudson}
2454f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck
246fe5e7b7346a54537b980796ceeca66bfdbd05561John Reckval Expr.conditionalFlags by Delegates.lazy { (expr : Expr) ->
247fe5e7b7346a54537b980796ceeca66bfdbd05561John Reck    val model = expr.getModel()
248ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck    arrayListOf(model.localizeFlag(FlagSet(expr.getRequirementFlagIndex(false)),
249ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck            "${expr.conditionalFlagPrefix}False"),
250ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck            model.localizeFlag(FlagSet(expr.getRequirementFlagIndex(true)),
251ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck                    "${expr.conditionalFlagPrefix}True"))
252107843de4507b3511006cb9c77b8d0364374385aTom Hudson}
2534f02bf4eef6af47f35c70c4dda5b7b9523d89ca0John Reck
2540e89e2b7bcb2c035e8cee77f93120e7c5617f8d2John Reckfun Expr.getRequirementFlagSet(expected : Boolean) : FlagSet = conditionalFlags[if(expected) 1 else 0]
2550e89e2b7bcb2c035e8cee77f93120e7c5617f8d2John Reck
25623b797ab5151eb2474f3bdd679f2f07bfd723042John Reckfun FlagSet.notEmpty(cb : (suffix : String, value : Long) -> Unit) {
257fe5e7b7346a54537b980796ceeca66bfdbd05561John Reck    buckets.withIndex().forEach {
258ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        if (it.value != 0L) {
259ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck            cb(getWordSuffix(it.index), buckets[it.index])
260ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        }
261fe5e7b7346a54537b980796ceeca66bfdbd05561John Reck    }
26223b797ab5151eb2474f3bdd679f2f07bfd723042John Reck}
26323b797ab5151eb2474f3bdd679f2f07bfd723042John Reck
264e45b1fd03b524d2b57cc6c222d89076a31a08beaJohn Reckfun FlagSet.getBitSuffix(bitIndex : Int) : String {
26518f16e6fba74eda173e1e7c869e6e2e2acc073ffJohn Reck    val word = bitIndex / FlagSet.sBucketSize
266368cdd85268999997fb495cf90c4417221797de0John Reck    return getWordSuffix(word)
267368cdd85268999997fb495cf90c4417221797de0John Reck}
268368cdd85268999997fb495cf90c4417221797de0John Reck
269368cdd85268999997fb495cf90c4417221797de0John Reckfun FlagSet.getWordSuffix(wordIndex : Int) : String {
270e45b1fd03b524d2b57cc6c222d89076a31a08beaJohn Reck    return if(wordIndex == 0) "" else "_${wordIndex}"
271e45b1fd03b524d2b57cc6c222d89076a31a08beaJohn Reck}
272fe5e7b7346a54537b980796ceeca66bfdbd05561John Reck
273ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reckfun FlagSet.localValue(bucketIndex : Int) =
274ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        if (getLocalName() == null) buckets[bucketIndex]
275ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        else "${getLocalName()}${getWordSuffix(bucketIndex)}"
276ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck
277ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reckfun FlagSet.or(other : FlagSet, cb : (suffix : String) -> Unit) {
278fe5e7b7346a54537b980796ceeca66bfdbd05561John Reck    val min = Math.min(buckets.size(), other.buckets.size())
2793b20251a355c88193c439f928a84ae69483fb488John Reck    for (i in 0..(min - 1)) {
280ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        // if these two can match by any chance, call the callback
281a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck        if (intersect(other, i)) {
282e4267ea4f20740c37c01bfb6aefcf61fddc4566aJohn Reck            cb(getWordSuffix(i))
283a5dda645da738da7b4ae15e28fa7d93d3b04b94fJohn Reck        }
284e45b1fd03b524d2b57cc6c222d89076a31a08beaJohn Reck    }
285e45b1fd03b524d2b57cc6c222d89076a31a08beaJohn Reck}
2863b20251a355c88193c439f928a84ae69483fb488John Reck
287d3d8dafc2f61fb118c060720b52684c59303f3dbJohn Reckfun <T> FlagSet.mapOr(other : FlagSet, cb : (suffix : String, index : Int) -> T) : List<T> {
2880d1f634f4b5e1bb37aa51777efb6a68619488d01John Reck    val min = Math.min(buckets.size(), other.buckets.size())
2893b20251a355c88193c439f928a84ae69483fb488John Reck    val result = arrayListOf<T>()
2903b20251a355c88193c439f928a84ae69483fb488John Reck    for (i in 0..(min - 1)) {
2910d1f634f4b5e1bb37aa51777efb6a68619488d01John Reck        // if these two can match by any chance, call the callback
2920d1f634f4b5e1bb37aa51777efb6a68619488d01John Reck        if (intersect(other, i)) {
2936f07a0dc875a9eac67312085a8e0133b9e2f4771John Reck            result.add(cb(getWordSuffix(i), i))
294d41c4d8c732095ae99c955b6b82f7306633004b1Chris Craik        }
29523b797ab5151eb2474f3bdd679f2f07bfd723042John Reck    }
29623b797ab5151eb2474f3bdd679f2f07bfd723042John Reck    return result
297998a6d81896df8b662cc10ddeb35087b78b38d72John Reck}
298998a6d81896df8b662cc10ddeb35087b78b38d72John Reck
299d41c4d8c732095ae99c955b6b82f7306633004b1Chris Craikclass LayoutBinderWriter(val layoutBinder : LayoutBinder) {
300998a6d81896df8b662cc10ddeb35087b78b38d72John Reck    val model = layoutBinder.getModel()
301998a6d81896df8b662cc10ddeb35087b78b38d72John Reck    val mDirtyFlags by Delegates.lazy {
302998a6d81896df8b662cc10ddeb35087b78b38d72John Reck        val fs = FlagSet(BitSet(), model.getFlagBucketCount());
303998a6d81896df8b662cc10ddeb35087b78b38d72John Reck        Arrays.fill(fs.buckets, -1)
304998a6d81896df8b662cc10ddeb35087b78b38d72John Reck        fs.setDynamic(true)
305998a6d81896df8b662cc10ddeb35087b78b38d72John Reck        model.localizeFlag(fs, "mDirtyFlags")
306d41c4d8c732095ae99c955b6b82f7306633004b1Chris Craik        fs
307998a6d81896df8b662cc10ddeb35087b78b38d72John Reck    }
308998a6d81896df8b662cc10ddeb35087b78b38d72John Reck
309998a6d81896df8b662cc10ddeb35087b78b38d72John Reck    val dynamics by Delegates.lazy { model.getExprMap().values().filter { it.isDynamic() } }
310998a6d81896df8b662cc10ddeb35087b78b38d72John Reck    val className = layoutBinder.getClassName()
311998a6d81896df8b662cc10ddeb35087b78b38d72John Reck
312998a6d81896df8b662cc10ddeb35087b78b38d72John Reck    val identifiers by Delegates.lazy {
313998a6d81896df8b662cc10ddeb35087b78b38d72John Reck        dynamics.filter { it is IdentifierExpr }
314998a6d81896df8b662cc10ddeb35087b78b38d72John Reck    }
315998a6d81896df8b662cc10ddeb35087b78b38d72John Reck
316998a6d81896df8b662cc10ddeb35087b78b38d72John Reck    val interfaceName = "${layoutBinder.getInterfaceName()}"
3173e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck
3183e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck    val includedBinders by Delegates.lazy {
3193e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck        layoutBinder.getBindingTargets().filter { it.isBinder() }
3203e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck    }
3213e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck
3223e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck    val variables by Delegates.lazy {
3233e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck        model.getExprMap().values().filterIsInstance(javaClass<IdentifierExpr>()).filter { it.isVariable() }
3243e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck    }
3253e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck
3263e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck    public fun write() : String =
3273e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck            kcode("package ${layoutBinder.getPackage()};") {
3283e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck                nl("import ${layoutBinder.getProjectPackage()}.R;")
3299eb9f6f8cbbbd87d45da8071aa54cb066a797723John Reck                nl("import android.view.View;")
3303e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck                nl("public class ${className} extends com.android.databinding.library.ViewDataBinder implements ${interfaceName} {") {
3313e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck                    tab(declareViews())
3323e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck                    tab(declareVariables())
3333e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck                    tab(declareConstructor())
3343e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck                    tab(declareInvalidateAll())
3353e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck                    tab(declareLog())
3363e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck                    tab(declareSetVariable())
337443a714fa7c0dd07fee3527cc5bc3d3ca1fb7d44John Reck                    tab(variableSettersAndGetters())
3383e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck                    tab(viewGetters())
339998a6d81896df8b662cc10ddeb35087b78b38d72John Reck                    tab(onFieldChange())
340d41c4d8c732095ae99c955b6b82f7306633004b1Chris Craik
341998a6d81896df8b662cc10ddeb35087b78b38d72John Reck                    tab(rebindDirty())
3423e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck
3433e8249568cc428296ac76c7ddce3f0382d40fe5bJohn Reck                    tab(declareDirtyFlags())
34419b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reck                }
34519b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reck                nl("}")
34668bfe0a37a0dcef52abd81688d8520c5d16e1a85John Reck                tab(flagMapping())
3473b20251a355c88193c439f928a84ae69483fb488John Reck                tab("//end")
34819b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reck            }.generate()
34919b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reck
350f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck    fun declareConstructor() = kcode("") {
351f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck        nl("public ${className}(View root) {") {
3523b20251a355c88193c439f928a84ae69483fb488John Reck            tab("super(root, ${model.getObservables().size()});")
353e1628b7c6fc3822fa83cf02028ce8ad67abb0afeJohn Reck            layoutBinder.getBindingTargets().forEach {
354dff9957cc22a1174a4cf91de6609c50934d29434John Reck                if (it.isBinder()) {
355dcba6725e8b9d3eba9ad7a01258d6aa974feafbaJohn Reck                    tab("this.${it.fieldName} = com.android.databinding.library.DataBinder.createBinder(root.findViewById(${it.androidId}), R.layout.${it.getIncludedLayout()});")
356f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck                } else {
357f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck                    tab("this.${it.fieldName} = (${it.getViewClass()}) root.findViewById(${it.androidId});")
358f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck                }
359f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck            }
360f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck            tab("invalidateAll();");
361f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck        }
362f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck        nl("}")
363f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck    }
364786afcb3eec18315ec54987a08814ff28f13d09fJorim Jaggi
3653c2b7fa8c584c5ed56f1bd6ad53f2e87f0a6eb44John Reck    fun declareInvalidateAll() = kcode("") {
366f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck        nl("@Override")
367f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck        nl("public void invalidateAll() {") {
368f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck            val bs = BitSet()
369f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck            bs.set(0, model.getInvalidateableFieldLimit())
370f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck            val fs = FlagSet(bs, mDirtyFlags.buckets.size())
371e1628b7c6fc3822fa83cf02028ce8ad67abb0afeJohn Reck            for (i in (0..(mDirtyFlags.buckets.size() - 1))) {
372e1628b7c6fc3822fa83cf02028ce8ad67abb0afeJohn Reck                tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};")
373e1628b7c6fc3822fa83cf02028ce8ad67abb0afeJohn Reck            }
374fc53ef27793a39e9effd829e9cae02a9ca14147eJohn Reck            includedBinders.forEach { binder ->
37519b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reck                tab("${binder.fieldName}.invalidateAll();")
37619b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reck            }
37719b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reck        }
37819b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reck        nl("}")
3791949e7928eeec22cd3f74b5f763a4eb433238453John Reck    }
380f7d9c1dc84671d4e99657ef071d275700d85bb11John Reck
3813b20251a355c88193c439f928a84ae69483fb488John Reck    fun declareSetVariable() = kcode("") {
3821949e7928eeec22cd3f74b5f763a4eb433238453John Reck        nl("public boolean setVariable(int variableId, Object variable) {") {
3831949e7928eeec22cd3f74b5f763a4eb433238453John Reck            tab("switch(variableId) {") {
38419b6bcfd83eb7fb92ebd06d2fec89e308311f1d0John Reck                variables.forEach {
385f47a594f5250b1914c36423ee6b371f0b8db09d0John Reck                    tab ("case ${it.getName().br()} :") {
386fc53ef27793a39e9effd829e9cae02a9ca14147eJohn Reck                        tab("${it.setterName}((${it.getResolvedType().toJavaCode()}) variable);")
387fc53ef27793a39e9effd829e9cae02a9ca14147eJohn Reck                        tab("return true;")
3883b20251a355c88193c439f928a84ae69483fb488John Reck                    }
3893b20251a355c88193c439f928a84ae69483fb488John Reck                }
3903b20251a355c88193c439f928a84ae69483fb488John Reck            }
39166f0be65a1046f54ddce0498b242c1fa0776b1eaJohn Reck            tab("}")
39266f0be65a1046f54ddce0498b242c1fa0776b1eaJohn Reck            tab("return false;")
393ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        }
394ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        nl("}")
395ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck    }
396ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck
397ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck    fun declareLog() = kcode("") {
398ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        nl("private void log(String msg, long i) {") {
399ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck            tab("""android.util.Log.d("BINDER", msg + ":" + Long.toHexString(i));""")
400ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        }
401ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        nl("}")
402ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck    }
403ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck
404ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck    fun variableSettersAndGetters() = kcode("") {
405ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck        variables.forEach {
406ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck            nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}) {") {
407ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck                if (it.isObservable()) {
408ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck                    tab("updateRegistration(${it.getId()}, ${it.readableUniqueName});");
409ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck                }
410ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck                tab("this.${it.fieldName} = ${it.readableUniqueName};")
411ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck                // set dirty flags!
412ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck                val flagSet = it.invalidateFlagSet
413ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck                mDirtyFlags.mapOr(flagSet) { suffix, index ->
414ba6adf66d3c44c0aa2fd8a224862ff1901d64300John Reck                    tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};")
41523b797ab5151eb2474f3bdd679f2f07bfd723042John Reck                }
41623b797ab5151eb2474f3bdd679f2f07bfd723042John Reck                tab("super.requestRebind();")
41723b797ab5151eb2474f3bdd679f2f07bfd723042John Reck            }
418            nl("}")
419            nl("")
420            nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") {
421                tab("return ${it.fieldName};")
422            }
423            nl("}")
424        }
425    }
426
427    fun onFieldChange() = kcode("") {
428        tab("@Override")
429        tab("protected boolean onFieldChange(int mLocalFieldId, Object object, int fieldId) {") {
430            tab("switch (mLocalFieldId) {") {
431                model.getObservables().forEach {
432                    tab("case ${it.getId()} :") {
433                        tab("return ${it.onChangeName}((${it.getResolvedType().toJavaCode()}) object, fieldId);")
434                    }
435                }
436            }
437            tab("}")
438            tab("return false;")
439        }
440        tab("}")
441
442        model.getObservables().forEach {
443            tab("private boolean ${it.onChangeName}(${it.getResolvedType().toJavaCode()} ${it.readableUniqueName}, int fieldId) {") {
444                tab("switch (fieldId) {", {
445                    val accessedFields: List<FieldAccessExpr> = it.getParents().filterIsInstance(javaClass<FieldAccessExpr>())
446                    accessedFields.filter { it.canBeInvalidated() }
447                            .groupBy { it.getName() }
448                            .forEach {
449                                tab("case ${it.key.br()}:") {
450                                    val field = it.value.first()
451                                    mDirtyFlags.mapOr(field.invalidateFlagSet) { suffix, index ->
452                                        tab("${mDirtyFlags.localValue(index)} |= ${field.invalidateFlagSet.localValue(index)};")
453                                    }
454                                    tab("return true;")
455                                }
456
457                            }
458                    tab("case ${"".br()}:") {
459                        val flagSet = it.invalidateFlagSet
460                        mDirtyFlags.mapOr(flagSet) { suffix, index ->
461                            tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};")
462                        }
463                        tab("return true;")
464                    }
465
466                })
467                tab("}")
468                tab("return false;")
469            }
470            tab("}")
471        }
472    }
473
474    fun declareViews() = kcode("// views") {
475        layoutBinder.getBindingTargets().forEach {
476            nl("private ${it.getViewClass()} ${it.fieldName};")
477        }
478    }
479
480    fun viewGetters() = kcode("// view getters") {
481        layoutBinder.getBindingTargets().forEach {
482            nl("@Override")
483            nl("public ${it.getViewClass()} ${it.getterName}() {") {
484                tab("return ${it.fieldName};")
485            }
486            nl("}")
487        }
488    }
489
490    fun declareVariables() = kcode("// variables") {
491        variables.forEach {
492            nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};")
493        }
494    }
495
496    fun declareDirtyFlags() = kcode("// dirty flag") {
497        model.ext.localizedFlags.forEach { flag ->
498            flag.notEmpty { (suffix, value) ->
499                nl("private")
500                app(" ", if(flag.isDynamic()) null else "static final");
501                app(" ", " ${flag.type} ${flag.getLocalName()}$suffix = $value;")
502            }
503        }
504    }
505
506    fun flagMapping() = kcode("/* flag mapping") {
507        if (model.getFlagMapping() != null) {
508            val mapping = model.getFlagMapping()
509            for (i in mapping.indices) {
510                tab("flag $i: ${mapping[i]}")
511            }
512        }
513        nl("flag mapping end*/")
514    }
515
516    fun rebindDirty() = kcode("") {
517        nl("@Override")
518        nl("public void rebindDirty() {") {
519            val tmpDirtyFlags = FlagSet(mDirtyFlags.buckets)
520            tmpDirtyFlags.setLocalName("dirtyFlags");
521            for (i in (0..mDirtyFlags.buckets.size() - 1)) {
522                tab("${tmpDirtyFlags.type} ${tmpDirtyFlags.localValue(i)} = ${mDirtyFlags.localValue(i)};")
523                tab("${mDirtyFlags.localValue(i)} = 0;")
524            }
525            //tab("""log("dirty flags", mDirtyFlags);""")
526            model.getPendingExpressions().filterNot {it.isVariable()}.forEach {
527                tab("${it.getResolvedType().toJavaCode()} ${it.localName} = ${it.getDefaultValue()};")
528            }
529
530            do {
531                val batch = model.filterShouldRead(model.getPendingExpressions()).toArrayList()
532                val mJustRead = arrayListOf<Expr>()
533                while (!batch.none()) {
534                    val readNow = batch.filter { it.shouldReadNow(mJustRead) }
535                    if (readNow.isEmpty()) {
536                        throw IllegalStateException("do not know what I can read. bailing out ${batch.joinToString("\n")}")
537                    }
538                    Log.d { "new read now. batch size: ${batch.size()}, readNow size: ${readNow.size()}" }
539
540                    readNow.forEach {
541                        nl(readWithDependants(it, mJustRead, batch, tmpDirtyFlags))
542                    }
543                    batch.removeAll(mJustRead)
544                }
545                tab("// batch finished")
546            } while(model.markBitsRead())
547
548            //
549            layoutBinder.getBindingTargets()
550                    .flatMap { it.getBindings() }
551                    .groupBy { it.getExpr() }
552                    .forEach {
553                        val flagSet = it.key.dirtyFlagSet
554                        tab("if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index ->
555                            "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0"
556                        }.joinToString(" || ")
557                        }) {") {
558                            it.value.forEach { binding ->
559                                tab("${binding.toJavaCode(binding.getTarget().fieldName, binding.getExpr().toCode().generate())};")
560                            }
561                        }
562                        tab("}")
563                    }
564            //
565            includedBinders.forEach { binder ->
566                tab("${binder.fieldName}.rebindDirty();")
567            }
568        }
569        nl("}")
570
571    }
572
573    fun readWithDependants(expr : Expr, mJustRead : MutableList<Expr>, batch : MutableList<Expr>, tmpDirtyFlags : FlagSet) : KCode = kcode("") {
574        mJustRead.add(expr)
575        Log.d { expr.getUniqueKey() }
576        val flagSet = expr.shouldReadFlagSet
577        tab("if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index ->
578            "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0"
579        }.joinToString(" || ")
580        }) {") {
581            if (!expr.isVariable()) {
582                // it is not a variable read it.
583                tab("// read ${expr.getUniqueKey()}")
584                // create an if case for all dependencies that might be null
585                val nullables = expr.getDependencies().filter {
586                    it.isMandatory() &&
587                    ClassAnalyzer.isNullable(it.getOther().getResolvedType())
588                }
589                        .map { it.getOther() }
590                if (!expr.isEqualityCheck() && nullables.isNotEmpty()) {
591                    tab ("if ( ${nullables.map { "${it.localName} != null" }.joinToString(" && ")}) {") {
592                        tab("${expr.localName}").app(" = ", expr.toCode(true)).app(";")
593                        //tab("""log("${expr}" + ${expr.localName},0);""")
594                    }
595                    tab("}")
596                } else {
597                    tab("${expr.localName}").app(" = ", expr.toCode(true)).app(";")
598                    //tab("""log("${expr}" + ${expr.localName},0);""")
599                }
600                if (expr.getResolvedType().isObservable()) {
601                    tab("updateRegistration(${expr.getId()}, ${expr.localName});")
602                }
603            }
604
605            // if I am the condition for an expression, set its flag
606            val conditionals = expr.getDependants().filter { !it.isConditional()
607                    && it.getDependant() is TernaryExpr && (it.getDependant() as TernaryExpr).getPred() == expr }
608                    .map { it.getDependant() }
609            if (conditionals.isNotEmpty()) {
610                tab("// setting conditional flags")
611                tab("if (${expr.localName}) {") {
612                    conditionals.forEach {
613                        val set = it.getRequirementFlagSet(true)
614                        mDirtyFlags.mapOr(set) { suffix , index ->
615                            tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};")
616                        }
617                    }
618                }
619                tab("} else {") {
620                    conditionals.forEach {
621                        val set = it.getRequirementFlagSet(false)
622                        mDirtyFlags.mapOr(set) { suffix , index ->
623                            tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};")
624                        }
625                    }
626                } tab("}")
627            }
628
629            val chosen = expr.getDependants().filter {
630                batch.contains(it.getDependant()) && it.getDependant().shouldReadNow(mJustRead)
631            }
632            if (chosen.isNotEmpty()) {
633                chosen.forEach {
634                    nl(readWithDependants(it.getDependant(), mJustRead, batch, tmpDirtyFlags))
635                }
636            }
637        }
638        tab("}")
639    }
640
641    public fun writeInterface() : String =
642        kcode("package ${layoutBinder.getPackage()};") {
643            nl("import android.binding.Bindable;")
644            nl("import com.android.databinding.library.IViewDataBinder;")
645
646            nl("public interface ${interfaceName} extends IViewDataBinder {")
647            variables.forEach {
648                tab("@Bindable")
649                tab("public void ${it.setterName}(${it.getUserDefinedType()} ${it.readableUniqueName});")
650            }
651            layoutBinder.getBindingTargets().forEach {
652                tab("public ${it.getViewClass()} ${it.getterName}();")
653            }
654            nl("}")
655        }.generate()
656}