ModelAnalyzer.java revision 3d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43
13d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar/*
23d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * Copyright (C) 2014 The Android Open Source Project
33d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar *
43d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
53d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * you may not use this file except in compliance with the License.
63d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * You may obtain a copy of the License at
73d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar *
83d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
93d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar *
103d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * Unless required by applicable law or agreed to in writing, software
113d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
123d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * See the License for the specific language governing permissions and
143d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar * limitations under the License.
153d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar */
163d4bdfeeb2ffd1b2ec8a26abd1f4306295a66a43Yigit Boyar
17085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarpackage com.android.databinding.vo
18085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
19085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.parser.ExprModel
20085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport kotlin.properties.Delegates
21085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport org.w3c.dom.Node
22085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.parser.Expr
23085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport java.util.TreeMap
24085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.parser.VariableRef
25085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.ext.toCamelCase
26085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.ext.joinToCamelCase
27085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.ext.extractAndroidId
28085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.ext.getAndroidIdPath
29085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.util.ClassAnalyzer
30085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.util.getMethodOrField
31085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.util.Log
32085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.ext.joinToCamelCaseAsVar
33085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarimport com.android.databinding.util.isObservable
34085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
35085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar/**
36085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar * Created by yboyar on 11/17/14.
37085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar */
38085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
39085724fd84795ae1631747443c43f77c08e11a28Yigit Boyarclass Binding(val target : BindingTarget, val targetFieldName : String, val expr : Expr) {
40085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    // which variables effect the result of this binding
41085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    // ordered by depth
42085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val relatedVariables : List<Variable> by Delegates.lazy {
43085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        expr.referencedVariables.map {it.variable}
44085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
45085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
46085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val isDirtyExpr : String by Delegates.lazy {
47085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        relatedVariables.map {it.dirtyFlagName}.join(" | ")
48085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
49085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
50085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val setter = "set${targetFieldName.capitalize()}"
51085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
52085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val isDirtyName by Delegates.lazy {"sDirtyFlag${target.resolvedUniqueName}_${targetFieldName.capitalize()}"}
53085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar}
54085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
55085724fd84795ae1631747443c43f77c08e11a28Yigit Boyardata class BindingTarget(val node: Node, val id: String, val viewClass: String) {
56085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val bindings = arrayListOf<Binding>()
57085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    public fun addBinding(fieldName : String, expr : Expr) {
58085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        bindings.add(Binding(this, fieldName, expr))
59085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
60085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val rId:String by Delegates.lazy { "R.id.$idName" }
61085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val idName by Delegates.lazy { id.extractAndroidId() }
62085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    var resolvedUniqueName : String by Delegates.notNull()
63085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val resolvedViewName by Delegates.lazy{"m${resolvedUniqueName}"}
64085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val idNamePath by Delegates.lazy {node.getAndroidIdPath(false).reverse().map {it.extractAndroidId()}}
65085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val findViewById by Delegates.lazy {
66085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        idNamePath.map {"findViewById(R.id.$it)"}.join(".")
67085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
68085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar}
69085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
70085724fd84795ae1631747443c43f77c08e11a28Yigit Boyardata class Variable(val fullName : String, val parent : Variable? = null) {
71085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    var static : Boolean = false
72085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    var localId : Int by Delegates.notNull() // assigned if and only if variable is non-static observable
73085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val path = fullName.split("\\.")
74085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val name = path.last()
75085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    var uniqueName  = fullName // TODO change if we support variables w/ the same name
76085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    var klassName : String by Delegates.notNull()
77085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val children = arrayListOf<Variable>()
78085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val observableChildren by Delegates.lazy {children.filter{it.isObservable}}
79085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val descendents : List<Variable> by Delegates.lazy {children + children.flatMap{it.descendents}}
80085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val observableDescendents : List<Variable> by Delegates.lazy {descendents.filter{it.isObservable}}
81085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    var isRootVariable = false
82085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val defaultValue by Delegates.lazy { ClassAnalyzer.instance.getDefaultValue(klassName)}
83085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val resolvedClass : Class<*> by Delegates.lazy {
84085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        ClassAnalyzer.instance.loadClass(klassName)
85085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
86085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val getter : String by Delegates.lazy {
87085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (parent == null) {
88085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            name
89085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        } else {
90085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            val pair = parent.resolvedClass.getMethodOrField(name)
91085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            klassName = pair.second
92085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            pair.first
93085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
94085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
95085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val fullCamelCase = uniqueName.split("\\.").joinToCamelCase()
96085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    // has to be lazy to get up to date static value
97085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    // TODO handle final pbulic instead!
98085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val localName by Delegates.lazy {if (static) getterGlobal else "${uniqueName.split("\\.").joinToCamelCaseAsVar()}" }
99085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val localIdName by Delegates.lazy { if(localId > -1) "s$fullCamelCase" else "<ERROR>" }//just adding a reference to localId to detect errors
100085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val dirtyFlag : Int by Delegates.lazy { (Math.pow(2.toDouble(), localId.toDouble())).toInt() }
101085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val dirtyFlagName by Delegates.lazy {if (static) "0" else "flag$fullCamelCase"}
102085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val variablePath : List<Variable> by Delegates.lazy {
103085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (parent == null) arrayListOf(this) else parent.variablePath + arrayListOf(this)
104085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
105085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val isDirtyExpr : String by Delegates.lazy {
106085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        variablePath.map {it.dirtyFlagName}.join(" | ")
107085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
108085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
109085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val childrenDirtyFlagsExpr: String by Delegates.lazy {
110085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        (children.map{it.childrenDirtyFlagsExpr } + arrayListOf(dirtyFlagName)).join(" | ")
111085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
112085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
113085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val isDirtyName : String = "isDirty$fullCamelCase"
114085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
115085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val childrenDirtyFlagName: String = "flagHasDirtyChild$fullCamelCase"
116085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
117085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val onChange : String by Delegates.lazy { "onChange$fullCamelCase"}
118085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
119085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val declare : String by Delegates.lazy { "$klassName $name" }
120085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
121085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val declareLocal : String by Delegates.lazy { "$klassName $localName" }
122085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
123085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val isObservable : Boolean by Delegates.lazy {
124085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        resolvedClass.isObservable()
125085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
126085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
127085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val isPrimitive : Boolean by Delegates.lazy {resolvedClass.isPrimitive()}
128085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
129085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val setter by Delegates.lazy {"set$fullCamelCase"}
130085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
131085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val getterGlobal : String by Delegates.lazy {
132085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (parent == null) {
133085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            if (static) {
134085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                klassName
135085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            } else {
136085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                "this.$name"
137085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
138085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        } else {
139085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            "${parent.getterGlobal}.$getter"
140085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
141085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
142085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
143085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val getterOnParentLocal by Delegates.lazy {
144085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (parent == null) {
145085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            if (static) {
146085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                klassName
147085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            } else {
148085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                "this.$name"
149085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
150085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        } else {
151085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            "${parent.localName}.$getter"
152085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
153085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
154085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
155085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    fun markAsStatic() {
156085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        static = true
157085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        children.forEach{it.markAsStatic()}
158085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
159085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar}
160085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
161085724fd84795ae1631747443c43f77c08e11a28Yigit Boyaropen data class VariableDefinition(val name : String, val klass : String, val static : Boolean = false)
162085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
163085724fd84795ae1631747443c43f77c08e11a28Yigit Boyardata class VariableScope(val node : Node, val variables : MutableList<VariableDefinition> = arrayListOf())
164085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
165085724fd84795ae1631747443c43f77c08e11a28Yigit Boyardata class StaticClass(name : String, klass : String) : VariableDefinition(name, klass, true)
166085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
167085724fd84795ae1631747443c43f77c08e11a28Yigit Boyardata class LayoutExprBinding(val root : Node) {
168085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    var exprModel : ExprModel by Delegates.notNull()
169085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val variableScopes = hashMapOf<Node, VariableScope>()
170085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val bindingTargets = arrayListOf<BindingTarget>()
171085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val variables = TreeMap<String, Variable>()
172085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val rootVariables = hashMapOf<String, Variable>()
173085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val dynamicVariables : List<Variable> by Delegates.lazy {variables.values().filterNot{it.static}}
174085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val observableVariables by Delegates.lazy {dynamicVariables.filter{it.isObservable}}
175085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val bindings by Delegates.lazy {
176085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        bindingTargets.flatMap{it.bindings}
177085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
178085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
179085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    val staticVariableScope : VariableScope by Delegates.lazy {
180085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        var existing = variableScopes[root]
181085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (existing == null) {
182085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            existing = VariableScope(root)
183085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            variableScopes.put(root, existing)
184085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
185085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        existing!!
186085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
187085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
188085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    public fun addVariableScope(scope : VariableScope) {
189085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (variableScopes.put(scope.node, scope) != null) {
190085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            throw IllegalArgumentException("duplicate scope created for node ${scope.node}")
191085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
192085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
193085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
194085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    public fun addStaticClass(name : String, klass : String) {
195085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        staticVariableScope.variables.add(VariableDefinition(name, klass, true))
196085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
197085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
198085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    private fun toVariable(ref : VariableRef, klassLookup : Map<String, String>) : Variable {
199085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        var existing = variables[ref.fullName]
200085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (existing != null) {
201085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            return existing
202085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
203085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        var parent = if (ref.parent == null) null else toVariable(ref.parent, klassLookup)
204085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        existing = Variable(ref.fullName, parent)
205085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        if (parent == null) {
206085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            existing.klassName = klassLookup[existing.fullName]!!
207085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            rootVariables[ref.fullName] = existing
208085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            existing.isRootVariable = true
209085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        } else {
210085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            parent!!.children.add(existing)
211085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
212085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
213085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        variables[ref.fullName] = existing
214085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        ref.variable = existing
215085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        return existing
216085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
217085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
218085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    fun pack() {
219085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        exprModel.pack() //seal the model, nothing will be added after this
220085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
221085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        // assign klass to root variables that we can
222085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        val rootVariableKlasses = TreeMap<String, String>()
223085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        val staticVariableKlasses = TreeMap<String, String>()
224085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        variableScopes.forEach {
225085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            it.value.variables.forEach {
226085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                if (rootVariableKlasses.put(it.name, it.klass) != null) {
227085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                    throw IllegalArgumentException("two variables cannot share the same name yet")
228085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                }
229085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                if (it.static) {
230085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                    staticVariableKlasses.put(it.name, it.klass)
231085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                }
232085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
233085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
234085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
235085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        // convert variable references to variables
236085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        exprModel.variables.forEach { entry ->
237085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            Log.d("converting ${entry.value} to variable")
238085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            toVariable(entry.value, rootVariableKlasses)
239085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
240085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
241085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        rootVariables.values().forEach {
242085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            if (staticVariableKlasses.contains(it.name)) {
243085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                it.markAsStatic()
244085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
245085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
246085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
247085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        // assign unique names to views
248085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        bindingTargets.groupBy{ it.idName }.forEach {
249085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            if (it.value.size() == 1) {
250085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                it.value[0].resolvedUniqueName = "${it.value[0].idName.toCamelCase()}"
251085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            } else {
252085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                it.value.forEach {
253085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                    it.resolvedUniqueName = "${it.idNamePath.joinToCamelCase()}"
254085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar                }
255085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            }
256085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
257085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
258085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
259085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    public fun analyzeVariables() {
260085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        variables.forEach {
261085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            Log.d("resolving method for ${it.key}")
262085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            it.value.getter
263085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar            Log.d("resolved method / field for ${it.key} is ${it.value.getter} of type ${it.value.klassName}")
264085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        }
265085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar
266085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        // set ids on observables first then in other variables
267085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        // observable ids are used for observer references which is why they get first ids
268085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        var id = 0
269085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        observableVariables.forEach { it.localId = id++ }
270085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar        dynamicVariables.filterNot { it.isObservable }.forEach { it.localId = id++ }
271085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar    }
272085724fd84795ae1631747443c43f77c08e11a28Yigit Boyar}