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