diff options
author | Yigit Boyar <yboyar@google.com> | 2018-10-04 14:58:19 -0700 |
---|---|---|
committer | TreeHugger Robot <treehugger-gerrit@google.com> | 2018-10-10 18:52:58 +0000 |
commit | 498a422582d70a9033cd83283011a558545a1825 (patch) | |
tree | abca9ba3ffd04c3026af63bb9d0c6d4c31a5c258 /compiler/src/main/java/android/databinding | |
parent | 26957e600f8f6476c5152e128226d43288d4f3cd (diff) | |
download | data-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')
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(), |