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