LayoutBinderWriter.kt revision 716ba89e7f459f49ea85070d4710c1d79d715298
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * Unless required by applicable law or agreed to in writing, software 8 * distributed under the License is distributed on an "AS IS" BASIS, 9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 * See the License for the specific language governing permissions and 11 * limitations under the License. 12 */ 13 14package android.databinding.tool.writer 15 16import android.databinding.tool.BindingTarget 17import android.databinding.tool.LayoutBinder 18import android.databinding.tool.expr.Expr 19import android.databinding.tool.expr.ExprModel 20import android.databinding.tool.expr.FieldAccessExpr 21import android.databinding.tool.expr.IdentifierExpr 22import android.databinding.tool.expr.TernaryExpr 23import android.databinding.tool.ext.androidId 24import android.databinding.tool.ext.br 25import android.databinding.tool.ext.joinToCamelCaseAsVar 26import android.databinding.tool.ext.lazy 27import android.databinding.tool.ext.versionedLazy 28import android.databinding.tool.reflection.ModelAnalyzer 29import android.databinding.tool.util.L 30import java.util.ArrayList 31import java.util.Arrays 32import java.util.BitSet 33import java.util.HashMap 34import java.util.HashSet 35import kotlin.properties.Delegates 36 37fun String.stripNonJava() = this.split("[^a-zA-Z0-9]".toRegex()).map{ it.trim() }.joinToCamelCaseAsVar() 38 39enum class Scope { 40 FIELD, 41 METHOD, 42 FLAG, 43 EXECUTE_PENDING_METHOD, 44 CONSTRUCTOR_PARAM 45} 46 47class ExprModelExt { 48 val usedFieldNames = hashMapOf<Scope, MutableSet<String>>(); 49 init { 50 Scope.values().forEach { usedFieldNames[it] = hashSetOf<String>() } 51 } 52 val localizedFlags = arrayListOf<FlagSet>() 53 54 fun localizeFlag(set : FlagSet, name:String) : FlagSet { 55 localizedFlags.add(set) 56 val result = getUniqueName(name, Scope.FLAG) 57 set.setLocalName(result) 58 return set 59 } 60 61 fun getUniqueName(base : String, scope : Scope) : String { 62 var candidate = base 63 var i = 0 64 while (usedFieldNames[scope].contains(candidate)) { 65 i ++ 66 candidate = base + i 67 } 68 usedFieldNames[scope].add(candidate) 69 return candidate 70 } 71} 72 73val ExprModel.ext by Delegates.lazy { target : ExprModel -> 74 ExprModelExt() 75} 76 77fun ExprModel.getUniqueFieldName(base : String) : String = ext.getUniqueName(base, Scope.FIELD) 78fun ExprModel.getUniqueMethodName(base : String) : String = ext.getUniqueName(base, Scope.METHOD) 79fun ExprModel.getUniqueFlagName(base : String) : String = ext.getUniqueName(base, Scope.FLAG) 80fun ExprModel.getConstructorParamName(base : String) : String = ext.getUniqueName(base, Scope.CONSTRUCTOR_PARAM) 81 82fun ExprModel.localizeFlag(set : FlagSet, base : String) : FlagSet = ext.localizeFlag(set, base) 83 84// not necessarily unique. Uniqueness is solved per scope 85val BindingTarget.readableName by Delegates.lazy { target: BindingTarget -> 86 if (target.getId() == null) { 87 "boundView" + indexFromTag(target.getTag()) 88 } else { 89 target.getId().androidId().stripNonJava() 90 } 91} 92 93fun BindingTarget.superConversion(variable : String) : String { 94 if (getResolvedType() != null && getResolvedType().extendsViewStub()) { 95 return "new android.databinding.ViewStubProxy((android.view.ViewStub) ${variable})" 96 } else { 97 return "(${interfaceType}) ${variable}" 98 } 99} 100 101val BindingTarget.fieldName : String by Delegates.lazy { target : BindingTarget -> 102 val name : String 103 if (target.getId() == null) { 104 name = "m${target.readableName}" 105 } else { 106 name = target.readableName 107 } 108 target.getModel().getUniqueFieldName(name) 109} 110 111val BindingTarget.androidId by Delegates.lazy { target : BindingTarget -> 112 "R.id.${target.getId().androidId()}" 113} 114 115val BindingTarget.interfaceType by Delegates.lazy { target : BindingTarget -> 116 if (target.getResolvedType() != null && target.getResolvedType().extendsViewStub()) { 117 "android.databinding.ViewStubProxy" 118 } else { 119 target.getInterfaceType() 120 } 121} 122 123val BindingTarget.constructorParamName by Delegates.lazy { target : BindingTarget -> 124 target.getModel().getConstructorParamName(target.readableName) 125} 126 127// not necessarily unique. Uniqueness is decided per scope 128val Expr.readableName by Delegates.lazy { expr : Expr -> 129 val stripped = "${expr.getUniqueKey().stripNonJava()}" 130 L.d("readableUniqueName for [%s] %s is %s", System.identityHashCode(expr), expr.getUniqueKey(), stripped) 131 stripped 132} 133 134val Expr.fieldName by Delegates.lazy { expr : Expr -> 135 expr.getModel().getUniqueFieldName("m${expr.readableName.capitalize()}") 136} 137 138val Expr.listenerClassName by Delegates.lazy { expr : Expr -> 139 expr.getModel().getUniqueFieldName("${expr.readableName.capitalize()}Impl") 140} 141 142val Expr.oldValueName by Delegates.lazy { expr : Expr -> 143 expr.getModel().getUniqueFieldName("mOld${expr.readableName.capitalize()}") 144} 145 146val Expr.executePendingLocalName by Delegates.lazy { expr : Expr -> 147 "${expr.getModel().ext.getUniqueName(expr.readableName, Scope.EXECUTE_PENDING_METHOD)}" 148} 149 150val Expr.setterName by Delegates.lazy { expr : Expr -> 151 expr.getModel().getUniqueMethodName("set${expr.readableName.capitalize()}") 152} 153 154val Expr.onChangeName by Delegates.lazy { expr : Expr -> 155 expr.getModel().getUniqueMethodName("onChange${expr.readableName.capitalize()}") 156} 157 158val Expr.getterName by Delegates.lazy { expr : Expr -> 159 expr.getModel().getUniqueMethodName("get${expr.readableName.capitalize()}") 160} 161 162val Expr.dirtyFlagName by Delegates.lazy { expr : Expr -> 163 expr.getModel().getUniqueFlagName("sFlag${expr.readableName.capitalize()}") 164} 165 166 167fun Expr.toCode(full : Boolean = false) : KCode = CodeGenUtil.toCode(this, full) 168 169fun Expr.isVariable() = this is IdentifierExpr && this.isDynamic() 170 171fun Expr.conditionalFlagName(output : Boolean, suffix : String) = "${dirtyFlagName}_${output}$suffix" 172 173val Expr.dirtyFlagSet by Delegates.lazy { expr : Expr -> 174 FlagSet(expr.getInvalidFlags(), expr.getModel().getFlagBucketCount()) 175} 176 177val Expr.invalidateFlagSet by Delegates.lazy { expr : Expr -> 178 FlagSet(expr.getId()) 179} 180 181val Expr.shouldReadFlagSet by Delegates.versionedLazy { expr : Expr -> 182 FlagSet(expr.getShouldReadFlags(), expr.getModel().getFlagBucketCount()) 183} 184 185val Expr.conditionalFlags by Delegates.lazy { expr : Expr -> 186 arrayListOf(FlagSet(expr.getRequirementFlagIndex(false)), 187 FlagSet(expr.getRequirementFlagIndex(true))) 188} 189 190fun Expr.getRequirementFlagSet(expected : Boolean) : FlagSet = conditionalFlags[if(expected) 1 else 0] 191 192fun FlagSet.notEmpty(cb : (suffix : String, value : Long) -> Unit) { 193 buckets.withIndex().forEach { 194 if (it.value != 0L) { 195 cb(getWordSuffix(it.index), buckets[it.index]) 196 } 197 } 198} 199 200fun FlagSet.getWordSuffix(wordIndex : Int) : String { 201 return if(wordIndex == 0) "" else "_${wordIndex}" 202} 203 204fun FlagSet.localValue(bucketIndex : Int) = 205 if (getLocalName() == null) binaryCode(bucketIndex) 206 else "${getLocalName()}${getWordSuffix(bucketIndex)}" 207 208fun FlagSet.binaryCode(bucketIndex : Int) = longToBinary(buckets[bucketIndex]) 209 210 211fun longToBinary(l : Long) = 212 "0b${java.lang.Long.toBinaryString(l)}L" 213 214fun <T> FlagSet.mapOr(other : FlagSet, cb : (suffix : String, index : Int) -> T) : List<T> { 215 val min = Math.min(buckets.size(), other.buckets.size()) 216 val result = arrayListOf<T>() 217 for (i in 0..(min - 1)) { 218 // if these two can match by any chance, call the callback 219 if (intersect(other, i)) { 220 result.add(cb(getWordSuffix(i), i)) 221 } 222 } 223 return result 224} 225 226fun indexFromTag(tag : String) : kotlin.Int { 227 val startIndex : kotlin.Int 228 if (tag.startsWith("binding_")) { 229 startIndex = "binding_".length(); 230 } else { 231 startIndex = tag.lastIndexOf('_') + 1 232 } 233 return Integer.parseInt(tag.substring(startIndex)) 234} 235 236class LayoutBinderWriter(val layoutBinder : LayoutBinder) { 237 val model = layoutBinder.getModel() 238 val indices = HashMap<BindingTarget, kotlin.Int>() 239 val mDirtyFlags by Delegates.lazy { 240 val fs = FlagSet(BitSet(), model.getFlagBucketCount()); 241 Arrays.fill(fs.buckets, -1) 242 fs.setDynamic(true) 243 model.localizeFlag(fs, "mDirtyFlags") 244 fs 245 } 246 247 val dynamics by Delegates.lazy { model.getExprMap().values().filter { it.isDynamic() } } 248 val className = layoutBinder.getImplementationName() 249 250 val baseClassName = "${layoutBinder.getClassName()}" 251 252 val includedBinders by Delegates.lazy { 253 layoutBinder.getBindingTargets().filter { it.isBinder() } 254 } 255 256 val variables by Delegates.lazy { 257 model.getExprMap().values().filterIsInstance(javaClass<IdentifierExpr>()).filter { it.isVariable() } 258 } 259 260 val usedVariables by Delegates.lazy { 261 variables.filter {it.isUsed()} 262 } 263 264 public fun write(minSdk : kotlin.Int) : String { 265 layoutBinder.resolveWhichExpressionsAreUsed() 266 calculateIndices(); 267 return kcode("package ${layoutBinder.getPackage()};") { 268 nl("import ${layoutBinder.getModulePackage()}.R;") 269 nl("import ${layoutBinder.getModulePackage()}.BR;") 270 nl("import android.view.View;") 271 val classDeclaration : String 272 if (layoutBinder.hasVariations()) { 273 classDeclaration = "${className} extends ${baseClassName}" 274 } else { 275 classDeclaration = "${className} extends android.databinding.ViewDataBinding" 276 } 277 nl("public class ${classDeclaration} {") { 278 tab(declareIncludeViews()) 279 tab(declareViews()) 280 tab(declareVariables()) 281 tab(declareBoundValues()) 282 tab(declareListeners()) 283 tab(declareConstructor(minSdk)) 284 tab(declareInvalidateAll()) 285 tab(declareHasPendingBindings()) 286 tab(declareLog()) 287 tab(declareSetVariable()) 288 tab(variableSettersAndGetters()) 289 tab(onFieldChange()) 290 291 tab(executePendingBindings()) 292 293 tab(declareListenerImpls()) 294 tab(declareDirtyFlags()) 295 if (!layoutBinder.hasVariations()) { 296 tab(declareFactories()) 297 } 298 } 299 nl("}") 300 tab(flagMapping()) 301 tab("//end") 302 }.generate() 303 } 304 fun calculateIndices() : Unit { 305 val taggedViews = layoutBinder.getBindingTargets().filter{ 306 it.isUsed() && it.getTag() != null && !it.isBinder() 307 } 308 taggedViews.forEach { 309 indices.put(it, indexFromTag(it.getTag())) 310 } 311 val indexStart = maxIndex() + 1 312 layoutBinder.getBindingTargets().filter{ 313 it.isUsed() && !taggedViews.contains(it) 314 }.withIndex().forEach { 315 indices.put(it.value, it.index + indexStart) 316 } 317 } 318 fun declareIncludeViews() = kcode("") { 319 nl("private static final android.databinding.ViewDataBinding.IncludedLayoutIndex[][] sIncludes;") 320 nl("private static final android.util.SparseIntArray sViewsWithIds;") 321 nl("static {") { 322 val hasBinders = layoutBinder.getBindingTargets().firstOrNull{ it.isUsed() && it.isBinder()} != null 323 if (!hasBinders) { 324 tab("sIncludes = null;") 325 } else { 326 val numBindings = layoutBinder.getBindingTargets().filter{ it.isUsed() }.count() 327 tab("sIncludes = new android.databinding.ViewDataBinding.IncludedLayoutIndex[${numBindings}][];") 328 val includeMap = HashMap<BindingTarget, ArrayList<BindingTarget>>() 329 layoutBinder.getBindingTargets().filter{ it.isUsed() && it.isBinder() }.forEach { 330 val includeTag = it.getTag(); 331 val parent = layoutBinder.getBindingTargets().firstOrNull { 332 it.isUsed() && !it.isBinder() && includeTag.equals(it.getTag()) 333 } 334 if (parent == null) { 335 throw IllegalStateException("Could not find parent of include file") 336 } 337 var list = includeMap.get(parent) 338 if (list == null) { 339 list = ArrayList<BindingTarget>() 340 includeMap.put(parent, list) 341 } 342 list.add(it) 343 } 344 345 includeMap.keySet().forEach { 346 val index = indices.get(it) 347 tab("sIncludes[${index}] = new android.databinding.ViewDataBinding.IncludedLayoutIndex[] {") { 348 includeMap.get(it).forEach { 349 val bindingIndex = indices.get(it) 350 val layoutName = it.getIncludedLayout() 351 tab("new android.databinding.ViewDataBinding.IncludedLayoutIndex(\"${layoutName}\", ${bindingIndex}, R.layout.${layoutName}),") 352 } 353 } 354 tab("};") 355 } 356 } 357 val viewsWithIds = layoutBinder.getBindingTargets().filter { 358 it.isUsed() && !it.isBinder() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null)) 359 } 360 if (viewsWithIds.isEmpty()) { 361 tab("sViewsWithIds = null;") 362 } else { 363 tab("sViewsWithIds = new android.util.SparseIntArray();") 364 viewsWithIds.forEach { 365 tab("sViewsWithIds.put(${it.androidId}, ${indices.get(it)});") 366 } 367 } 368 } 369 nl("}") 370 } 371 372 fun maxIndex() : kotlin.Int { 373 val maxIndex = indices.values().max() 374 if (maxIndex == null) { 375 return -1 376 } else { 377 return maxIndex 378 } 379 } 380 381 fun declareConstructor(minSdk : kotlin.Int) = kcode("") { 382 val bindingCount = maxIndex() + 1 383 val parameterType : String 384 val superParam : String 385 if (layoutBinder.isMerge()) { 386 parameterType = "View[]" 387 superParam = "root[0]" 388 } else { 389 parameterType = "View" 390 superParam = "root" 391 } 392 val rootTagsSupported = minSdk >= 14 393 if (layoutBinder.hasVariations()) { 394 nl("") 395 nl("public ${className}(${parameterType} root) {") { 396 tab("this(${superParam}, mapBindings(root, ${bindingCount}, sIncludes, sViewsWithIds));") 397 } 398 nl("}") 399 nl("private ${className}(${parameterType} root, Object[] bindings) {") { 400 tab("super(${superParam}, ${model.getObservables().size()}") { 401 layoutBinder.getSortedTargets().filter { it.getId() != null }.forEach { 402 tab(", ${fieldConversion(it)}") 403 } 404 tab(");") 405 } 406 } 407 } else { 408 nl("public ${baseClassName}(${parameterType} root) {") { 409 tab("super(${superParam}, ${model.getObservables().size()});") 410 tab("final Object[] bindings = mapBindings(root, ${bindingCount}, sIncludes, sViewsWithIds);") 411 } 412 } 413 val taggedViews = layoutBinder.getSortedTargets().filter{it.isUsed()} 414 taggedViews.forEach { 415 if (!layoutBinder.hasVariations() || it.getId() == null) { 416 tab("this.${it.fieldName} = ${fieldConversion(it)};") 417 } 418 if (!it.isBinder()) { 419 if (it.getResolvedType() != null && it.getResolvedType().extendsViewStub()) { 420 tab("this.${it.fieldName}.setContainingBinding(this);") 421 } 422 if (it.supportsTag() && it.getTag() != null && 423 (rootTagsSupported || it.getTag().startsWith("binding_"))) { 424 val originalTag = it.getOriginalTag(); 425 var tagValue = "null" 426 if (originalTag != null) { 427 tagValue = "\"${originalTag}\"" 428 if (originalTag.startsWith("@")) { 429 var packageName = layoutBinder.getModulePackage() 430 if (originalTag.startsWith("@android:")) { 431 packageName = "android" 432 } 433 val slashIndex = originalTag.indexOf('/') 434 val resourceId = originalTag.substring(slashIndex + 1) 435 tagValue = "root.getResources().getString(${packageName}.R.string.${resourceId})" 436 } 437 } 438 tab("this.${it.fieldName}.setTag(${tagValue});") 439 } 440 } 441 } 442 tab("setRootTag(root);") 443 tab("invalidateAll();"); 444 nl("}") 445 } 446 447 fun fieldConversion(target : BindingTarget) : String { 448 if (!target.isUsed()) { 449 return "null" 450 } else { 451 val index = indices.get(target) 452 if (index == null) { 453 throw IllegalStateException("Unknown binding target") 454 } 455 val variableName = "bindings[${index}]" 456 return target.superConversion(variableName) 457 } 458 } 459 460 fun declareInvalidateAll() = kcode("") { 461 nl("@Override") 462 nl("public void invalidateAll() {") { 463 val fs = FlagSet(layoutBinder.getModel().getInvalidateAnyBitSet(), 464 layoutBinder.getModel().getFlagBucketCount()); 465 tab("synchronized(this) {") { 466 for (i in (0..(mDirtyFlags.buckets.size() - 1))) { 467 tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};") 468 } 469 } tab("}") 470 includedBinders.filter{it.isUsed()}.forEach { binder -> 471 tab("${binder.fieldName}.invalidateAll();") 472 } 473 tab("requestRebind();"); 474 } 475 nl("}") 476 } 477 478 fun declareHasPendingBindings() = kcode("") { 479 nl("@Override") 480 nl("public boolean hasPendingBindings() {") { 481 if (mDirtyFlags.buckets.size() > 0) { 482 tab("synchronized(this) {") { 483 val flagCheck = 0.rangeTo(mDirtyFlags.buckets.size() - 1).map { 484 "${mDirtyFlags.localValue(it)} != 0" 485 }.joinToString(" || ") 486 tab("if (${flagCheck}) {") { 487 tab("return true;") 488 } 489 tab("}") 490 } 491 tab("}") 492 } 493 includedBinders.filter{it.isUsed()}.forEach { binder -> 494 tab("if (${binder.fieldName}.hasPendingBindings()) {") { 495 tab("return true;") 496 } 497 tab("}") 498 } 499 tab("return false;") 500 } 501 nl("}") 502 } 503 504 fun declareSetVariable() = kcode("") { 505 nl("public boolean setVariable(int variableId, Object variable) {") { 506 tab("switch(variableId) {") { 507 usedVariables.forEach { 508 tab ("case ${it.getName().br()} :") { 509 tab("${it.setterName}((${it.getResolvedType().toJavaCode()}) variable);") 510 tab("return true;") 511 } 512 } 513 } 514 tab("}") 515 tab("return false;") 516 } 517 nl("}") 518 } 519 520 fun declareLog() = kcode("") { 521 nl("private void log(String msg, long i) {") { 522 tab("""android.util.Log.d("BINDER", msg + ":" + Long.toHexString(i));""") 523 } 524 nl("}") 525 } 526 527 fun variableSettersAndGetters() = kcode("") { 528 variables.filterNot{it.isUsed()}.forEach { 529 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableName}) {") { 530 tab("// not used, ignore") 531 } 532 nl("}") 533 nl("") 534 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 535 tab("return ${it.getDefaultValue()};") 536 } 537 nl("}") 538 } 539 usedVariables.forEach { 540 if (it.getUserDefinedType() != null) { 541 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableName}) {") { 542 if (it.isObservable()) { 543 tab("updateRegistration(${it.getId()}, ${it.readableName});"); 544 } 545 tab("this.${it.fieldName} = ${it.readableName};") 546 // set dirty flags! 547 val flagSet = it.invalidateFlagSet 548 tab("synchronized(this) {") { 549 mDirtyFlags.mapOr(flagSet) { suffix, index -> 550 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 551 } 552 } tab ("}") 553 tab("super.requestRebind();") 554 } 555 nl("}") 556 nl("") 557 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 558 tab("return ${it.fieldName};") 559 } 560 nl("}") 561 } 562 } 563 } 564 565 fun onFieldChange() = kcode("") { 566 nl("@Override") 567 nl("protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {") { 568 tab("switch (localFieldId) {") { 569 model.getObservables().forEach { 570 tab("case ${it.getId()} :") { 571 tab("return ${it.onChangeName}((${it.getResolvedType().toJavaCode()}) object, fieldId);") 572 } 573 } 574 } 575 tab("}") 576 tab("return false;") 577 } 578 nl("}") 579 nl("") 580 581 model.getObservables().forEach { 582 nl("private boolean ${it.onChangeName}(${it.getResolvedType().toJavaCode()} ${it.readableName}, int fieldId) {") { 583 tab("switch (fieldId) {", { 584 val accessedFields: List<FieldAccessExpr> = it.getParents().filterIsInstance(javaClass<FieldAccessExpr>()) 585 accessedFields.filter { it.hasBindableAnnotations() } 586 .groupBy { it.getName() } 587 .forEach { 588 tab("case ${it.key.br()}:") { 589 val field = it.value.first() 590 tab("synchronized(this) {") { 591 mDirtyFlags.mapOr(field.invalidateFlagSet) { suffix, index -> 592 tab("${mDirtyFlags.localValue(index)} |= ${field.invalidateFlagSet.localValue(index)};") 593 } 594 } tab("}") 595 tab("return true;") 596 } 597 598 } 599 tab("case ${"".br()}:") { 600 val flagSet = it.invalidateFlagSet 601 tab("synchronized(this) {") { 602 mDirtyFlags.mapOr(flagSet) { suffix, index -> 603 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 604 } 605 } tab("}") 606 tab("return true;") 607 } 608 609 }) 610 tab("}") 611 tab("return false;") 612 } 613 nl("}") 614 nl("") 615 } 616 } 617 618 fun declareViews() = kcode("// views") { 619 val oneLayout = !layoutBinder.hasVariations(); 620 layoutBinder.getSortedTargets().filter {it.isUsed() && (oneLayout || it.getId() == null)}.forEach { 621 val access : String 622 if (oneLayout && it.getId() != null) { 623 access = "public" 624 } else { 625 access = "private" 626 } 627 nl("${access} final ${it.interfaceType} ${it.fieldName};") 628 } 629 } 630 631 fun declareVariables() = kcode("// variables") { 632 usedVariables.forEach { 633 nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};") 634 } 635 } 636 637 fun declareBoundValues() = kcode("// values") { 638 layoutBinder.getSortedTargets().filter { it.isUsed() } 639 .flatMap { it.getBindings() } 640 .filter { it.requiresOldValue() } 641 .flatMap{ it.getComponentExpressions().toArrayList() } 642 .groupBy { it } 643 .forEach { 644 val expr = it.getKey() 645 nl("private ${expr.getResolvedType().toJavaCode()} ${expr.oldValueName};") 646 } 647 } 648 649 fun declareListeners() = kcode("// listeners") { 650 model.getExprMap().values().filter { 651 it is FieldAccessExpr && it.isListener() 652 }.groupBy { it }.forEach { 653 val expr = it.key as FieldAccessExpr 654 nl("private ${expr.listenerClassName} ${expr.fieldName};") 655 } 656 } 657 658 fun declareDirtyFlags() = kcode("// dirty flag") { 659 model.ext.localizedFlags.forEach { flag -> 660 flag.notEmpty { suffix, value -> 661 nl("private") 662 app(" ", if(flag.isDynamic()) null else "static final"); 663 app(" ", " ${flag.type} ${flag.getLocalName()}$suffix = ${longToBinary(value)};") 664 } 665 } 666 } 667 668 fun flagMapping() = kcode("/* flag mapping") { 669 if (model.getFlagMapping() != null) { 670 val mapping = model.getFlagMapping() 671 for (i in mapping.indices) { 672 tab("flag $i: ${mapping[i]}") 673 } 674 } 675 nl("flag mapping end*/") 676 } 677 678 fun executePendingBindings() = kcode("") { 679 nl("@Override") 680 nl("protected void executeBindings() {") { 681 val tmpDirtyFlags = FlagSet(mDirtyFlags.buckets) 682 tmpDirtyFlags.setLocalName("dirtyFlags"); 683 for (i in (0..mDirtyFlags.buckets.size() - 1)) { 684 tab("${tmpDirtyFlags.type} ${tmpDirtyFlags.localValue(i)} = 0;") 685 } 686 tab("synchronized(this) {") { 687 for (i in (0..mDirtyFlags.buckets.size() - 1)) { 688 tab("${tmpDirtyFlags.localValue(i)} = ${mDirtyFlags.localValue(i)};") 689 tab("${mDirtyFlags.localValue(i)} = 0;") 690 } 691 } tab("}") 692 model.getPendingExpressions().filterNot {!it.canBeEvaluatedToAVariable() || (it.isVariable() && !it.isUsed())}.forEach { 693 tab("${it.getResolvedType().toJavaCode()} ${it.executePendingLocalName} = ${if(it.isVariable()) it.fieldName else it.getDefaultValue()};") 694 } 695 L.d("writing executePendingBindings for %s", className) 696 do { 697 val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList() 698 L.d("batch: %s", batch) 699 val mJustRead = arrayListOf<Expr>() 700 while (!batch.none()) { 701 val readNow = batch.filter { it.shouldReadNow(mJustRead) } 702 if (readNow.isEmpty()) { 703 throw IllegalStateException("do not know what I can read. bailing out ${batch.joinToString("\n")}") 704 } 705 L.d("new read now. batch size: %d, readNow size: %d", batch.size(), readNow.size()) 706 707 readNow.forEach { 708 nl(readWithDependants(it, mJustRead, batch, tmpDirtyFlags)) 709 } 710 batch.removeAll(mJustRead) 711 } 712 tab("// batch finished") 713 } while(model.markBitsRead()) 714 // verify everything is read. 715 val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList() 716 if (batch.isNotEmpty()) { 717 L.e("could not generate code for %s. This might be caused by circular dependencies." 718 + "Please report on b.android.com", layoutBinder.getLayoutname()) 719 } 720 // 721 layoutBinder.getSortedTargets().filter { it.isUsed() } 722 .flatMap { it.getBindings() } 723 .groupBy { it.getExpr() } 724 .forEach { 725 val flagSet = it.key.dirtyFlagSet 726 tab("if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 727 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 728 }.joinToString(" || ") 729 }) {") { 730 it.value.forEach { binding -> 731 tab("// api target ${binding.getMinApi()}") 732 val fieldName : String 733 if (binding.getTarget().getViewClass(). 734 equals(binding.getTarget().getInterfaceType())) { 735 fieldName = "this.${binding.getTarget().fieldName}" 736 } else { 737 fieldName = "((${binding.getTarget().getViewClass()}) this.${binding.getTarget().fieldName})" 738 } 739 val bindingCode = binding.toJavaCode(fieldName) 740 if (binding.getMinApi() > 1) { 741 tab("if(getBuildSdkInt() >= ${binding.getMinApi()}) {") { 742 tab("$bindingCode;") 743 } 744 tab("}") 745 } else { 746 tab("$bindingCode;") 747 } 748 } 749 } 750 tab("}") 751 } 752 753 layoutBinder.getSortedTargets().filter { it.isUsed() } 754 .flatMap { it.getBindings() } 755 .filter { it.requiresOldValue() } 756 .groupBy { it.getExpr() } 757 .forEach { 758 val flagSet = it.key.dirtyFlagSet 759 tab("if (${tmpDirtyFlags.mapOr(flagSet) { suffix, index -> 760 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 761 }.joinToString(" || ") 762 }) {") { 763 it.value.first().getComponentExpressions().forEach { expr -> 764 tab("this.${expr.oldValueName} = ${expr.toCode(false).generate()};") 765 } 766 } 767 tab("}") 768 } 769 includedBinders.filter{it.isUsed()}.forEach { binder -> 770 tab("${binder.fieldName}.executePendingBindings();") 771 } 772 layoutBinder.getSortedTargets().filter{ 773 it.isUsed() && it.getResolvedType() != null && it.getResolvedType().extendsViewStub() 774 }.forEach { 775 tab("if (${it.fieldName}.getBinding() != null) {") { 776 tab("${it.fieldName}.getBinding().executePendingBindings();") 777 } 778 tab("}") 779 } 780 } 781 nl("}") 782 } 783 784 fun readWithDependants(expr : Expr, mJustRead : MutableList<Expr>, batch : MutableList<Expr>, 785 tmpDirtyFlags : FlagSet, inheritedFlags : FlagSet? = null) : KCode = kcode("") { 786 mJustRead.add(expr) 787 L.d("%s / readWithDependants %s", className, expr.getUniqueKey()); 788 val flagSet = expr.shouldReadFlagSet 789 val needsIfWrapper = inheritedFlags == null || !flagSet.bitsEqual(inheritedFlags) 790 L.d("flag set:%s . inherited flags: %s. need another if: %s", flagSet, inheritedFlags, needsIfWrapper); 791 val ifClause = "if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 792 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 793 }.joinToString(" || ") 794 })" 795 796 val readCode = kcode("") { 797 if (expr.canBeEvaluatedToAVariable() && !expr.isVariable()) { 798 // it is not a variable read it. 799 tab("// read ${expr.getUniqueKey()}") 800 // create an if case for all dependencies that might be null 801 val nullables = expr.getDependencies().filter { 802 it.isMandatory() && it.getOther().getResolvedType().isNullable() 803 }.map { it.getOther() } 804 if (!expr.isEqualityCheck() && nullables.isNotEmpty()) { 805 tab ("if ( ${nullables.map { "${it.executePendingLocalName} != null" }.joinToString(" && ")}) {") { 806 tab("${expr.executePendingLocalName}").app(" = ", expr.toCode(true)).app(";") 807 } 808 tab("}") 809 } else { 810 tab("${expr.executePendingLocalName}").app(" = ", expr.toCode(true)).app(";") 811 } 812 if (expr.isObservable()) { 813 tab("updateRegistration(${expr.getId()}, ${expr.executePendingLocalName});") 814 } 815 } 816 817 // if I am the condition for an expression, set its flag 818 val conditionals = expr.getDependants().filter { !it.isConditional() 819 && it.getDependant() is TernaryExpr && (it.getDependant() as TernaryExpr).getPred() == expr } 820 .map { it.getDependant() } 821 if (conditionals.isNotEmpty()) { 822 tab("// setting conditional flags") 823 tab("if (${expr.executePendingLocalName}) {") { 824 conditionals.forEach { 825 val set = it.getRequirementFlagSet(true) 826 mDirtyFlags.mapOr(set) { suffix , index -> 827 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 828 } 829 } 830 } 831 tab("} else {") { 832 conditionals.forEach { 833 val set = it.getRequirementFlagSet(false) 834 mDirtyFlags.mapOr(set) { suffix , index -> 835 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 836 } 837 } 838 } tab("}") 839 } 840 841 val chosen = expr.getDependants().filter { 842 val dependant = it.getDependant() 843 batch.contains(dependant) && 844 dependant.shouldReadFlagSet.andNot(flagSet).isEmpty() && 845 dependant.shouldReadNow(mJustRead) 846 } 847 if (chosen.isNotEmpty()) { 848 val nextInheritedFlags = if (needsIfWrapper) flagSet else inheritedFlags 849 chosen.forEach { 850 nl(readWithDependants(it.getDependant(), mJustRead, batch, tmpDirtyFlags, nextInheritedFlags)) 851 } 852 } 853 } 854 if (needsIfWrapper) { 855 tab(ifClause) { 856 app(" {") 857 nl(readCode) 858 } 859 tab("}") 860 } else { 861 nl(readCode) 862 } 863 } 864 865 fun declareListenerImpls() = kcode("// Listener Stub Implementations") { 866 model.getExprMap().values().filter { 867 it.isUsed() && it is FieldAccessExpr && it.isListener() 868 }.groupBy { it }.forEach { 869 val expr = it.key as FieldAccessExpr 870 val listeners = expr.getListenerTypes() 871 val extends = listeners.firstOrNull{ !it.isInterface() } 872 val extendsImplements = StringBuilder() 873 if (extends != null) { 874 extendsImplements.append("extends ${extends.toJavaCode()} "); 875 } 876 val implements = expr.getListenerTypes().filter{ it.isInterface() }.map { 877 it.toJavaCode() 878 }.joinToString(", ") 879 if (!implements.isEmpty()) { 880 extendsImplements.append("implements ${implements}") 881 } 882 nl("public static class ${expr.listenerClassName} ${extendsImplements} {") { 883 tab("public ${expr.listenerClassName}() {}") 884 if (expr.getChild().isDynamic()) { 885 tab("private ${expr.getChild().getResolvedType().toJavaCode()} value;") 886 tab("public ${expr.listenerClassName} setValue(${expr.getChild().getResolvedType().toJavaCode()} value) {") { 887 tab("this.value = value;") 888 tab("return value == null ? null : this;") 889 } 890 tab("}") 891 } 892 val signatures = HashSet<String>() 893 expr.getListenerMethods().withIndex().forEach { 894 val listener = it.value 895 val calledMethod = expr.getCalledMethods().get(it.index) 896 val parameterTypes = listener.getParameterTypes() 897 val returnType = listener.getReturnType(parameterTypes.toArrayList()) 898 val signature = "public ${returnType} ${listener.getName()}(${ 899 parameterTypes.withIndex().map { 900 "${it.value.toJavaCode()} arg${it.index}" 901 }.joinToString(", ") 902 }) {" 903 if (!signatures.contains(signature)) { 904 signatures.add(signature) 905 tab("@Override") 906 tab(signature) { 907 val obj : String 908 if (expr.getChild().isDynamic()) { 909 obj = "this.value" 910 } else { 911 obj = expr.getChild().toCode(false).generate(); 912 } 913 val returnStr : String 914 if (!returnType.isVoid()) { 915 returnStr = "return " 916 } else { 917 returnStr = "" 918 } 919 val args = parameterTypes.withIndex().map { 920 "arg${it.index}" 921 }.joinToString(", ") 922 tab("${returnStr}${obj}.${calledMethod.getName()}(${args});") 923 } 924 tab("}") 925 } 926 } 927 } 928 nl("}") 929 } 930 } 931 932 fun declareFactories() = kcode("") { 933 nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") { 934 tab("return android.databinding.DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, attachToRoot);") 935 } 936 nl("}") 937 if (!layoutBinder.isMerge()) { 938 nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater) {") { 939 tab("return bind(inflater.inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false));") 940 } 941 nl("}") 942 nl("public static ${baseClassName} bind(android.view.View view) {") { 943 tab("if (!\"${layoutBinder.getTag()}_0\".equals(view.getTag())) {") { 944 tab("throw new RuntimeException(\"view tag isn't correct on view:\" + view.getTag());") 945 } 946 tab("}") 947 tab("return new ${baseClassName}(view);") 948 } 949 nl("}") 950 } 951 } 952 953 /** 954 * When called for a library compilation, we do not generate real implementations 955 */ 956 public fun writeBaseClass(forLibrary : Boolean) : String = 957 kcode("package ${layoutBinder.getPackage()};") { 958 nl("import android.databinding.Bindable;") 959 nl("import android.databinding.DataBindingUtil;") 960 nl("import android.databinding.ViewDataBinding;") 961 nl("public abstract class ${baseClassName} extends ViewDataBinding {") 962 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 963 tab("public final ${it.interfaceType} ${it.fieldName};") 964 } 965 nl("") 966 tab("protected ${baseClassName}(android.view.View root_, int localFieldCount") { 967 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 968 tab(", ${it.interfaceType} ${it.constructorParamName}") 969 } 970 } 971 tab(") {") { 972 tab("super(root_, localFieldCount);") 973 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 974 tab("this.${it.fieldName} = ${it.constructorParamName};") 975 } 976 } 977 tab("}") 978 nl("") 979 variables.forEach { 980 if (it.getUserDefinedType() != null) { 981 val type = ModelAnalyzer.getInstance().applyImports(it.getUserDefinedType(), model.getImports()) 982 tab("public abstract void ${it.setterName}(${type} ${it.readableName});") 983 } 984 } 985 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") { 986 if (forLibrary) { 987 tab("return null;") 988 } else { 989 tab("return DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, attachToRoot);") 990 } 991 } 992 tab("}") 993 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater) {") { 994 if (forLibrary) { 995 tab("return null;") 996 } else { 997 tab("return DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false);") 998 } 999 } 1000 tab("}") 1001 tab("public static ${baseClassName} bind(android.view.View view) {") { 1002 if (forLibrary) { 1003 tab("return null;") 1004 } else { 1005 tab("return (${baseClassName})bind(view, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()});") 1006 } 1007 } 1008 tab("}") 1009 nl("}") 1010 }.generate() 1011} 1012