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