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