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