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