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