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