summaryrefslogtreecommitdiff
path: root/compiler/src/main/java/android/databinding
diff options
context:
space:
mode:
authorYigit Boyar <yboyar@google.com>2018-10-04 14:58:19 -0700
committerTreeHugger Robot <treehugger-gerrit@google.com>2018-10-10 18:52:58 +0000
commit498a422582d70a9033cd83283011a558545a1825 (patch)
treeabca9ba3ffd04c3026af63bb9d0c6d4c31a5c258 /compiler/src/main/java/android/databinding
parent26957e600f8f6476c5152e128226d43288d4f3cd (diff)
downloaddata-binding-498a422582d70a9033cd83283011a558545a1825.tar.gz
Auto jetify during class finding
This CL fixes an issue in data binding where if a binding adapter from a dependency was writting in android.support and app is using androidX, we would not be able to use it. Now, the annotation class finder automatically jetifies a class if it cannot find it. This CL also updates BindingAdapterStore to ignore all android.databinding.adapter* artifacts which might lurk into dependencies if the dependency was compiled with V1. This CL also includes a small optimization in ModelAnalyzer where we would always query a known class even if it is not there (e.g. LiveData). This avoids such unnecssary queries. Bug: 117266727 Test: MultiModuleTestApp Change-Id: Ic8079160f8a5aa3c14c40f673a6339c090fa9f9a
Diffstat (limited to 'compiler/src/main/java/android/databinding')
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/CachedClass.java41
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java141
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java15
-rw-r--r--compiler/src/main/java/android/databinding/tool/store/BindingAdapterStore.kt71
-rw-r--r--compiler/src/main/java/android/databinding/tool/store/SetterStore.java10
5 files changed, 197 insertions, 81 deletions
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/CachedClass.java b/compiler/src/main/java/android/databinding/tool/reflection/CachedClass.java
new file mode 100644
index 00000000..84866093
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/reflection/CachedClass.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 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.reflection;
+
+import com.android.annotations.Nullable;
+
+/**
+ * A simple data structure that avoids searching the same class again and again.
+ * Especially useful since things like isObservable might look for classes
+ * (e.g. LiveData) repeatedly that are not mandatory.
+ */
+public abstract class CachedClass {
+ private boolean mSearched = false;
+ @Nullable
+ private ModelClass klass;
+
+ @Nullable
+ public ModelClass get() {
+ if (!mSearched) {
+ klass = find();
+ mSearched = true;
+ }
+ return klass;
+ }
+
+ @Nullable
+ abstract ModelClass find();
+}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
index 5b48b79f..f650b9c5 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java
@@ -19,12 +19,12 @@ import android.databinding.tool.Context;
import android.databinding.tool.LibTypes;
import android.databinding.tool.util.L;
import android.databinding.tool.util.Preconditions;
+import com.android.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -44,18 +44,76 @@ public abstract class ModelAnalyzer {
private static final String VIEW_STUB_CLASS_NAME = "android.view.ViewStub";
private List<ModelClass> mListTypes;
- private ModelClass mMapType;
- private ModelClass mStringType;
- private ModelClass mObjectType;
- private ModelClass mObservableType;
- private ModelClass mObservableListType;
- private ModelClass mObservableMapType;
- private ModelClass mLiveDataType;
- private ModelClass mMutableLiveDataType;
+ private CachedClass mMapType = new CachedClass() {
+ @Override
+ ModelClass find() {
+ return loadClassErasure(MAP_CLASS_NAME);
+ }
+ };
+ private CachedClass mStringType = new CachedClass() {
+ @Override
+ ModelClass find() {
+ return findClass(STRING_CLASS_NAME, null);
+ }
+ };
+ private CachedClass mObjectType = new CachedClass() {
+
+ @Override
+ ModelClass find() {
+ return findClass(OBJECT_CLASS_NAME, null);
+
+ }
+ };
+ private CachedClass mObservableType = new CachedClass() {
+ @Override
+ ModelClass find() {
+ return findClass(libTypes.getObservable(), null);
+ }
+ };
+ private CachedClass mObservableListType = new CachedClass() {
+ @Override
+ ModelClass find() {
+ return loadClassErasure(libTypes.getObservableList());
+ }
+ };
+ private CachedClass mObservableMapType = new CachedClass() {
+
+ @Override
+ ModelClass find() {
+ return loadClassErasure(libTypes.getObservableMap());
+ }
+ };
+ private CachedClass mLiveDataType = new CachedClass() {
+ @Override
+ ModelClass find() {
+ return loadClassErasure(libTypes.getLiveData());
+ }
+ };
+ private CachedClass mMutableLiveDataType = new CachedClass() {
+ @Override
+ ModelClass find() {
+ return loadClassErasure(libTypes.getMutableLiveData());
+ }
+ };
private List<ModelClass> mObservableFieldTypes;
- private ModelClass mViewBindingType;
- private ModelClass mViewStubType;
- private ModelClass mViewStubProxyType;
+ private CachedClass mViewBindingType = new CachedClass() {
+ @Override
+ ModelClass find() {
+ return findClass(libTypes.getViewDataBinding(), null);
+ }
+ };
+ private CachedClass mViewStubType = new CachedClass() {
+ @Override
+ ModelClass find() {
+ return findClass(VIEW_STUB_CLASS_NAME, null);
+ }
+ };
+ private CachedClass mViewStubProxyType = new CachedClass() {
+ @Override
+ ModelClass find() {
+ return findClass(libTypes.getViewStubProxy(), null);
+ }
+ };
public final LibTypes libTypes;
@@ -223,75 +281,46 @@ public abstract class ModelAnalyzer {
}
public ModelClass getMapType() {
- if (mMapType == null) {
- mMapType = loadClassErasure(MAP_CLASS_NAME);
- }
- return mMapType;
+ return mMapType.get();
}
ModelClass getStringType() {
- if (mStringType == null) {
- mStringType = findClass(STRING_CLASS_NAME, null);
- }
- return mStringType;
+ return mStringType.get();
}
ModelClass getObjectType() {
- if (mObjectType == null) {
- mObjectType = findClass(OBJECT_CLASS_NAME, null);
- }
- return mObjectType;
+ return mObjectType.get();
}
ModelClass getObservableType() {
- if (mObservableType == null) {
- mObservableType = findClass(libTypes.getObservable(), null);
- }
- return mObservableType;
+ return mObservableType.get();
}
ModelClass getObservableListType() {
- if (mObservableListType == null) {
- mObservableListType = loadClassErasure(libTypes.getObservableList());
- }
- return mObservableListType;
+ return mObservableListType.get();
}
ModelClass getObservableMapType() {
- if (mObservableMapType == null) {
- mObservableMapType = loadClassErasure(libTypes.getObservableMap());
- }
- return mObservableMapType;
+ return mObservableMapType.get();
}
ModelClass getLiveDataType() {
- if (mLiveDataType == null) {
- mLiveDataType = loadClassErasure(libTypes.getLiveData());
- }
- return mLiveDataType;
+ return mLiveDataType.get();
}
ModelClass getMutableLiveDataType() {
- if (mMutableLiveDataType == null) {
- mMutableLiveDataType = loadClassErasure(libTypes.getMutableLiveData());
- }
- return mMutableLiveDataType;
+ return mMutableLiveDataType.get();
}
ModelClass getViewDataBindingType() {
- if (mViewBindingType == null) {
- mViewBindingType = findClass(libTypes.getViewDataBinding(), null);
- }
- Preconditions.checkNotNull(mViewBindingType, "Cannot find %s class. Something is wrong "
+ ModelClass result = mViewBindingType.get();
+ Preconditions.checkNotNull(result, "Cannot find %s class. Something is wrong "
+ "in the classpath, please submit a bug report", libTypes.getViewDataBinding());
- return mViewBindingType;
+ return result;
}
public ModelClass getViewStubProxyType() {
- if (mViewStubProxyType == null) {
- mViewStubProxyType = findClass(libTypes.getViewStubProxy(), null);
- }
- return mViewStubProxyType;
+ return mViewStubProxyType.get();
}
protected List<ModelClass> getObservableFieldTypes() {
@@ -306,10 +335,7 @@ public abstract class ModelAnalyzer {
}
ModelClass getViewStubType() {
- if (mViewStubType == null) {
- mViewStubType = findClass(VIEW_STUB_CLASS_NAME, null);
- }
- return mViewStubType;
+ return mViewStubType.get();
}
private ModelClass loadClassErasure(String className) {
@@ -329,4 +355,5 @@ public abstract class ModelAnalyzer {
}
protected abstract boolean findGeneratedAnnotation();
+
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
index a3763ce7..7ef7585c 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationAnalyzer.java
@@ -157,11 +157,18 @@ public class AnnotationAnalyzer extends ModelAnalyzer {
if (typeElement == null && hasDot && imports != null) {
int lastDot = className.lastIndexOf('.');
TypeElement parent = getTypeElement(className.substring(0, lastDot), imports);
- if (parent == null) {
- return null;
+ if (parent != null) {
+ String name = parent.getQualifiedName() + "."
+ + className.substring(lastDot + 1);
+ return getTypeElement(name, null);
+ }
+ }
+ // try to jetify if we couldn't find it
+ if (typeElement == null) {
+ String converted = libTypes.convert(className);
+ if (!converted.equals(className)) {
+ return getTypeElement(converted, imports);
}
- String name = parent.getQualifiedName() + "." + className.substring(lastDot + 1);
- return getTypeElement(name, null);
}
return typeElement;
} catch (Exception e) {
diff --git a/compiler/src/main/java/android/databinding/tool/store/BindingAdapterStore.kt b/compiler/src/main/java/android/databinding/tool/store/BindingAdapterStore.kt
index 653610bd..10d908b2 100644
--- a/compiler/src/main/java/android/databinding/tool/store/BindingAdapterStore.kt
+++ b/compiler/src/main/java/android/databinding/tool/store/BindingAdapterStore.kt
@@ -78,8 +78,13 @@ internal class BindingAdapterStore : Intermediate {
*/
@field:Transient
private var currentModuleStore: BindingAdapterStore? = null
+ private val useAndroidX: Boolean
- constructor(stores: MutableList<Intermediate>, previousStores: List<BindingAdapterStore>) {
+ constructor(
+ stores: MutableList<Intermediate>,
+ previousStores: List<BindingAdapterStore>,
+ useAndroidX: Boolean
+ ) : this(useAndroidX) {
previousStores.forEach {
merge(it.upgrade() as BindingAdapterStore)
}
@@ -88,7 +93,9 @@ internal class BindingAdapterStore : Intermediate {
}
}
- constructor(v3: SetterStore.IntermediateV3) {
+
+ // we only care about androidX for the current process' store, others can stay unprocessed
+ constructor(v3: SetterStore.IntermediateV3) : this(false) {
merge(adapterMethods, v3.adapterMethods)
merge(renamedMethods, v3.renamedMethods)
merge(conversionMethods, v3.conversionMethods)
@@ -99,24 +106,64 @@ internal class BindingAdapterStore : Intermediate {
twoWayMethods.putAll(v3.twoWayMethods)
}
- private constructor()
+ private constructor(useAndroidX: Boolean) {
+ this.useAndroidX = useAndroidX
+ }
+
+ private fun String.androidSupportArtifact() = this.startsWith("android.databinding.adapters")
+
+ private fun <K1, K2, V : MethodDescription> Map<K1, Map<K2, V>>
+ .filterOutAndroidSupport(): Map<K1, Map<K2, V>> {
+ if (!useAndroidX) {
+ return this
+ }
+ return mapValues {
+ it.value.filterOutAndroidSupportFromMap()
+ }
+ }
+
+ private fun <K, V : MethodDescription> Map<K, V>
+ .filterOutAndroidSupportFromMap(): Map<K, V> {
+ if (!useAndroidX) {
+ return this
+ }
+ return filterValues {
+ !it.type.androidSupportArtifact()
+ }
+ }
+
+ private fun <K : InverseMethodDescription, V> Map<K, V>
+ .filterOutAndroidSupportFromMapByKeys(): Map<K, V> {
+ if (!useAndroidX) {
+ return this
+ }
+ return filterKeys {
+ !it.type.androidSupportArtifact()
+ }
+ }
/**
* Sets this as the instance used by SetterStore which means it will have modifications.
*/
fun setAsMainStore() {
- currentModuleStore = BindingAdapterStore()
+ currentModuleStore = BindingAdapterStore(useAndroidX)
}
private fun merge(other: BindingAdapterStore) {
- merge(adapterMethods, other.adapterMethods)
- merge(renamedMethods, other.renamedMethods)
- merge(conversionMethods, other.conversionMethods)
- multiValueAdapters.putAll(other.multiValueAdapters)
- untaggableTypes.putAll(other.untaggableTypes)
- merge(inverseAdapters, other.inverseAdapters)
- merge(inverseMethods, other.inverseMethods)
- twoWayMethods.putAll(other.twoWayMethods)
+ merge(adapterMethods, other.adapterMethods.filterOutAndroidSupport())
+ merge(renamedMethods, other.renamedMethods.filterOutAndroidSupport())
+ merge(conversionMethods, other.conversionMethods.filterOutAndroidSupport())
+ multiValueAdapters.putAll(other.multiValueAdapters.filterOutAndroidSupportFromMap())
+ untaggableTypes.putAll(if (useAndroidX) {
+ other.untaggableTypes.filterNot {
+ it.key.androidSupportArtifact() || it.value.androidSupportArtifact()
+ }
+ } else {
+ other.untaggableTypes
+ })
+ merge(inverseAdapters, other.inverseAdapters.filterOutAndroidSupport())
+ merge(inverseMethods, other.inverseMethods.filterOutAndroidSupport())
+ twoWayMethods.putAll(other.twoWayMethods.filterOutAndroidSupportFromMapByKeys())
}
/**
diff --git a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
index 7ec8f010..b8c07ec9 100644
--- a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
+++ b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java
@@ -176,7 +176,8 @@ public class SetterStore {
List<BindingAdapterStore> gsonIntermediates = generationalClassUtil
.load(GenerationalClassUtil.ExtensionFilter.SETTER_STORE_JSON,
BindingAdapterStore.class);
- BindingAdapterStore store = new BindingAdapterStore(previousStores, gsonIntermediates);
+ BindingAdapterStore store = new BindingAdapterStore(previousStores, gsonIntermediates,
+ modelAnalyzer.libTypes.getUseAndroidX());
return new SetterStore(modelAnalyzer, store);
}
@@ -383,13 +384,6 @@ public class SetterStore {
mStore.clear(classes);
}
- private static <K, V> void removeFromMap(Map<K, V> map, List<K> keys) {
- for (K key : keys) {
- map.remove(key);
- }
- keys.clear();
- }
-
public void write(String projectPackage)
throws IOException {
Preconditions.checkNotNull(mStore.getCurrentModuleStore(),