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