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