LayoutBinderWriter.kt revision 239e15adad52d3a7d77852953a5dd7eee82f7f2c
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.IncludedLayouts 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.IncludedLayouts(${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.setIncludes(${index}, ") { 348 tab ("new String[] {${ 349 includeMap.get(it).map { 350 "\"${it.getIncludedLayout()}\"" 351 }.joinToString(", ") 352 }},") 353 tab("new int[] {${ 354 includeMap.get(it).map { 355 "${indices.get(it)}" 356 }.joinToString(", ") 357 }},") 358 tab("new int[] {${ 359 includeMap.get(it).map { 360 "R.layout.${it.getIncludedLayout()}" 361 }.joinToString(", ") 362 }});") 363 } 364 } 365 } 366 val viewsWithIds = layoutBinder.getBindingTargets().filter { 367 it.isUsed() && !it.isBinder() && (!it.supportsTag() || (it.getId() != null && it.getTag() == null)) 368 } 369 if (viewsWithIds.isEmpty()) { 370 tab("sViewsWithIds = null;") 371 } else { 372 tab("sViewsWithIds = new android.util.SparseIntArray();") 373 viewsWithIds.forEach { 374 tab("sViewsWithIds.put(${it.androidId}, ${indices.get(it)});") 375 } 376 } 377 } 378 nl("}") 379 } 380 381 fun maxIndex() : kotlin.Int { 382 val maxIndex = indices.values().max() 383 if (maxIndex == null) { 384 return -1 385 } else { 386 return maxIndex 387 } 388 } 389 390 fun declareConstructor(minSdk : kotlin.Int) = kcode("") { 391 val bindingCount = maxIndex() + 1 392 val parameterType : String 393 val superParam : String 394 if (layoutBinder.isMerge()) { 395 parameterType = "View[]" 396 superParam = "root[0]" 397 } else { 398 parameterType = "View" 399 superParam = "root" 400 } 401 val rootTagsSupported = minSdk >= 14 402 if (layoutBinder.hasVariations()) { 403 nl("") 404 nl("public ${className}(${parameterType} root) {") { 405 tab("this(${superParam}, mapBindings(root, ${bindingCount}, sIncludes, sViewsWithIds));") 406 } 407 nl("}") 408 nl("private ${className}(${parameterType} root, Object[] bindings) {") { 409 tab("super(${superParam}, ${model.getObservables().size()}") { 410 layoutBinder.getSortedTargets().filter { it.getId() != null }.forEach { 411 tab(", ${fieldConversion(it)}") 412 } 413 tab(");") 414 } 415 } 416 } else { 417 nl("public ${baseClassName}(${parameterType} root) {") { 418 tab("super(${superParam}, ${model.getObservables().size()});") 419 tab("final Object[] bindings = mapBindings(root, ${bindingCount}, sIncludes, sViewsWithIds);") 420 } 421 } 422 val taggedViews = layoutBinder.getSortedTargets().filter{it.isUsed()} 423 taggedViews.forEach { 424 if (!layoutBinder.hasVariations() || it.getId() == null) { 425 tab("this.${it.fieldName} = ${fieldConversion(it)};") 426 } 427 if (!it.isBinder()) { 428 if (it.getResolvedType() != null && it.getResolvedType().extendsViewStub()) { 429 tab("this.${it.fieldName}.setContainingBinding(this);") 430 } 431 if (it.supportsTag() && it.getTag() != null && 432 (rootTagsSupported || it.getTag().startsWith("binding_"))) { 433 val originalTag = it.getOriginalTag(); 434 var tagValue = "null" 435 if (originalTag != null) { 436 tagValue = "\"${originalTag}\"" 437 if (originalTag.startsWith("@")) { 438 var packageName = layoutBinder.getModulePackage() 439 if (originalTag.startsWith("@android:")) { 440 packageName = "android" 441 } 442 val slashIndex = originalTag.indexOf('/') 443 val resourceId = originalTag.substring(slashIndex + 1) 444 tagValue = "root.getResources().getString(${packageName}.R.string.${resourceId})" 445 } 446 } 447 tab("this.${it.fieldName}.setTag(${tagValue});") 448 } 449 } 450 } 451 tab("setRootTag(root);") 452 tab("invalidateAll();"); 453 nl("}") 454 } 455 456 fun fieldConversion(target : BindingTarget) : String { 457 if (!target.isUsed()) { 458 return "null" 459 } else { 460 val index = indices.get(target) 461 if (index == null) { 462 throw IllegalStateException("Unknown binding target") 463 } 464 val variableName = "bindings[${index}]" 465 return target.superConversion(variableName) 466 } 467 } 468 469 fun declareInvalidateAll() = kcode("") { 470 nl("@Override") 471 nl("public void invalidateAll() {") { 472 val fs = FlagSet(layoutBinder.getModel().getInvalidateAnyBitSet(), 473 layoutBinder.getModel().getFlagBucketCount()); 474 tab("synchronized(this) {") { 475 for (i in (0..(mDirtyFlags.buckets.size() - 1))) { 476 tab("${mDirtyFlags.localValue(i)} = ${fs.localValue(i)};") 477 } 478 } tab("}") 479 includedBinders.filter{it.isUsed()}.forEach { binder -> 480 tab("${binder.fieldName}.invalidateAll();") 481 } 482 tab("requestRebind();"); 483 } 484 nl("}") 485 } 486 487 fun declareHasPendingBindings() = kcode("") { 488 nl("@Override") 489 nl("public boolean hasPendingBindings() {") { 490 if (mDirtyFlags.buckets.size() > 0) { 491 tab("synchronized(this) {") { 492 val flagCheck = 0.rangeTo(mDirtyFlags.buckets.size() - 1).map { 493 "${mDirtyFlags.localValue(it)} != 0" 494 }.joinToString(" || ") 495 tab("if (${flagCheck}) {") { 496 tab("return true;") 497 } 498 tab("}") 499 } 500 tab("}") 501 } 502 includedBinders.filter{it.isUsed()}.forEach { binder -> 503 tab("if (${binder.fieldName}.hasPendingBindings()) {") { 504 tab("return true;") 505 } 506 tab("}") 507 } 508 tab("return false;") 509 } 510 nl("}") 511 } 512 513 fun declareSetVariable() = kcode("") { 514 nl("public boolean setVariable(int variableId, Object variable) {") { 515 tab("switch(variableId) {") { 516 usedVariables.forEach { 517 tab ("case ${it.getName().br()} :") { 518 tab("${it.setterName}((${it.getResolvedType().toJavaCode()}) variable);") 519 tab("return true;") 520 } 521 } 522 } 523 tab("}") 524 tab("return false;") 525 } 526 nl("}") 527 } 528 529 fun declareLog() = kcode("") { 530 nl("private void log(String msg, long i) {") { 531 tab("""android.util.Log.d("BINDER", msg + ":" + Long.toHexString(i));""") 532 } 533 nl("}") 534 } 535 536 fun variableSettersAndGetters() = kcode("") { 537 variables.filterNot{it.isUsed()}.forEach { 538 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableName}) {") { 539 tab("// not used, ignore") 540 } 541 nl("}") 542 nl("") 543 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 544 tab("return ${it.getDefaultValue()};") 545 } 546 nl("}") 547 } 548 usedVariables.forEach { 549 if (it.getUserDefinedType() != null) { 550 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableName}) {") { 551 if (it.isObservable()) { 552 tab("updateRegistration(${it.getId()}, ${it.readableName});"); 553 } 554 tab("this.${it.fieldName} = ${it.readableName};") 555 // set dirty flags! 556 val flagSet = it.invalidateFlagSet 557 tab("synchronized(this) {") { 558 mDirtyFlags.mapOr(flagSet) { suffix, index -> 559 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 560 } 561 } tab ("}") 562 tab("super.requestRebind();") 563 } 564 nl("}") 565 nl("") 566 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 567 tab("return ${it.fieldName};") 568 } 569 nl("}") 570 } 571 } 572 } 573 574 fun onFieldChange() = kcode("") { 575 nl("@Override") 576 nl("protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {") { 577 tab("switch (localFieldId) {") { 578 model.getObservables().forEach { 579 tab("case ${it.getId()} :") { 580 tab("return ${it.onChangeName}((${it.getResolvedType().toJavaCode()}) object, fieldId);") 581 } 582 } 583 } 584 tab("}") 585 tab("return false;") 586 } 587 nl("}") 588 nl("") 589 590 model.getObservables().forEach { 591 nl("private boolean ${it.onChangeName}(${it.getResolvedType().toJavaCode()} ${it.readableName}, int fieldId) {") { 592 tab("switch (fieldId) {", { 593 val accessedFields: List<FieldAccessExpr> = it.getParents().filterIsInstance(javaClass<FieldAccessExpr>()) 594 accessedFields.filter { it.hasBindableAnnotations() } 595 .groupBy { it.getName() } 596 .forEach { 597 tab("case ${it.key.br()}:") { 598 val field = it.value.first() 599 tab("synchronized(this) {") { 600 mDirtyFlags.mapOr(field.invalidateFlagSet) { suffix, index -> 601 tab("${mDirtyFlags.localValue(index)} |= ${field.invalidateFlagSet.localValue(index)};") 602 } 603 } tab("}") 604 tab("return true;") 605 } 606 607 } 608 tab("case ${"".br()}:") { 609 val flagSet = it.invalidateFlagSet 610 tab("synchronized(this) {") { 611 mDirtyFlags.mapOr(flagSet) { suffix, index -> 612 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 613 } 614 } tab("}") 615 tab("return true;") 616 } 617 618 }) 619 tab("}") 620 tab("return false;") 621 } 622 nl("}") 623 nl("") 624 } 625 } 626 627 fun declareViews() = kcode("// views") { 628 val oneLayout = !layoutBinder.hasVariations(); 629 layoutBinder.getSortedTargets().filter {it.isUsed() && (oneLayout || it.getId() == null)}.forEach { 630 val access : String 631 if (oneLayout && it.getId() != null) { 632 access = "public" 633 } else { 634 access = "private" 635 } 636 nl("${access} final ${it.interfaceType} ${it.fieldName};") 637 } 638 } 639 640 fun declareVariables() = kcode("// variables") { 641 usedVariables.forEach { 642 nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};") 643 } 644 } 645 646 fun declareBoundValues() = kcode("// values") { 647 layoutBinder.getSortedTargets().filter { it.isUsed() } 648 .flatMap { it.getBindings() } 649 .filter { it.requiresOldValue() } 650 .flatMap{ it.getComponentExpressions().toArrayList() } 651 .groupBy { it } 652 .forEach { 653 val expr = it.getKey() 654 nl("private ${expr.getResolvedType().toJavaCode()} ${expr.oldValueName};") 655 } 656 } 657 658 fun declareListeners() = kcode("// listeners") { 659 model.getExprMap().values().filter { 660 it is FieldAccessExpr && it.isListener() 661 }.groupBy { it }.forEach { 662 val expr = it.key as FieldAccessExpr 663 nl("private ${expr.listenerClassName} ${expr.fieldName};") 664 } 665 } 666 667 fun declareDirtyFlags() = kcode("// dirty flag") { 668 model.ext.localizedFlags.forEach { flag -> 669 flag.notEmpty { suffix, value -> 670 nl("private") 671 app(" ", if(flag.isDynamic()) null else "static final"); 672 app(" ", " ${flag.type} ${flag.getLocalName()}$suffix = ${longToBinary(value)};") 673 } 674 } 675 } 676 677 fun flagMapping() = kcode("/* flag mapping") { 678 if (model.getFlagMapping() != null) { 679 val mapping = model.getFlagMapping() 680 for (i in mapping.indices) { 681 tab("flag $i: ${mapping[i]}") 682 } 683 } 684 nl("flag mapping end*/") 685 } 686 687 fun executePendingBindings() = kcode("") { 688 nl("@Override") 689 nl("protected void executeBindings() {") { 690 val tmpDirtyFlags = FlagSet(mDirtyFlags.buckets) 691 tmpDirtyFlags.setLocalName("dirtyFlags"); 692 for (i in (0..mDirtyFlags.buckets.size() - 1)) { 693 tab("${tmpDirtyFlags.type} ${tmpDirtyFlags.localValue(i)} = 0;") 694 } 695 tab("synchronized(this) {") { 696 for (i in (0..mDirtyFlags.buckets.size() - 1)) { 697 tab("${tmpDirtyFlags.localValue(i)} = ${mDirtyFlags.localValue(i)};") 698 tab("${mDirtyFlags.localValue(i)} = 0;") 699 } 700 } tab("}") 701 model.getPendingExpressions().filterNot {!it.canBeEvaluatedToAVariable() || (it.isVariable() && !it.isUsed())}.forEach { 702 tab("${it.getResolvedType().toJavaCode()} ${it.executePendingLocalName} = ${if(it.isVariable()) it.fieldName else it.getDefaultValue()};") 703 } 704 L.d("writing executePendingBindings for %s", className) 705 do { 706 val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList() 707 L.d("batch: %s", batch) 708 val mJustRead = arrayListOf<Expr>() 709 while (!batch.none()) { 710 val readNow = batch.filter { it.shouldReadNow(mJustRead) } 711 if (readNow.isEmpty()) { 712 throw IllegalStateException("do not know what I can read. bailing out ${batch.joinToString("\n")}") 713 } 714 L.d("new read now. batch size: %d, readNow size: %d", batch.size(), readNow.size()) 715 716 readNow.forEach { 717 nl(readWithDependants(it, mJustRead, batch, tmpDirtyFlags)) 718 } 719 batch.removeAll(mJustRead) 720 } 721 tab("// batch finished") 722 } while(model.markBitsRead()) 723 // verify everything is read. 724 val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList() 725 if (batch.isNotEmpty()) { 726 L.e("could not generate code for %s. This might be caused by circular dependencies." 727 + "Please report on b.android.com", layoutBinder.getLayoutname()) 728 } 729 // 730 layoutBinder.getSortedTargets().filter { it.isUsed() } 731 .flatMap { it.getBindings() } 732 .groupBy { it.getExpr() } 733 .forEach { 734 val flagSet = it.key.dirtyFlagSet 735 tab("if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 736 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 737 }.joinToString(" || ") 738 }) {") { 739 it.value.forEach { binding -> 740 tab("// api target ${binding.getMinApi()}") 741 val fieldName : String 742 if (binding.getTarget().getViewClass(). 743 equals(binding.getTarget().getInterfaceType())) { 744 fieldName = "this.${binding.getTarget().fieldName}" 745 } else { 746 fieldName = "((${binding.getTarget().getViewClass()}) this.${binding.getTarget().fieldName})" 747 } 748 val bindingCode = binding.toJavaCode(fieldName) 749 if (binding.getMinApi() > 1) { 750 tab("if(getBuildSdkInt() >= ${binding.getMinApi()}) {") { 751 tab("$bindingCode;") 752 } 753 tab("}") 754 } else { 755 tab("$bindingCode;") 756 } 757 } 758 } 759 tab("}") 760 } 761 762 layoutBinder.getSortedTargets().filter { it.isUsed() } 763 .flatMap { it.getBindings() } 764 .filter { it.requiresOldValue() } 765 .groupBy { it.getExpr() } 766 .forEach { 767 val flagSet = it.key.dirtyFlagSet 768 tab("if (${tmpDirtyFlags.mapOr(flagSet) { suffix, index -> 769 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 770 }.joinToString(" || ") 771 }) {") { 772 it.value.first().getComponentExpressions().forEach { expr -> 773 tab("this.${expr.oldValueName} = ${expr.toCode(false).generate()};") 774 } 775 } 776 tab("}") 777 } 778 includedBinders.filter{it.isUsed()}.forEach { binder -> 779 tab("${binder.fieldName}.executePendingBindings();") 780 } 781 layoutBinder.getSortedTargets().filter{ 782 it.isUsed() && it.getResolvedType() != null && it.getResolvedType().extendsViewStub() 783 }.forEach { 784 tab("if (${it.fieldName}.getBinding() != null) {") { 785 tab("${it.fieldName}.getBinding().executePendingBindings();") 786 } 787 tab("}") 788 } 789 } 790 nl("}") 791 } 792 793 fun readWithDependants(expr : Expr, mJustRead : MutableList<Expr>, batch : MutableList<Expr>, 794 tmpDirtyFlags : FlagSet, inheritedFlags : FlagSet? = null) : KCode = kcode("") { 795 mJustRead.add(expr) 796 L.d("%s / readWithDependants %s", className, expr.getUniqueKey()); 797 val flagSet = expr.shouldReadFlagSet 798 val needsIfWrapper = inheritedFlags == null || !flagSet.bitsEqual(inheritedFlags) 799 L.d("flag set:%s . inherited flags: %s. need another if: %s", flagSet, inheritedFlags, needsIfWrapper); 800 val ifClause = "if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 801 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 802 }.joinToString(" || ") 803 })" 804 805 val readCode = kcode("") { 806 if (expr.canBeEvaluatedToAVariable() && !expr.isVariable()) { 807 // it is not a variable read it. 808 tab("// read ${expr.getUniqueKey()}") 809 // create an if case for all dependencies that might be null 810 val nullables = expr.getDependencies().filter { 811 it.isMandatory() && it.getOther().getResolvedType().isNullable() 812 }.map { it.getOther() } 813 if (!expr.isEqualityCheck() && nullables.isNotEmpty()) { 814 tab ("if ( ${nullables.map { "${it.executePendingLocalName} != null" }.joinToString(" && ")}) {") { 815 tab("${expr.executePendingLocalName}").app(" = ", expr.toCode(true)).app(";") 816 } 817 tab("}") 818 } else { 819 tab("${expr.executePendingLocalName}").app(" = ", expr.toCode(true)).app(";") 820 } 821 if (expr.isObservable()) { 822 tab("updateRegistration(${expr.getId()}, ${expr.executePendingLocalName});") 823 } 824 } 825 826 // if I am the condition for an expression, set its flag 827 val conditionals = expr.getDependants().filter { !it.isConditional() 828 && it.getDependant() is TernaryExpr && (it.getDependant() as TernaryExpr).getPred() == expr } 829 .map { it.getDependant() } 830 if (conditionals.isNotEmpty()) { 831 tab("// setting conditional flags") 832 tab("if (${expr.executePendingLocalName}) {") { 833 conditionals.forEach { 834 val set = it.getRequirementFlagSet(true) 835 mDirtyFlags.mapOr(set) { suffix , index -> 836 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 837 } 838 } 839 } 840 tab("} else {") { 841 conditionals.forEach { 842 val set = it.getRequirementFlagSet(false) 843 mDirtyFlags.mapOr(set) { suffix , index -> 844 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 845 } 846 } 847 } tab("}") 848 } 849 850 val chosen = expr.getDependants().filter { 851 val dependant = it.getDependant() 852 batch.contains(dependant) && 853 dependant.shouldReadFlagSet.andNot(flagSet).isEmpty() && 854 dependant.shouldReadNow(mJustRead) 855 } 856 if (chosen.isNotEmpty()) { 857 val nextInheritedFlags = if (needsIfWrapper) flagSet else inheritedFlags 858 chosen.forEach { 859 nl(readWithDependants(it.getDependant(), mJustRead, batch, tmpDirtyFlags, nextInheritedFlags)) 860 } 861 } 862 } 863 if (needsIfWrapper) { 864 tab(ifClause) { 865 app(" {") 866 nl(readCode) 867 } 868 tab("}") 869 } else { 870 nl(readCode) 871 } 872 } 873 874 fun declareListenerImpls() = kcode("// Listener Stub Implementations") { 875 model.getExprMap().values().filter { 876 it.isUsed() && it is FieldAccessExpr && it.isListener() 877 }.groupBy { it }.forEach { 878 val expr = it.key as FieldAccessExpr 879 val listeners = expr.getListenerTypes() 880 val extends = listeners.firstOrNull{ !it.isInterface() } 881 val extendsImplements = StringBuilder() 882 if (extends != null) { 883 extendsImplements.append("extends ${extends.toJavaCode()} "); 884 } 885 val implements = expr.getListenerTypes().filter{ it.isInterface() }.map { 886 it.toJavaCode() 887 }.joinToString(", ") 888 if (!implements.isEmpty()) { 889 extendsImplements.append("implements ${implements}") 890 } 891 nl("public static class ${expr.listenerClassName} ${extendsImplements} {") { 892 tab("public ${expr.listenerClassName}() {}") 893 if (expr.getChild().isDynamic()) { 894 tab("private ${expr.getChild().getResolvedType().toJavaCode()} value;") 895 tab("public ${expr.listenerClassName} setValue(${expr.getChild().getResolvedType().toJavaCode()} value) {") { 896 tab("this.value = value;") 897 tab("return value == null ? null : this;") 898 } 899 tab("}") 900 } 901 val signatures = HashSet<String>() 902 expr.getListenerMethods().withIndex().forEach { 903 val listener = it.value 904 val calledMethod = expr.getCalledMethods().get(it.index) 905 val parameterTypes = listener.getParameterTypes() 906 val returnType = listener.getReturnType(parameterTypes.toArrayList()) 907 val signature = "public ${returnType} ${listener.getName()}(${ 908 parameterTypes.withIndex().map { 909 "${it.value.toJavaCode()} arg${it.index}" 910 }.joinToString(", ") 911 }) {" 912 if (!signatures.contains(signature)) { 913 signatures.add(signature) 914 tab("@Override") 915 tab(signature) { 916 val obj : String 917 if (expr.getChild().isDynamic()) { 918 obj = "this.value" 919 } else { 920 obj = expr.getChild().toCode(false).generate(); 921 } 922 val returnStr : String 923 if (!returnType.isVoid()) { 924 returnStr = "return " 925 } else { 926 returnStr = "" 927 } 928 val args = parameterTypes.withIndex().map { 929 "arg${it.index}" 930 }.joinToString(", ") 931 tab("${returnStr}${obj}.${calledMethod.getName()}(${args});") 932 } 933 tab("}") 934 } 935 } 936 } 937 nl("}") 938 } 939 } 940 941 fun declareFactories() = kcode("") { 942 nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") { 943 tab("return android.databinding.DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, attachToRoot);") 944 } 945 nl("}") 946 if (!layoutBinder.isMerge()) { 947 nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater) {") { 948 tab("return bind(inflater.inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false));") 949 } 950 nl("}") 951 nl("public static ${baseClassName} bind(android.view.View view) {") { 952 tab("if (!\"${layoutBinder.getTag()}_0\".equals(view.getTag())) {") { 953 tab("throw new RuntimeException(\"view tag isn't correct on view:\" + view.getTag());") 954 } 955 tab("}") 956 tab("return new ${baseClassName}(view);") 957 } 958 nl("}") 959 } 960 } 961 962 /** 963 * When called for a library compilation, we do not generate real implementations 964 */ 965 public fun writeBaseClass(forLibrary : Boolean) : String = 966 kcode("package ${layoutBinder.getPackage()};") { 967 nl("import android.databinding.Bindable;") 968 nl("import android.databinding.DataBindingUtil;") 969 nl("import android.databinding.ViewDataBinding;") 970 nl("public abstract class ${baseClassName} extends ViewDataBinding {") 971 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 972 tab("public final ${it.interfaceType} ${it.fieldName};") 973 } 974 nl("") 975 tab("protected ${baseClassName}(android.view.View root_, int localFieldCount") { 976 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 977 tab(", ${it.interfaceType} ${it.constructorParamName}") 978 } 979 } 980 tab(") {") { 981 tab("super(root_, localFieldCount);") 982 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 983 tab("this.${it.fieldName} = ${it.constructorParamName};") 984 } 985 } 986 tab("}") 987 nl("") 988 variables.forEach { 989 if (it.getUserDefinedType() != null) { 990 val type = ModelAnalyzer.getInstance().applyImports(it.getUserDefinedType(), model.getImports()) 991 tab("public abstract void ${it.setterName}(${type} ${it.readableName});") 992 } 993 } 994 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") { 995 if (forLibrary) { 996 tab("return null;") 997 } else { 998 tab("return DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, attachToRoot);") 999 } 1000 } 1001 tab("}") 1002 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater) {") { 1003 if (forLibrary) { 1004 tab("return null;") 1005 } else { 1006 tab("return DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false);") 1007 } 1008 } 1009 tab("}") 1010 tab("public static ${baseClassName} bind(android.view.View view) {") { 1011 if (forLibrary) { 1012 tab("return null;") 1013 } else { 1014 tab("return (${baseClassName})bind(view, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()});") 1015 } 1016 } 1017 tab("}") 1018 nl("}") 1019 }.generate() 1020} 1021