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