summaryrefslogtreecommitdiff
path: root/compiler/src/main/kotlin/android/databinding
diff options
context:
space:
mode:
authorYigit Boyar <yboyar@google.com>2017-12-04 12:53:31 -0800
committerYigit Boyar <yboyar@google.com>2017-12-05 15:18:58 -0800
commitfab603376f9ab1573ee214988ebce0d2e68510f1 (patch)
treefb5cb7456d3f38f4bfa4e8c5550d069c0f7dd80b /compiler/src/main/kotlin/android/databinding
parent510d4f6bf7a4d5ff3a87afaef839699abdfe82b4 (diff)
downloaddata-binding-fab603376f9ab1573ee214988ebce0d2e68510f1.tar.gz
Generate data binding mappers in libraries
This CL changes compilation to generate mappers in libraries. Then they are merged in the mapper generated by the app. This is 1 step further to keep generated code in libraries. Now we only have data binding component and BR left. Bug: 68392907 Test: existing tests pass Change-Id: Id3c68429fa157d9f7969890d0a4c8a344899a7f4
Diffstat (limited to 'compiler/src/main/kotlin/android/databinding')
-rw-r--r--compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt2
-rw-r--r--compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriter.kt24
-rw-r--r--compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriterV2.kt365
-rw-r--r--compiler/src/main/kotlin/android/databinding/tool/writer/MergedBindingMapperWriter.kt83
4 files changed, 328 insertions, 146 deletions
diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt
index 2de922e5..8eb31bd1 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/BRWriter.kt
@@ -18,7 +18,7 @@ package android.databinding.tool.writer
import android.databinding.tool.util.StringUtils
-class BRWriter(properties: Set<String>, val useFinal : Boolean) {
+class BRWriter(val properties: Set<String>, val useFinal : Boolean) {
val indexedProps = properties.sorted().withIndex()
fun write(pkg : String): String = "package $pkg;${StringUtils.LINE_SEPARATOR}$klass"
val klass: String by lazy {
diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriter.kt
index fe417da7..44ddbfc3 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriter.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriter.kt
@@ -16,10 +16,12 @@ package android.databinding.tool.writer
import android.databinding.tool.DataBindingCompilerArgs
import android.databinding.tool.LayoutBinder
-class BindingMapperWriter(var pkg : String, var className: String, val layoutBinders : List<LayoutBinder>,
+class BindingMapperWriter(var pkg : String, var className: String,
+ val layoutBinders : List<LayoutBinder>,
val compilerArgs: DataBindingCompilerArgs) {
private val appClassName : String = className
private val testClassName = "Test$className"
+ private val baseMapperClassName = "android.databinding.DataBinderMapper"
val generateAsTest = compilerArgs.isTestVariant && compilerArgs.isApp
val generateTestOverride = !generateAsTest && compilerArgs.isEnabledForTests
init {
@@ -30,10 +32,13 @@ class BindingMapperWriter(var pkg : String, var className: String, val layoutBin
fun write(brWriter : BRWriter) = kcode("") {
nl("package $pkg;")
nl("import ${compilerArgs.modulePackage}.BR;")
- val extends = if (generateAsTest) "extends $appClassName" else ""
+ val extends = if (generateAsTest) {
+ "extends $appClassName"
+ } else {
+ "extends $baseMapperClassName"
+ }
annotateWithGenerated()
block("class $className $extends") {
- nl("final static int TARGET_MIN_SDK = ${compilerArgs.minApi};")
if (generateTestOverride) {
nl("static $appClassName mTestOverride;")
block("static") {
@@ -51,6 +56,7 @@ class BindingMapperWriter(var pkg : String, var className: String, val layoutBin
block("public $className()") {
}
nl("")
+ nl("@Override")
block("public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId)") {
block("switch(layoutId)") {
layoutBinders.groupBy{it.layoutname }.forEach {
@@ -81,7 +87,9 @@ class BindingMapperWriter(var pkg : String, var className: String, val layoutBin
}
nl("return null;")
}
- block("android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View[] views, int layoutId)") {
+ nl("@Override")
+ block("public android.databinding.ViewDataBinding getDataBinder(android.databinding" +
+ ".DataBindingComponent bindingComponent, android.view.View[] views, int layoutId)") {
block("switch(layoutId)") {
layoutBinders.filter{it.isMerge }.groupBy{it.layoutname }.forEach {
val firstVal = it.value[0]
@@ -108,8 +116,8 @@ class BindingMapperWriter(var pkg : String, var className: String, val layoutBin
}
nl("return null;")
}
-
- block("int getLayoutId(String tag)") {
+ nl("@Override")
+ block("public int getLayoutId(String tag)") {
block("if (tag == null)") {
nl("return 0;");
}
@@ -135,8 +143,8 @@ class BindingMapperWriter(var pkg : String, var className: String, val layoutBin
}
nl("return 0;")
}
-
- block("String convertBrIdToString(int id)") {
+ nl("@Override")
+ block("public String convertBrIdToString(int id)") {
block("if (id < 0 || id >= InnerBrLookup.sKeys.length)") {
if (generateTestOverride) {
block("if(mTestOverride != null)") {
diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriterV2.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriterV2.kt
index badf70cb..302769e3 100644
--- a/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriterV2.kt
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/BindingMapperWriterV2.kt
@@ -21,6 +21,7 @@ import android.databinding.tool.ext.L
import android.databinding.tool.ext.N
import android.databinding.tool.ext.S
import android.databinding.tool.ext.T
+import android.databinding.tool.ext.stripNonJava
import android.databinding.tool.reflection.ModelAnalyzer
import android.databinding.tool.store.GenClassInfoLog
import com.squareup.javapoet.AnnotationSpec
@@ -30,22 +31,22 @@ import com.squareup.javapoet.CodeBlock
import com.squareup.javapoet.FieldSpec
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeSpec
+import java.util.Locale
import javax.annotation.Generated
import javax.lang.model.element.Modifier
-class BindingMapperWriterV2(private val pkg: String,
- private val appClassName: String,
- private val genClassInfoLog: GenClassInfoLog,
+class BindingMapperWriterV2(private val genClassInfoLog: GenClassInfoLog,
private val compilerArgs: DataBindingCompilerArgs) {
- private val testClassName = "Test$appClassName"
-
companion object {
private val VIEW_DATA_BINDING = ClassName
.get("android.databinding", "ViewDataBinding")
private val COMPONENT = ClassName
.get("android.databinding", "DataBindingComponent")
+ val DATA_BINDER_MAPPER: ClassName = ClassName
+ .get("android.databinding", "DataBinderMapper")
private val VIEW = ClassName
.get("android.view", "View")
private val OBJECT = ClassName
@@ -56,93 +57,170 @@ class BindingMapperWriterV2(private val pkg: String,
.get("java.lang", "IllegalArgumentException")
private val STRING = ClassName
.get("java.lang", "String")
+ private val INTEGER = ClassName
+ .get("java.lang", "Integer")
+ private val LAYOUT_ID_LOOKUP_MAP_NAME = "INTERNAL_LAYOUT_ID_LOOKUP"
+ private val IMPL_CLASS_NAME = "DataBinderMapperImpl"
+ private val SPARSE_INT_ARRAY =
+ ClassName.get("android.util", "SparseIntArray")
+ private val SPARSE_ARRAY =
+ ClassName.get("android.util", "SparseArray")
}
- private val appTypeSpec = ClassName.bestGuess("$pkg.${this.appClassName}")
- private val generateAsTest = compilerArgs.isTestVariant && compilerArgs.isApp
- private val generateTestOverride = !generateAsTest && compilerArgs.isEnabledForTests
- private val overrideField = FieldSpec.builder(appTypeSpec, "sTestOverride")
- .addModifiers(Modifier.STATIC)
- .build()
-
private val rClassMap = mutableMapOf<String, ClassName>()
- val className = if (generateAsTest) {
- "Test$appClassName"
- } else {
- appClassName
+
+ val pkg : String
+ val className : String
+ init {
+ val generateAsTest = compilerArgs.isTestVariant && compilerArgs.isApp
+ if(generateAsTest) {
+ val testOverride = MergedBindingMapperWriter.TEST_OVERRIDE
+ pkg = testOverride.packageName()
+ className = testOverride.simpleName()
+ } else {
+ pkg = compilerArgs.modulePackage
+ className = IMPL_CLASS_NAME
+ }
}
+ val qualifiedName = "$pkg.$className"
+
private fun getRClass(pkg: String): ClassName {
return rClassMap.getOrPut(pkg) {
ClassName.get(pkg, "R")
}
}
- fun write(brWriter: BRWriter): TypeSpec = TypeSpec.classBuilder(className).apply {
- if (generateAsTest) {
- superclass(appTypeSpec)
+ /**
+ * Layout ids might be non-final while generating the mapper for a library.
+ * For that case, we generate an internal lookup table that converts an R file into a local
+ * known field value.
+ */
+ private val localizedLayoutIdMap = mutableMapOf<String, LayoutId>()
+
+ data class LayoutId(val pkg: String, val id: Int, val layoutName: String,
+ val fieldSpec: FieldSpec)
+
+ private fun getLocalizedLayoutId(pkg: String, layoutName: String): FieldSpec {
+ val layoutId = localizedLayoutIdMap.getOrPut(layoutName) {
+ val fieldName = "LAYOUT_${layoutName.stripNonJava().toUpperCase(Locale.US)}"
+ // must be > 0
+ val id = localizedLayoutIdMap.size + 1
+ val spec = FieldSpec.builder(TypeName.INT, fieldName,
+ Modifier.FINAL, Modifier.STATIC, Modifier.PRIVATE)
+ .initializer(L, id)
+ .build()
+ LayoutId(
+ pkg = pkg,
+ layoutName = layoutName,
+ id = id,
+ fieldSpec = spec)
}
+ return layoutId.fieldSpec
+ }
+
+ fun write(brWriter: BRWriter): TypeSpec = TypeSpec.classBuilder(className).apply {
+ superclass(DATA_BINDER_MAPPER)
+ addModifiers(Modifier.PUBLIC)
if (ModelAnalyzer.getInstance().hasGeneratedAnnotation()) {
addAnnotation(AnnotationSpec.builder(Generated::class.java).apply {
addMember("value", S, "Android Data Binding")
}.build())
}
- val minSdkField = FieldSpec.builder(TypeName.INT, "TARGET_MIN_SDK",
- Modifier.FINAL, Modifier.STATIC).apply {
- initializer(L, compilerArgs.minApi)
- }.build()
- addField(minSdkField)
- if (generateTestOverride) {
- addField(overrideField)
- addStaticBlock(CodeBlock.builder()
- .beginControlFlow("try").apply {
- addStatement("$N = ($T) $T.class.getClassLoader().loadClass($S).newInstance()",
- overrideField, appTypeSpec, appTypeSpec, """$pkg.$testClassName""")
- }.nextControlFlow("catch($T ignored)", ClassName.get(Throwable::class.java))
- .apply {
- addStatement("$N = null", overrideField)
- }
- .endControlFlow()
- .build())
- }
addMethod(generateGetViewDataBinder())
addMethod(generateGetViewArrayDataBinder())
addMethod(generateGetLayoutId())
addMethod(generateConvertBrIdToString())
addType(generateInnerBrLookup(brWriter))
+ // must write this at the end
+ createLocalizedLayoutIds(this)
}.build()
+ private fun createLocalizedLayoutIds(builder: TypeSpec.Builder) {
+ /**
+ * generated code looks like:
+ * private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP =
+ * new SparseIntArray(99);
+ * static {
+ * INTERNAL_LAYOUT_ID_LOOKUP.put(
+ * foo.bar.R.layout.generic_view, LAYOUT_GENERICVIEW);
+ * ... //for all layouts
+ * }
+ */
+ builder.apply {
+ // create fields
+ localizedLayoutIdMap.forEach {
+ addField(it.value.fieldSpec)
+ }
+ // now create conversion hash map
+ // reverse map from ids to values
+ val lookupType = SPARSE_INT_ARRAY
+ val lookupField = FieldSpec.builder(
+ lookupType,
+ LAYOUT_ID_LOOKUP_MAP_NAME)
+ .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
+ .initializer("new $T($L)", lookupType, localizedLayoutIdMap.size)
+ .build()
+ addField(lookupField)
+ addStaticBlock(CodeBlock.builder().apply {
+ localizedLayoutIdMap.values.forEach {
+ addStatement("$N.put($L.layout.$L, $N)", lookupField,
+ getRClass(it.pkg), it.layoutName, it.fieldSpec)
+ }
+ }.build())
+ }
+ }
+
private fun generateInnerBrLookup(brWriter: BRWriter) = TypeSpec
.classBuilder("InnerBrLookup").apply {
+ /**
+ * generated code looks like:
+ * static final SparseArray<String> sKeys = new SparseArray<String>(214);
+ * static {
+ * sKeys.put(foo.bar.BR._all, "_all");
+ * ....//for all BRs
+ */
addModifiers(Modifier.PRIVATE, Modifier.STATIC)
- val keysField = FieldSpec.builder(ArrayTypeName.of(STRING), "sKeys").apply {
+ val keysTypeName = ParameterizedTypeName.get(
+ SPARSE_ARRAY,
+ STRING
+ )
+ val keysField = FieldSpec.builder(keysTypeName, "sKeys").apply {
addModifiers(Modifier.STATIC, Modifier.FINAL)
- val placeholders = brWriter.indexedProps.joinToString(",") { S }
- val args = listOf(ArrayTypeName.of(STRING), "_all") +
- brWriter.indexedProps.map { it.value }
- initializer("new $T{$S, $placeholders}", *(args.toTypedArray()))
+ initializer("new $T($L)", keysTypeName, brWriter.properties.size + 1)
}.build()
addField(keysField)
+ addStaticBlock(CodeBlock.builder().apply {
+ addStatement("$N.put($L.BR.$L, $S)",
+ keysField,
+ compilerArgs.modulePackage,
+ "_all",
+ "_all")
+ brWriter.properties.forEach {
+ addStatement("$N.put($L.BR.$L, $S)",
+ keysField,
+ compilerArgs.modulePackage,
+ it,
+ it)
+ }
+ }.build())
}.build()
private fun generateConvertBrIdToString() = MethodSpec
.methodBuilder("convertBrIdToString").apply {
+ addModifiers(Modifier.PUBLIC)
+ addAnnotation(Override::class.java)
val idParam = ParameterSpec.builder(TypeName.INT, "id").build()
addParameter(idParam)
- returns(BindingMapperWriterV2.STRING)
- beginControlFlow("if($N < 0 || $N >= InnerBrLookup.sKeys.length)",
- idParam, idParam).apply {
- if (generateTestOverride) {
- beginControlFlow("if($N != null)", overrideField).apply {
- addStatement("return $N.convertBrIdToString($N)", overrideField, idParam)
- }.endControlFlow()
- addStatement("return null")
- }
- }.endControlFlow()
- addStatement("return InnerBrLookup.sKeys[$N]", idParam)
+ returns(STRING)
+ val tmpResult = "tmpVal"
+ addStatement("$T $L = InnerBrLookup.sKeys.get($N)", STRING, tmpResult, idParam)
+ addStatement("return $L", tmpResult)
}.build()
private fun generateGetLayoutId() = MethodSpec.methodBuilder("getLayoutId").apply {
+ addModifiers(Modifier.PUBLIC)
+ addAnnotation(Override::class.java)
val tagParam = ParameterSpec.builder(STRING, "tag").build()
addParameter(tagParam)
returns(TypeName.INT)
@@ -164,8 +242,8 @@ class BindingMapperWriterV2(private val pkg: String,
mapping.value.implementations
.map { Pair(it, mapping) }
}
- .groupBy { pair ->
- (pair.first.tag + "_0").hashCode()
+ .groupBy { (first) ->
+ (first.tag + "_0").hashCode()
}
.forEach { code, pairs ->
beginControlFlow("case $L:", code).apply {
@@ -180,17 +258,13 @@ class BindingMapperWriterV2(private val pkg: String,
}.endControlFlow()
}
}.endControlFlow()
- if (generateTestOverride) {
- beginControlFlow("if($N != null)", overrideField).apply {
- addStatement("return $N.getLayoutId($N)", overrideField, tagParam)
- }.endControlFlow()
- }
addStatement("return 0")
}.build()
private fun generateGetViewDataBinder(): MethodSpec {
return MethodSpec.methodBuilder("getDataBinder").apply {
addModifiers(Modifier.PUBLIC)
+ addAnnotation(Override::class.java)
returns(VIEW_DATA_BINDING)
val componentParam = ParameterSpec.builder(COMPONENT, "component").build()
val viewParam = ParameterSpec.builder(VIEW, "view").build()
@@ -198,94 +272,111 @@ class BindingMapperWriterV2(private val pkg: String,
addParameter(componentParam)
addParameter(viewParam)
addParameter(layoutIdParam)
+ val localizedLayoutId = "localizedLayoutId"
+ addStatement("$T $L = $L.get($N)",
+ TypeName.INT,
+ localizedLayoutId,
+ LAYOUT_ID_LOOKUP_MAP_NAME,
+ layoutIdParam)
// output looks like:
+ // localize layout id from R.layout.XY to local private constant
// switch(layoutId)
// case known_layout_id
// verify, generate impl and return
- beginControlFlow("switch($N)", layoutIdParam).apply {
- genClassInfoLog.mappings().forEach { layoutName, info ->
- val rClass = getRClass(info.modulePackage)
- beginControlFlow("case $T.layout.$L :", rClass, layoutName).apply {
- // we should check the tag to decide which layout we need to inflate
- // we do it here because it is ok to pass a non-data-binding layout
- addStatement("final $T tag = $N.getTag()", OBJECT, viewParam)
- beginControlFlow("if(tag == null)").apply {
- addStatement("throw new $T($S)", RUNTIME_EXCEPTION,
- "view must have a tag")
+ beginControlFlow("if($L > 0)", localizedLayoutId).apply {
+ addStatement("final $T tag = $N.getTag()", OBJECT, viewParam)
+ beginControlFlow("if(tag == null)").apply {
+ addStatement("throw new $T($S)", RUNTIME_EXCEPTION,
+ "view must have a tag")
+ }.endControlFlow()
+ beginControlFlow("switch($N)", localizedLayoutId).apply {
+ genClassInfoLog.mappings().forEach { layoutName, info ->
+ val layoutIdField = getLocalizedLayoutId(info.modulePackage, layoutName)
+ beginControlFlow("case $N:", layoutIdField).apply {
+ // we should check the tag to decide which layout we need to inflate
+ // we do it here because it is ok to pass a non-data-binding layout
+ info.implementations.forEach {
+ beginControlFlow("if ($S.equals(tag))",
+ "${it.tag}_0").apply {
+ val binderTypeName = ClassName.bestGuess(it.qualifiedName)
+ if (it.merge) {
+ addStatement("return new $T($N, new $T[]{$N})",
+ binderTypeName, componentParam, VIEW, viewParam)
+ } else {
+ addStatement("return new $T($N, $N)",
+ binderTypeName, componentParam, viewParam)
+ }
+ }.endControlFlow()
+ }
+ addStatement("throw new $T($S + tag)", ILLEGAL_ARG_EXCEPTION,
+ "The tag for $layoutName is invalid. Received: ")
}.endControlFlow()
- info.implementations.forEach {
- beginControlFlow("if ($S.equals(tag))",
- "${it.tag}_0").apply {
- val binderTypeName = ClassName.bestGuess(it.qualifiedName)
- if (it.merge) {
- addStatement("return new $T($N, new $T[]{$N})",
- binderTypeName, componentParam, VIEW, viewParam)
- } else {
- addStatement("return new $T($N, $N)",
- binderTypeName, componentParam, viewParam)
- }
- }.endControlFlow()
- }
- addStatement("throw new $T($S + tag)", ILLEGAL_ARG_EXCEPTION,
- "The tag for $layoutName is invalid. Received: ")
- }.endControlFlow()
- }
- }.endControlFlow()
- if (generateTestOverride) {
- beginControlFlow("if($N != null)", overrideField).apply {
- addStatement("return $N.getDataBinder($N, $N, $N)",
- overrideField, componentParam, viewParam, layoutIdParam)
+ }
}.endControlFlow()
- }
+ }.endControlFlow()
addStatement("return null")
}.build()
}
- private fun generateGetViewArrayDataBinder() = MethodSpec.methodBuilder("getDataBinder").apply {
- addModifiers(Modifier.PUBLIC)
- returns(VIEW_DATA_BINDING)
- val componentParam = ParameterSpec.builder(COMPONENT, "component").build()
- val viewParam = ParameterSpec.builder(ArrayTypeName.of(VIEW), "views").build()
- val layoutIdParam = ParameterSpec.builder(TypeName.INT, "layoutId").build()
- addParameter(componentParam)
- addParameter(viewParam)
- addParameter(layoutIdParam)
- // output looks like:
- // switch(layoutId)
- // case known_layout_id
- // verify, generate impl and return
- beginControlFlow("switch($N)", layoutIdParam).apply {
- genClassInfoLog.mappings().forEach { layoutName, info ->
- val mergeImpls = info.implementations.filter { it.merge }
- if (mergeImpls.isNotEmpty()) {
- val rClass = getRClass(info.modulePackage)
- beginControlFlow("case $T.layout.$L:", rClass, layoutName).apply {
- // we should check the tag to decide which layout we need to inflate
- // we do it here because it is ok to pass non-data-binding view.
- addStatement("final $T tag = $N[0].getTag()", OBJECT, viewParam)
- beginControlFlow("if(tag == null)").apply {
- addStatement("throw new $T($S)", RUNTIME_EXCEPTION,
- "view must have a tag")
- }.endControlFlow()
+ private fun generateGetViewArrayDataBinder() = MethodSpec.methodBuilder("getDataBinder")
+ .apply {
+ addModifiers(Modifier.PUBLIC)
+ addAnnotation(Override::class.java)
+ returns(VIEW_DATA_BINDING)
+ val componentParam = ParameterSpec.builder(COMPONENT, "component").build()
+ val viewParam = ParameterSpec.builder(ArrayTypeName.of(VIEW), "views").build()
+ val layoutIdParam = ParameterSpec.builder(TypeName.INT, "layoutId").build()
+ addParameter(componentParam)
+ addParameter(viewParam)
+ addParameter(layoutIdParam)
+ beginControlFlow("if($N == null || $N.length == 0)",
+ viewParam, viewParam).apply {
+ addStatement("return null")
+ }.endControlFlow()
+
+ val localizedLayoutId = "localizedLayoutId"
+ addStatement("$T $L = $L.get($N)",
+ TypeName.INT,
+ localizedLayoutId,
+ LAYOUT_ID_LOOKUP_MAP_NAME,
+ layoutIdParam)
+ // output looks like:
+ // localize layout id from R.layout.XY to local private constant
+ // switch(layoutId)
+ // case known_layout_id
+ // verify, generate impl and return
- mergeImpls.forEach {
- beginControlFlow("if($S.equals(tag))",
- "${it.tag}_0").apply {
- val binderTypeName = ClassName.bestGuess(it.qualifiedName)
- addStatement("return new $T($N, $N)",
- binderTypeName, componentParam, viewParam)
- }.endControlFlow()
+ beginControlFlow("if($L > 0)", localizedLayoutId).apply {
+ addStatement("final $T tag = $N[0].getTag()", OBJECT, viewParam)
+ beginControlFlow("if(tag == null)").apply {
+ addStatement("throw new $T($S)", RUNTIME_EXCEPTION,
+ "view must have a tag")
+ }.endControlFlow()
+ beginControlFlow("switch($N)", localizedLayoutId).apply {
+ genClassInfoLog.mappings().forEach { layoutName, info ->
+ val mergeImpls = info.implementations.filter { it.merge }
+ if (mergeImpls.isNotEmpty()) {
+ val layoutIdField = getLocalizedLayoutId(
+ info.modulePackage,
+ layoutName)
+ beginControlFlow("case $N:", layoutIdField).apply {
+ mergeImpls.forEach {
+ beginControlFlow("if($S.equals(tag))",
+ "${it.tag}_0").apply {
+ val binderTypeName = ClassName.bestGuess(
+ it.qualifiedName)
+ addStatement("return new $T($N, $N)",
+ binderTypeName, componentParam, viewParam)
+ }.endControlFlow()
+ }
+ addStatement("throw new $T($S + tag)", ILLEGAL_ARG_EXCEPTION,
+ "The tag for $layoutName is invalid. Received: ")
+ }.endControlFlow()
+ }
}
}.endControlFlow()
- }
- }
- }.endControlFlow()
- if (generateTestOverride) {
- beginControlFlow("if($N != null)", overrideField).apply {
- addStatement("return $N.getDataBinder($N, $N, $N)",
- overrideField, componentParam, viewParam, layoutIdParam)
- }.endControlFlow()
- }
- addStatement("return null")
- }.build()
+ }.endControlFlow()
+
+ addStatement("return null")
+ }.build()
}
diff --git a/compiler/src/main/kotlin/android/databinding/tool/writer/MergedBindingMapperWriter.kt b/compiler/src/main/kotlin/android/databinding/tool/writer/MergedBindingMapperWriter.kt
new file mode 100644
index 00000000..b60b6d41
--- /dev/null
+++ b/compiler/src/main/kotlin/android/databinding/tool/writer/MergedBindingMapperWriter.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.databinding.tool.writer
+
+import android.databinding.tool.DataBindingCompilerArgs
+import android.databinding.tool.ext.N
+import android.databinding.tool.ext.S
+import android.databinding.tool.ext.T
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+
+class MergedBindingMapperWriter(private val packages: List<String>,
+ compilerArgs: DataBindingCompilerArgs) {
+ private val generateAsTest = compilerArgs.isTestVariant && compilerArgs.isApp
+ private val generateTestOverride = !generateAsTest && compilerArgs.isEnabledForTests
+ private val overrideField = FieldSpec.builder(BindingMapperWriterV2.DATA_BINDER_MAPPER,
+ "sTestOverride")
+ .addModifiers(Modifier.STATIC)
+ .build()
+
+ companion object {
+ private val APP_CLASS_NAME = "DataBinderMapperImpl"
+ private val TEST_CLASS_NAME = "Test$APP_CLASS_NAME"
+ val MERGED_MAPPER_BASE: ClassName = ClassName.get(
+ "android.databinding",
+ "MergedDataBinderMapper")
+ internal val TEST_OVERRIDE: ClassName = ClassName.get(
+ "android.databinding",
+ TEST_CLASS_NAME)
+ }
+
+ val pkg = "android.databinding"
+ val qualifiedName = "$pkg.$APP_CLASS_NAME"
+
+ fun write() = TypeSpec.classBuilder(APP_CLASS_NAME).apply {
+ superclass(MERGED_MAPPER_BASE)
+ addModifiers(Modifier.PUBLIC)
+ addMethod(MethodSpec.constructorBuilder().apply {
+ packages.forEach { pkg ->
+ val mapper = ClassName.get(pkg, APP_CLASS_NAME)
+ addStatement("addMapper(new $T())", mapper)
+ }
+ if (generateTestOverride) {
+ beginControlFlow("if($N != null)", overrideField).apply {
+ addStatement("addMapper($N)", overrideField)
+ }.endControlFlow()
+ }
+ }.build())
+ if (generateTestOverride) {
+ addField(overrideField)
+ addStaticBlock(CodeBlock.builder()
+ .beginControlFlow("try").apply {
+ addStatement("$N = ($T) $T.class.getClassLoader().loadClass($S).newInstance()",
+ overrideField, BindingMapperWriterV2.DATA_BINDER_MAPPER,
+ BindingMapperWriterV2.DATA_BINDER_MAPPER,
+ TEST_OVERRIDE)
+ }.nextControlFlow("catch($T ignored)", ClassName.get(Throwable::class.java))
+ .apply {
+ addStatement("$N = null", overrideField)
+ }
+ .endControlFlow()
+ .build())
+ }
+ }.build()!!
+}