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