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