LayoutBinderWriter.kt revision 23910cf498c35704a03ba4f3889de2ab97ccbe21
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 val declaredOnly = variables.filter { !it.isUsed() && it.isDeclared() }; 547 declaredOnly.forEachIndexed { i, identifierExpr -> 548 tab ("case ${identifierExpr.getName().br()} :") { 549 if (i == declaredOnly.size() - 1) { 550 tab("return true;") 551 } 552 } 553 } 554 } 555 tab("}") 556 tab("return false;") 557 } 558 nl("}") 559 } 560 561 fun declareLog() = kcode("") { 562 nl("private void log(String msg, long i) {") { 563 tab("""android.util.Log.d("BINDER", msg + ":" + Long.toHexString(i));""") 564 } 565 nl("}") 566 } 567 568 fun variableSettersAndGetters() = kcode("") { 569 variables.filterNot{it.isUsed()}.forEach { 570 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableName}) {") { 571 tab("// not used, ignore") 572 } 573 nl("}") 574 nl("") 575 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 576 tab("return ${it.getDefaultValue()};") 577 } 578 nl("}") 579 } 580 usedVariables.forEach { 581 if (it.getUserDefinedType() != null) { 582 nl("public void ${it.setterName}(${it.getResolvedType().toJavaCode()} ${it.readableName}) {") { 583 if (it.isObservable()) { 584 tab("updateRegistration(${it.getId()}, ${it.readableName});"); 585 } 586 tab("this.${it.fieldName} = ${it.readableName};") 587 // set dirty flags! 588 val flagSet = it.invalidateFlagSet 589 tab("synchronized(this) {") { 590 mDirtyFlags.mapOr(flagSet) { suffix, index -> 591 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 592 } 593 } tab ("}") 594 tab("super.requestRebind();") 595 } 596 nl("}") 597 nl("") 598 nl("public ${it.getResolvedType().toJavaCode()} ${it.getterName}() {") { 599 tab("return ${it.fieldName};") 600 } 601 nl("}") 602 } 603 } 604 } 605 606 fun onFieldChange() = kcode("") { 607 nl("@Override") 608 nl("protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {") { 609 tab("switch (localFieldId) {") { 610 model.getObservables().forEach { 611 tab("case ${it.getId()} :") { 612 tab("return ${it.onChangeName}((${it.getResolvedType().toJavaCode()}) object, fieldId);") 613 } 614 } 615 } 616 tab("}") 617 tab("return false;") 618 } 619 nl("}") 620 nl("") 621 622 model.getObservables().forEach { 623 nl("private boolean ${it.onChangeName}(${it.getResolvedType().toJavaCode()} ${it.readableName}, int fieldId) {") { 624 tab("switch (fieldId) {", { 625 val accessedFields: List<FieldAccessExpr> = it.getParents().filterIsInstance(javaClass<FieldAccessExpr>()) 626 accessedFields.filter { it.hasBindableAnnotations() } 627 .groupBy { it.getName() } 628 .forEach { 629 tab("case ${it.key.br()}:") { 630 val field = it.value.first() 631 tab("synchronized(this) {") { 632 mDirtyFlags.mapOr(field.invalidateFlagSet) { suffix, index -> 633 tab("${mDirtyFlags.localValue(index)} |= ${field.invalidateFlagSet.localValue(index)};") 634 } 635 } tab("}") 636 tab("return true;") 637 } 638 639 } 640 tab("case ${"".br()}:") { 641 val flagSet = it.invalidateFlagSet 642 tab("synchronized(this) {") { 643 mDirtyFlags.mapOr(flagSet) { suffix, index -> 644 tab("${mDirtyFlags.getLocalName()}$suffix |= ${flagSet.localValue(index)};") 645 } 646 } tab("}") 647 tab("return true;") 648 } 649 650 }) 651 tab("}") 652 tab("return false;") 653 } 654 nl("}") 655 nl("") 656 } 657 } 658 659 fun declareViews() = kcode("// views") { 660 val oneLayout = !layoutBinder.hasVariations(); 661 layoutBinder.getSortedTargets().filter {it.isUsed() && (oneLayout || it.getId() == null)}.forEach { 662 val access : String 663 if (oneLayout && it.getId() != null) { 664 access = "public" 665 } else { 666 access = "private" 667 } 668 nl("${access} final ${it.interfaceType} ${it.fieldName};") 669 } 670 } 671 672 fun declareVariables() = kcode("// variables") { 673 usedVariables.forEach { 674 nl("private ${it.getResolvedType().toJavaCode()} ${it.fieldName};") 675 } 676 } 677 678 fun declareBoundValues() = kcode("// values") { 679 layoutBinder.getSortedTargets().filter { it.isUsed() } 680 .flatMap { it.getBindings() } 681 .filter { it.requiresOldValue() } 682 .flatMap{ it.getComponentExpressions().toArrayList() } 683 .groupBy { it } 684 .forEach { 685 val expr = it.getKey() 686 nl("private ${expr.getResolvedType().toJavaCode()} ${expr.oldValueName};") 687 } 688 } 689 690 fun declareListeners() = kcode("// listeners") { 691 model.getExprMap().values().filter { 692 it is ListenerExpr 693 }.groupBy { it }.forEach { 694 val expr = it.key as ListenerExpr 695 nl("private ${expr.listenerClassName} ${expr.fieldName};") 696 } 697 } 698 699 fun declareDirtyFlags() = kcode("// dirty flag") { 700 model.ext.localizedFlags.forEach { flag -> 701 flag.notEmpty { suffix, value -> 702 nl("private") 703 app(" ", if(flag.isDynamic()) null else "static final"); 704 app(" ", " ${flag.type} ${flag.getLocalName()}$suffix = ${longToBinary(value)};") 705 } 706 } 707 } 708 709 fun flagMapping() = kcode("/* flag mapping") { 710 if (model.getFlagMapping() != null) { 711 val mapping = model.getFlagMapping() 712 for (i in mapping.indices) { 713 tab("flag $i: ${mapping[i]}") 714 } 715 } 716 nl("flag mapping end*/") 717 } 718 719 fun executePendingBindings() = kcode("") { 720 nl("@Override") 721 nl("protected void executeBindings() {") { 722 val tmpDirtyFlags = FlagSet(mDirtyFlags.buckets) 723 tmpDirtyFlags.setLocalName("dirtyFlags"); 724 for (i in (0..mDirtyFlags.buckets.size() - 1)) { 725 tab("${tmpDirtyFlags.type} ${tmpDirtyFlags.localValue(i)} = 0;") 726 } 727 tab("synchronized(this) {") { 728 for (i in (0..mDirtyFlags.buckets.size() - 1)) { 729 tab("${tmpDirtyFlags.localValue(i)} = ${mDirtyFlags.localValue(i)};") 730 tab("${mDirtyFlags.localValue(i)} = 0;") 731 } 732 } tab("}") 733 model.getPendingExpressions().filterNot { !it.canBeEvaluatedToAVariable() || (it.isVariable() && !it.isUsed()) }.forEach { 734 tab("${it.getResolvedType().toJavaCode()} ${it.executePendingLocalName} = ${if (it.isVariable()) it.fieldName else it.getDefaultValue()};") 735 } 736 L.d("writing executePendingBindings for %s", className) 737 do { 738 val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList() 739 val justRead = arrayListOf<Expr>() 740 L.d("batch: %s", batch) 741 while (!batch.none()) { 742 val readNow = batch.filter { it.shouldReadNow(justRead) } 743 if (readNow.isEmpty()) { 744 throw IllegalStateException("do not know what I can read. bailing out ${batch.joinToString("\n")}") 745 } 746 L.d("new read now. batch size: %d, readNow size: %d", batch.size(), readNow.size()) 747 nl(readWithDependants(readNow, justRead, batch, tmpDirtyFlags)) 748 batch.removeAll(justRead) 749 } 750 tab("// batch finished") 751 } while (model.markBitsRead()) 752 // verify everything is read. 753 val batch = ExprModel.filterShouldRead(model.getPendingExpressions()).toArrayList() 754 if (batch.isNotEmpty()) { 755 L.e("could not generate code for %s. This might be caused by circular dependencies." 756 + "Please report on b.android.com. %d %s %s", layoutBinder.getLayoutname(), 757 batch.size(), batch.get(0), batch.get(0).toCode().generate()) 758 } 759 // 760 layoutBinder.getSortedTargets().filter { it.isUsed() } 761 .flatMap { it.getBindings() } 762 .groupBy { 763 "${tmpDirtyFlags.mapOr(it.getExpr().dirtyFlagSet) { suffix, index -> 764 "(${tmpDirtyFlags.localValue(index)} & ${it.getExpr().dirtyFlagSet.localValue(index)}) != 0" 765 }.joinToString(" || ") }" 766 }.forEach { 767 tab("if (${it.key}) {") { 768 it.value.groupBy { Math.max(1, it.getMinApi()) }.forEach { 769 val setterValues = kcode("") { 770 it.value.forEach { binding -> 771 val fieldName: String 772 if (binding.getTarget().getViewClass(). 773 equals(binding.getTarget().getInterfaceType())) { 774 fieldName = "this.${binding.getTarget().fieldName}" 775 } else { 776 fieldName = "((${binding.getTarget().getViewClass()}) this.${binding.getTarget().fieldName})" 777 } 778 tab(binding.toJavaCode(fieldName, "this.mBindingComponent")).app(";") 779 } 780 } 781 tab("// api target ${it.key}") 782 if (it.key > 1) { 783 tab("if(getBuildSdkInt() >= ${it.key}) {") { 784 app("", setterValues) 785 } 786 tab("}") 787 } else { 788 app("", setterValues) 789 } 790 } 791 } 792 tab("}") 793 } 794 795 796 layoutBinder.getSortedTargets().filter { it.isUsed() } 797 .flatMap { it.getBindings() } 798 .filter { it.requiresOldValue() } 799 .groupBy {"${tmpDirtyFlags.mapOr(it.getExpr().dirtyFlagSet) { suffix, index -> 800 "(${tmpDirtyFlags.localValue(index)} & ${it.getExpr().dirtyFlagSet.localValue(index)}) != 0" 801 }.joinToString(" || ") 802 }"}.forEach { 803 tab("if (${it.key}) {") { 804 it.value.groupBy { it.getExpr() }.map { it.value.first() }.forEach { 805 it.getComponentExpressions().forEach { expr -> 806 tab("this.${expr.oldValueName} = ${expr.toCode().generate()};") 807 } 808 } 809 } 810 tab("}") 811 } 812 includedBinders.filter{it.isUsed()}.forEach { binder -> 813 tab("${binder.fieldName}.executePendingBindings();") 814 } 815 layoutBinder.getSortedTargets().filter{ 816 it.isUsed() && it.getResolvedType() != null && it.getResolvedType().extendsViewStub() 817 }.forEach { 818 tab("if (${it.fieldName}.getBinding() != null) {") { 819 tab("${it.fieldName}.getBinding().executePendingBindings();") 820 } 821 tab("}") 822 } 823 } 824 nl("}") 825 } 826 827 fun readWithDependants(expressionList: List<Expr>, justRead: MutableList<Expr>, 828 batch: MutableList<Expr>, tmpDirtyFlags: FlagSet, 829 inheritedFlags: FlagSet? = null) : KCode = kcode("") { 830 expressionList.groupBy { it.shouldReadFlagSet }.forEach { 831 val flagSet = it.key 832 val needsIfWrapper = inheritedFlags == null || !flagSet.bitsEqual(inheritedFlags) 833 val expressions = it.value 834 val ifClause = "if (${tmpDirtyFlags.mapOr(flagSet){ suffix, index -> 835 "(${tmpDirtyFlags.localValue(index)} & ${flagSet.localValue(index)}) != 0" 836 }.joinToString(" || ") 837 })" 838 val readCode = kcode("") { 839 val dependants = ArrayList<Expr>() 840 expressions.groupBy { condition(it) }.forEach { 841 val condition = it.key 842 val assignedValues = it.value.filter { 843 it.canBeEvaluatedToAVariable() && !it.isVariable() 844 } 845 if (!assignedValues.isEmpty()) { 846 val assignment = kcode("") { 847 assignedValues.forEach { expr: Expr -> 848 tab("// read ${expr.getUniqueKey()}") 849 tab("${expr.executePendingLocalName}").app(" = ", expr.toFullCode()).app(";") 850 } 851 } 852 if (condition != null) { 853 tab("if (${condition}) {") { 854 app("", assignment) 855 } 856 tab ("}") 857 } else { 858 app("", assignment) 859 } 860 it.value.filter { it.isObservable() }.forEach { expr: Expr -> 861 tab("updateRegistration(${expr.getId()}, ${expr.executePendingLocalName});") 862 } 863 } 864 865 it.value.forEach { expr: Expr -> 866 justRead.add(expr) 867 L.d("%s / readWithDependants %s", className, expr.getUniqueKey()); 868 L.d("flag set:%s . inherited flags: %s. need another if: %s", flagSet, inheritedFlags, needsIfWrapper); 869 870 // if I am the condition for an expression, set its flag 871 val conditionals = expr.getDependants().filter { 872 !it.isConditional() && it.getDependant() is TernaryExpr && 873 (it.getDependant() as TernaryExpr).getPred() == expr 874 }.map { it.getDependant() } 875 if (conditionals.isNotEmpty()) { 876 tab("// setting conditional flags") 877 tab("if (${expr.executePendingLocalName}) {") { 878 conditionals.forEach { 879 val set = it.getRequirementFlagSet(true) 880 mDirtyFlags.mapOr(set) { suffix, index -> 881 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 882 } 883 } 884 } 885 tab("} else {") { 886 conditionals.forEach { 887 val set = it.getRequirementFlagSet(false) 888 mDirtyFlags.mapOr(set) { suffix, index -> 889 tab("${tmpDirtyFlags.localValue(index)} |= ${set.localValue(index)};") 890 } 891 } 892 } 893 tab("}") 894 } 895 896 val chosen = expr.getDependants().filter { 897 val dependant = it.getDependant() 898 batch.contains(dependant) && 899 dependant.shouldReadFlagSet.andNot(flagSet).isEmpty() && 900 dependant.shouldReadNow(justRead) 901 } 902 if (chosen.isNotEmpty()) { 903 dependants.addAll(chosen.map { it.getDependant() }) 904 } 905 } 906 } 907 if (dependants.isNotEmpty()) { 908 val nextInheritedFlags = if (needsIfWrapper) flagSet else inheritedFlags 909 nl(readWithDependants(dependants, justRead, batch, tmpDirtyFlags, nextInheritedFlags)) 910 } 911 } 912 913 if (needsIfWrapper) { 914 tab(ifClause) { 915 app(" {") 916 app("", readCode) 917 } 918 tab("}") 919 } else { 920 app("", readCode) 921 } 922 } 923 } 924 925 fun condition(expr : Expr) : String? { 926 if (expr.canBeEvaluatedToAVariable() && !expr.isVariable()) { 927 // create an if case for all dependencies that might be null 928 val nullables = expr.getDependencies().filter { 929 it.isMandatory() && it.getOther().getResolvedType().isNullable() 930 }.map { it.getOther() } 931 if (!expr.isEqualityCheck() && nullables.isNotEmpty()) { 932 return "${nullables.map { "${it.executePendingLocalName} != null" }.joinToString(" && ")}" 933 } else { 934 return null 935 } 936 } else { 937 return null 938 } 939 } 940 941 fun declareListenerImpls() = kcode("// Listener Stub Implementations") { 942 model.getExprMap().values().filter { 943 it.isUsed() && it is ListenerExpr 944 }.groupBy { it }.forEach { 945 val expr = it.key as ListenerExpr 946 val listenerType = expr.getResolvedType(); 947 val extendsImplements : String 948 if (listenerType.isInterface()) { 949 extendsImplements = "implements" 950 } else { 951 extendsImplements = "extends" 952 } 953 nl("public static class ${expr.listenerClassName} ${extendsImplements} ${listenerType.getCanonicalName()}{") { 954 if (expr.getChild().isDynamic()) { 955 tab("private ${expr.getChild().getResolvedType().toJavaCode()} value;") 956 tab("public ${expr.listenerClassName} setValue(${expr.getChild().getResolvedType().toJavaCode()} value) {") { 957 tab("this.value = value;") 958 tab("return value == null ? null : this;") 959 } 960 tab("}") 961 } 962 val listenerMethod = expr.getMethod() 963 val parameterTypes = listenerMethod.getParameterTypes() 964 val returnType = listenerMethod.getReturnType(parameterTypes.toArrayList()) 965 tab("@Override") 966 tab("public ${returnType} ${listenerMethod.getName()}(${ 967 parameterTypes.withIndex().map { 968 "${it.value.toJavaCode()} arg${it.index}" 969 }.joinToString(", ") 970 }) {") { 971 val obj : String 972 if (expr.getChild().isDynamic()) { 973 obj = "this.value" 974 } else { 975 obj = expr.getChild().toCode().generate(); 976 } 977 val returnStr : String 978 if (!returnType.isVoid()) { 979 returnStr = "return " 980 } else { 981 returnStr = "" 982 } 983 val args = parameterTypes.withIndex().map { 984 "arg${it.index}" 985 }.joinToString(", ") 986 tab("${returnStr}${obj}.${expr.getName()}(${args});") 987 } 988 tab("}") 989 } 990 nl("}") 991 } 992 } 993 994 fun declareFactories() = kcode("") { 995 nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") { 996 tab("return inflate(inflater, root, attachToRoot, android.databinding.DataBindingUtil.getDefaultComponent());") 997 } 998 nl("}") 999 nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot, android.databinding.DataBindingComponent bindingComponent) {") { 1000 tab("return android.databinding.DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, root, attachToRoot, bindingComponent);") 1001 } 1002 nl("}") 1003 if (!layoutBinder.isMerge()) { 1004 nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater) {") { 1005 tab("return inflate(inflater, android.databinding.DataBindingUtil.getDefaultComponent());") 1006 } 1007 nl("}") 1008 nl("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.databinding.DataBindingComponent bindingComponent) {") { 1009 tab("return bind(inflater.inflate(${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false), bindingComponent);") 1010 } 1011 nl("}") 1012 nl("public static ${baseClassName} bind(android.view.View view) {") { 1013 tab("return bind(view, android.databinding.DataBindingUtil.getDefaultComponent());") 1014 } 1015 nl("}") 1016 nl("public static ${baseClassName} bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) {") { 1017 tab("if (!\"${layoutBinder.getTag()}_0\".equals(view.getTag())) {") { 1018 tab("throw new RuntimeException(\"view tag isn't correct on view:\" + view.getTag());") 1019 } 1020 tab("}") 1021 tab("return new ${baseClassName}(bindingComponent, view);") 1022 } 1023 nl("}") 1024 } 1025 } 1026 1027 /** 1028 * When called for a library compilation, we do not generate real implementations 1029 */ 1030 public fun writeBaseClass(forLibrary : Boolean) : String = 1031 kcode("package ${layoutBinder.getPackage()};") { 1032 nl("import android.databinding.Bindable;") 1033 nl("import android.databinding.DataBindingUtil;") 1034 nl("import android.databinding.ViewDataBinding;") 1035 nl("public abstract class ${baseClassName} extends ViewDataBinding {") 1036 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 1037 tab("public final ${it.interfaceType} ${it.fieldName};") 1038 } 1039 nl("") 1040 tab("protected ${baseClassName}(android.databinding.DataBindingComponent bindingComponent, android.view.View root_, int localFieldCount") { 1041 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 1042 tab(", ${it.interfaceType} ${it.constructorParamName}") 1043 } 1044 } 1045 tab(") {") { 1046 tab("super(bindingComponent, root_, localFieldCount);") 1047 layoutBinder.getSortedTargets().filter{it.getId() != null}.forEach { 1048 tab("this.${it.fieldName} = ${it.constructorParamName};") 1049 } 1050 } 1051 tab("}") 1052 nl("") 1053 variables.forEach { 1054 if (it.getUserDefinedType() != null) { 1055 val type = ModelAnalyzer.getInstance().applyImports(it.getUserDefinedType(), model.getImports()) 1056 tab("public abstract void ${it.setterName}(${type} ${it.readableName});") 1057 } 1058 } 1059 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot) {") { 1060 tab("return inflate(inflater, root, attachToRoot, android.databinding.DataBindingUtil.getDefaultComponent());") 1061 } 1062 tab("}") 1063 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater) {") { 1064 tab("return inflate(inflater, android.databinding.DataBindingUtil.getDefaultComponent());") 1065 } 1066 tab("}") 1067 tab("public static ${baseClassName} bind(android.view.View view) {") { 1068 if (forLibrary) { 1069 tab("return null;") 1070 } else { 1071 tab("return bind(view, android.databinding.DataBindingUtil.getDefaultComponent());") 1072 } 1073 } 1074 tab("}") 1075 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.view.ViewGroup root, boolean attachToRoot, 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()}, root, attachToRoot, bindingComponent);") 1080 } 1081 } 1082 tab("}") 1083 tab("public static ${baseClassName} inflate(android.view.LayoutInflater inflater, android.databinding.DataBindingComponent bindingComponent) {") { 1084 if (forLibrary) { 1085 tab("return null;") 1086 } else { 1087 tab("return DataBindingUtil.<${baseClassName}>inflate(inflater, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()}, null, false, bindingComponent);") 1088 } 1089 } 1090 tab("}") 1091 tab("public static ${baseClassName} bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) {") { 1092 if (forLibrary) { 1093 tab("return null;") 1094 } else { 1095 tab("return (${baseClassName})bind(bindingComponent, view, ${layoutBinder.getModulePackage()}.R.layout.${layoutBinder.getLayoutname()});") 1096 } 1097 } 1098 tab("}") 1099 nl("}") 1100 }.generate() 1101} 1102