diff options
author | Yigit Boyar <yboyar@google.com> | 2018-10-16 01:08:55 -0700 |
---|---|---|
committer | Yigit Boyar <yboyar@google.com> | 2018-10-17 17:38:33 +0000 |
commit | 5cf6311da198837770582e5f6df9abe1582e4123 (patch) | |
tree | cd653aeeea11259562a4819bd190371ccf924277 /compiler/src | |
parent | 797d08c2e629d3a4dbb460951e97cf06e86baf96 (diff) | |
download | data-binding-5cf6311da198837770582e5f6df9abe1582e4123.tar.gz |
Move imports to a concrete class
This CL moves Map<String(alias), String(qname)> imports to a concrete
ImportBag class. It is mostly a cleanup so that in a follow-up CL,
we can start caching findClass calls based on available imports.
Right now it is not possible since imports are all bloated with
java.lang.
This CL also stops adding all java.lang.* as static identifiers to
every single model. Instead, it lazily registers it if an identifier
expression cannot be found and name matches a java.lang class.
Bug: 117808327
Test: ImportBagTest, existing tests pass
Change-Id: Ideb41ab8cfc68aa4c7b55e84ffa218ba65558eb3
Diffstat (limited to 'compiler/src')
16 files changed, 324 insertions, 139 deletions
diff --git a/compiler/src/main/java/android/databinding/tool/Binding.java b/compiler/src/main/java/android/databinding/tool/Binding.java index 19884201..9e7dccb3 100644 --- a/compiler/src/main/java/android/databinding/tool/Binding.java +++ b/compiler/src/main/java/android/databinding/tool/Binding.java @@ -22,6 +22,7 @@ import android.databinding.tool.expr.LambdaExpr; import android.databinding.tool.processing.ErrorMessages; import android.databinding.tool.processing.Scope; import android.databinding.tool.processing.scopes.LocationScopeProvider; +import android.databinding.tool.reflection.ImportBag; import android.databinding.tool.reflection.ModelAnalyzer; import android.databinding.tool.reflection.ModelClass; import android.databinding.tool.reflection.ModelMethod; @@ -34,7 +35,6 @@ import android.databinding.tool.util.Preconditions; import android.databinding.tool.writer.LayoutBinderWriterKt; import java.util.List; -import java.util.Map; public class Binding implements LocationScopeProvider { @@ -314,7 +314,7 @@ public class Binding implements LocationScopeProvider { private final SetterCall mWrappedCall; public ViewStubDirectCall(String name, ModelClass viewType, ModelClass resolvedType, - Map<String, String> imports) { + ImportBag imports) { mWrappedCall = SetterStore.get().getSetterCall(name, viewType, resolvedType, imports); if (mWrappedCall == null) { diff --git a/compiler/src/main/java/android/databinding/tool/CompilerChef.java b/compiler/src/main/java/android/databinding/tool/CompilerChef.java index a2b609d5..46cd6724 100644 --- a/compiler/src/main/java/android/databinding/tool/CompilerChef.java +++ b/compiler/src/main/java/android/databinding/tool/CompilerChef.java @@ -15,8 +15,10 @@ package android.databinding.tool; import android.databinding.tool.processing.Scope; import android.databinding.tool.processing.ScopedException; +import android.databinding.tool.reflection.ImportBag; import android.databinding.tool.reflection.InjectedClass; import android.databinding.tool.reflection.ModelAnalyzer; +import android.databinding.tool.reflection.MutableImportBag; import android.databinding.tool.store.FeatureInfoList; import android.databinding.tool.store.GenClassInfoLog; import android.databinding.tool.store.ResourceBundle; @@ -143,7 +145,7 @@ public class CompilerChef { analyzer.injectClass(bindingClass); for (ResourceBundle.LayoutFileBundle layoutFileBundle : bundles) { - final HashMap<String, String> imports = new HashMap<String, String>(); + final MutableImportBag imports = new MutableImportBag(); for (ResourceBundle.NameTypeLocation imp : layoutFileBundle.getImports()) { imports.put(imp.name, imp.type); } diff --git a/compiler/src/main/java/android/databinding/tool/LayoutBinder.java b/compiler/src/main/java/android/databinding/tool/LayoutBinder.java index b8024c3b..9befb9cf 100644 --- a/compiler/src/main/java/android/databinding/tool/LayoutBinder.java +++ b/compiler/src/main/java/android/databinding/tool/LayoutBinder.java @@ -65,106 +65,6 @@ public class LayoutBinder implements FileScopeProvider { private LayoutBinderWriter mWriter; private ResourceBundle.LayoutFileBundle mBundle; - private static final String[] sJavaLangClasses = { - "Deprecated", - "Override", - "SafeVarargs", - "SuppressWarnings", - "Appendable", - "AutoCloseable", - "CharSequence", - "Cloneable", - "Comparable", - "Iterable", - "Readable", - "Runnable", - "Thread.UncaughtExceptionHandler", - "Boolean", - "Byte", - "Character", - "Character.Subset", - "Character.UnicodeBlock", - "Class", - "ClassLoader", - "Compiler", - "Double", - "Enum", - "Float", - "InheritableThreadLocal", - "Integer", - "Long", - "Math", - "Number", - "Object", - "Package", - "Process", - "ProcessBuilder", - "Runtime", - "RuntimePermission", - "SecurityManager", - "Short", - "StackTraceElement", - "StrictMath", - "String", - "StringBuffer", - "StringBuilder", - "System", - "Thread", - "ThreadGroup", - "ThreadLocal", - "Throwable", - "Void", - "Thread.State", - "ArithmeticException", - "ArrayIndexOutOfBoundsException", - "ArrayStoreException", - "ClassCastException", - "ClassNotFoundException", - "CloneNotSupportedException", - "EnumConstantNotPresentException", - "Exception", - "IllegalAccessException", - "IllegalArgumentException", - "IllegalMonitorStateException", - "IllegalStateException", - "IllegalThreadStateException", - "IndexOutOfBoundsException", - "InstantiationException", - "InterruptedException", - "NegativeArraySizeException", - "NoSuchFieldException", - "NoSuchMethodException", - "NullPointerException", - "NumberFormatException", - "ReflectiveOperationException", - "RuntimeException", - "SecurityException", - "StringIndexOutOfBoundsException", - "TypeNotPresentException", - "UnsupportedOperationException", - "AbstractMethodError", - "AssertionError", - "ClassCircularityError", - "ClassFormatError", - "Error", - "ExceptionInInitializerError", - "IllegalAccessError", - "IncompatibleClassChangeError", - "InstantiationError", - "InternalError", - "LinkageError", - "NoClassDefFoundError", - "NoSuchFieldError", - "NoSuchMethodError", - "OutOfMemoryError", - "StackOverflowError", - "ThreadDeath", - "UnknownError", - "UnsatisfiedLinkError", - "UnsupportedClassVersionError", - "VerifyError", - "VirtualMachineError", - }; public final ResourceBundle.LayoutFileBundle mLayoutBundle; @@ -197,9 +97,6 @@ public class LayoutBinder implements FileScopeProvider { "getRoot().getContext()"); names.add("context"); } - for (String javaLangClass : sJavaLangClasses) { - mExprModel.addImport(javaLangClass, "java.lang." + javaLangClass, null); - } // First resolve all the View fields // Ensure there are no conflicts with variable names for (BindingTargetBundle targetBundle : mBundle.getBindingTargetBundles()) { diff --git a/compiler/src/main/java/android/databinding/tool/expr/CallbackExprModel.java b/compiler/src/main/java/android/databinding/tool/expr/CallbackExprModel.java index af49ee1d..9c3495a6 100644 --- a/compiler/src/main/java/android/databinding/tool/expr/CallbackExprModel.java +++ b/compiler/src/main/java/android/databinding/tool/expr/CallbackExprModel.java @@ -18,6 +18,7 @@ package android.databinding.tool.expr; import android.databinding.tool.processing.ErrorMessages; import android.databinding.tool.processing.Scope; +import android.databinding.tool.reflection.ImportBag; import android.databinding.tool.store.Location; import android.databinding.tool.util.L; import android.databinding.tool.util.Preconditions; @@ -42,7 +43,7 @@ public class CallbackExprModel extends ExprModel { } @Override - public Map<String, String> getImports() { + public ImportBag getImports() { return mOriginal.getImports(); } diff --git a/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java b/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java index 1aa57b08..6a946c69 100644 --- a/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java +++ b/compiler/src/main/java/android/databinding/tool/expr/ExprModel.java @@ -20,9 +20,11 @@ import android.databinding.tool.BindingTarget; import android.databinding.tool.CallbackWrapper; import android.databinding.tool.InverseBinding; import android.databinding.tool.processing.ErrorMessages; +import android.databinding.tool.reflection.ImportBag; import android.databinding.tool.reflection.ModelAnalyzer; import android.databinding.tool.reflection.ModelClass; import android.databinding.tool.reflection.ModelMethod; +import android.databinding.tool.reflection.MutableImportBag; import android.databinding.tool.store.Location; import android.databinding.tool.util.L; import android.databinding.tool.util.Preconditions; @@ -45,7 +47,7 @@ import java.util.stream.Collectors; public class ExprModel { public static final String SAFE_UNBOX_METHOD_NAME = "safeUnbox"; - Map<String, Expr> mExprMap = new HashMap<String, Expr>(); + Map<String, Expr> mExprMap = new HashMap<>(); List<Expr> mBindingExpressions = new ArrayList<Expr>(); @@ -81,7 +83,7 @@ public class ExprModel { private boolean mSealed = false; - private Map<String, String> mImports = new HashMap<String, String>(); + private MutableImportBag mImports = new MutableImportBag(); private ParserRuleContext mCurrentParserContext; private Location mCurrentLocationInFile; @@ -116,6 +118,15 @@ public class ExprModel { } //noinspection unchecked T existing = (T) mExprMap.get(expr.getUniqueKey()); + if (existing == null) { + // if it is identifier, look for java.lang + // extra check to exclude StaticIdentifier since + // while registering it, we'll hit this case + if (expr instanceof IdentifierExpr && !(expr instanceof StaticIdentifierExpr)) { + IdentifierExpr id = (IdentifierExpr) expr; + existing = (T) lazyImportFromJavaLang(id.getName()); + } + } if (existing != null) { Preconditions.check(expr.getParents().isEmpty(), "If an expression already exists, it should've never been added to a parent," @@ -269,7 +280,7 @@ public class ExprModel { } while (true) { String candidate = cnt == 0 ? baseName : baseName + cnt; - if (!mImports.containsKey(candidate)) { + if (!mImports.contains(candidate)) { return addImport(candidate, type, null); } cnt ++; @@ -339,7 +350,7 @@ public class ExprModel { } public StaticIdentifierExpr addImport(String alias, String type, Location location) { - String existing = mImports.get(alias); + String existing = mImports.find(alias); if (existing != null) { if (existing.equals(type)) { final StaticIdentifierExpr id = findStaticIdentifierExpr(type); @@ -362,7 +373,7 @@ public class ExprModel { return id; } - public Map<String, String> getImports() { + public ImportBag getImports() { return mImports; } @@ -784,6 +795,18 @@ public class ExprModel { return (IdentifierExpr) expr; } } + return lazyImportFromJavaLang(name); + } + + @Nullable + private StaticIdentifierExpr lazyImportFromJavaLang(String name) { + // check for java lang imports and add from there if it exists + if (ImportBag.JAVA_LANG_IMPORTS.contains(name)) { + final StaticIdentifierExpr id = staticIdentifier(name); + L.d("adding java lang import %s", name); + id.setUserDefinedType("java.lang." + name); + return id; + } return null; } } diff --git a/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java index 56418e3e..ce32889c 100644 --- a/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java +++ b/compiler/src/main/java/android/databinding/tool/expr/ResourceExpr.java @@ -16,6 +16,7 @@ package android.databinding.tool.expr; import android.databinding.tool.BindingTarget; +import android.databinding.tool.reflection.ImportBag; import android.databinding.tool.reflection.ModelAnalyzer; import android.databinding.tool.reflection.ModelClass; import android.databinding.tool.writer.KCode; @@ -66,7 +67,7 @@ public class ResourceExpr extends Expr { private Map<String, ModelClass> getResourceToTypeMapping(ModelAnalyzer modelAnalyzer) { if (mResourceToTypeMapping == null) { - final Map<String, String> imports = getModel().getImports(); + final ImportBag imports = getModel().getImports(); mResourceToTypeMapping = new HashMap<String, ModelClass>(); mResourceToTypeMapping.put("anim", modelAnalyzer.findClass("android.view.animation.Animation", imports)); diff --git a/compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.java b/compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.java index 9444c3cc..4e1cc0d3 100644 --- a/compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.java +++ b/compiler/src/main/java/android/databinding/tool/expr/TwoWayListenerExpr.java @@ -16,6 +16,7 @@ package android.databinding.tool.expr; import android.databinding.tool.InverseBinding; +import android.databinding.tool.reflection.ImportBag; import android.databinding.tool.reflection.ModelAnalyzer; import android.databinding.tool.reflection.ModelClass; import android.databinding.tool.writer.KCode; @@ -37,7 +38,7 @@ public class TwoWayListenerExpr extends Expr { @Override protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { return modelAnalyzer.findClass(modelAnalyzer.libTypes.getInverseBindingListener(), - Collections.emptyMap()); + ImportBag.EMPTY); } @Override diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ImportBag.kt b/compiler/src/main/java/android/databinding/tool/reflection/ImportBag.kt new file mode 100644 index 00000000..8b6fae1a --- /dev/null +++ b/compiler/src/main/java/android/databinding/tool/reflection/ImportBag.kt @@ -0,0 +1,171 @@ +/* + * 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 + +class MutableImportBag : ImportBag() { + open fun put(alias : String, qName : String) { + imports.putIfAbsent(alias, qName) + } +} + +private class ImmutableImportBag : ImportBag() { + +} + +/** + * A class that can keep a list of imports and also run an equals check against itself. + * + * We do import everything in java.lang which is a waste of memory and killer for equals + * check. Instead, this class optimizes that part automatically. + * + * Equals on ImportBag is important because we resolve classes based on imports. + */ +sealed class ImportBag { + // alias to Import mapping + protected val imports = mutableMapOf<String, String>() + + fun find(alias: String) : String? { + return imports[alias] ?: importJavaLang(alias) + } + + fun contains(alias: String) = find(alias) != null + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ImportBag) return false + + if (imports != other.imports) return false + + return true + } + + override fun hashCode(): Int { + return imports.hashCode() + } + + companion object { + @JvmField + val EMPTY : ImportBag = ImmutableImportBag() + @JvmField + val JAVA_LANG_IMPORTS = setOf( + "Deprecated", + "Override", + "SafeVarargs", + "SuppressWarnings", + "Appendable", + "AutoCloseable", + "CharSequence", + "Cloneable", + "Comparable", + "Iterable", + "Readable", + "Runnable", + "Thread.UncaughtExceptionHandler", + "Boolean", + "Byte", + "Character", + "Character.Subset", + "Character.UnicodeBlock", + "Class", + "ClassLoader", + "Compiler", + "Double", + "Enum", + "Float", + "InheritableThreadLocal", + "Integer", + "Long", + "Math", + "Number", + "Object", + "Package", + "Process", + "ProcessBuilder", + "Runtime", + "RuntimePermission", + "SecurityManager", + "Short", + "StackTraceElement", + "StrictMath", + "String", + "StringBuffer", + "StringBuilder", + "System", + "Thread", + "ThreadGroup", + "ThreadLocal", + "Throwable", + "Void", + "Thread.State", + "ArithmeticException", + "ArrayIndexOutOfBoundsException", + "ArrayStoreException", + "ClassCastException", + "ClassNotFoundException", + "CloneNotSupportedException", + "EnumConstantNotPresentException", + "Exception", + "IllegalAccessException", + "IllegalArgumentException", + "IllegalMonitorStateException", + "IllegalStateException", + "IllegalThreadStateException", + "IndexOutOfBoundsException", + "InstantiationException", + "InterruptedException", + "NegativeArraySizeException", + "NoSuchFieldException", + "NoSuchMethodException", + "NullPointerException", + "NumberFormatException", + "ReflectiveOperationException", + "RuntimeException", + "SecurityException", + "StringIndexOutOfBoundsException", + "TypeNotPresentException", + "UnsupportedOperationException", + "AbstractMethodError", + "AssertionError", + "ClassCircularityError", + "ClassFormatError", + "Error", + "ExceptionInInitializerError", + "IllegalAccessError", + "IncompatibleClassChangeError", + "InstantiationError", + "InternalError", + "LinkageError", + "NoClassDefFoundError", + "NoSuchFieldError", + "NoSuchMethodError", + "OutOfMemoryError", + "StackOverflowError", + "ThreadDeath", + "UnknownError", + "UnsatisfiedLinkError", + "UnsupportedClassVersionError", + "VerifyError", + "VirtualMachineError" + ) + private fun importJavaLang(alias : String) : String? { + return if (JAVA_LANG_IMPORTS.contains(alias)) { + "java.lang.$alias" + } else { + null + } + } + } +}
\ No newline at end of file diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java index 45bedcb0..1ae22afa 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java @@ -46,7 +46,7 @@ public class InjectedClass extends ModelClass { mSuperClass = superClass; } - public void addVariable(String name, String type, Map<String, String> imports) { + public void addVariable(String name, String type, ImportBag imports) { String capName = StringUtils.capitalize(name); String setName = "set" + capName; String getName = "get" + capName; diff --git a/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java index be1f8d79..0a9f3ce5 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedMethod.java @@ -31,13 +31,13 @@ public class InjectedMethod extends ModelMethod { private final String mName; private final String mReturnTypeName; private final String[] mParameterTypeNames; - private final Map<String, String> mImports; + private final ImportBag mImports; private ModelClass[] mParameterTypes; private ModelClass mReturnType; private boolean mIsStatic; public InjectedMethod(InjectedClass containingClass, boolean isStatic, String name, - Map<String, String> imports, String returnType, String... parameters) { + ImportBag imports, String returnType, String... parameters) { mContainingClass = containingClass; mName = name; mIsStatic = isStatic; 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 f650b9c5..0671165d 100644 --- a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java +++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.java @@ -19,7 +19,6 @@ 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; @@ -251,14 +250,14 @@ public abstract class ModelAnalyzer { return "null"; } - public final ModelClass findClass(String className, Map<String, String> imports) { + public final ModelClass findClass(String className, ImportBag imports) { if (mInjectedClasses.containsKey(className)) { return mInjectedClasses.get(className); } return findClassInternal(className, imports); } - public abstract ModelClass findClassInternal(String className, Map<String, String> imports); + public abstract ModelClass findClassInternal(String className, ImportBag importBag); public abstract ModelClass findClass(Class classType); 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 7ef7585c..ec7b872e 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 @@ -16,6 +16,7 @@ package android.databinding.tool.reflection.annotation; import android.databinding.tool.LibTypes; +import android.databinding.tool.reflection.ImportBag; import android.databinding.tool.reflection.ModelAnalyzer; import android.databinding.tool.reflection.ModelClass; import android.databinding.tool.reflection.TypeUtil; @@ -71,7 +72,7 @@ public class AnnotationAnalyzer extends ModelAnalyzer { } @Override - public ModelClass findClassInternal(String className, Map<String, String> imports) { + public ModelClass findClassInternal(String className, ImportBag imports) { className = className.trim(); int numDimensions = 0; while (className.endsWith("[]")) { @@ -130,12 +131,12 @@ public class AnnotationAnalyzer extends ModelAnalyzer { return new AnnotationClass(type); } - private TypeElement getTypeElement(String className, Map<String, String> imports) { + private TypeElement getTypeElement(String className, ImportBag imports) { Elements elementUtils = getElementUtils(); final boolean hasDot = className.indexOf('.') >= 0; if (!hasDot && imports != null) { // try the imports - String importedClass = imports.get(className); + String importedClass = imports.find(className); if (importedClass != null) { className = importedClass; } @@ -177,7 +178,7 @@ public class AnnotationAnalyzer extends ModelAnalyzer { } private ArrayList<String> splitTemplateParameters(String templateParameters) { - ArrayList<String> list = new ArrayList<String>(); + ArrayList<String> list = new ArrayList<>(); int index = 0; int openCount = 0; StringBuilder arg = new StringBuilder(); 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 b8c07ec9..4fce56ef 100644 --- a/compiler/src/main/java/android/databinding/tool/store/SetterStore.java +++ b/compiler/src/main/java/android/databinding/tool/store/SetterStore.java @@ -16,6 +16,7 @@ package android.databinding.tool.store; import android.databinding.tool.Context; +import android.databinding.tool.reflection.ImportBag; import android.databinding.tool.reflection.ModelAnalyzer; import android.databinding.tool.reflection.ModelClass; import android.databinding.tool.reflection.ModelMethod; @@ -553,7 +554,7 @@ public class SetterStore { } public SetterCall getSetterCall(String attribute, ModelClass viewType, - ModelClass valueType, Map<String, String> imports) { + ModelClass valueType, ImportBag imports) { if (viewType == null) { return null; } @@ -628,7 +629,7 @@ public class SetterStore { } public BindingGetterCall getGetterCall(String attribute, ModelClass viewType, - ModelClass valueType, Map<String, String> imports) { + ModelClass valueType, ImportBag imports) { if (viewType == null) { return null; } else if (viewType.isViewDataBinding()) { @@ -660,7 +661,7 @@ public class SetterStore { ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); ModelClass listenerType = modelAnalyzer.findClass( modelAnalyzer.libTypes.getInverseBindingListener(), - Collections.emptyMap()); + ImportBag.EMPTY); BindingSetterCall eventCall = getSetterCall( inverseDescription.event, finalViewType, listenerType, imports); if (eventCall == null) { @@ -704,7 +705,7 @@ public class SetterStore { } private ModelMethod getBestSetter(ModelClass viewType, ModelClass argumentType, - String attribute, Map<String, String> imports) { + String attribute, ImportBag imports) { if (viewType.isGeneric()) { argumentType = eraseType(argumentType, viewType.getTypeArguments()); viewType = viewType.erasure(); @@ -747,7 +748,7 @@ public class SetterStore { } private InverseMethod getBestGetter(ModelClass viewType, ModelClass valueType, - String attribute, Map<String, String> imports) { + String attribute, ImportBag imports) { @SuppressWarnings("WeakerAccess") class BestSetter { @Nullable @@ -797,7 +798,7 @@ public class SetterStore { if (bestSetter.description != null) { ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); final ModelClass listenerType = modelAnalyzer.findClass( - modelAnalyzer.libTypes.getInverseBindingListener(), Collections.emptyMap()); + modelAnalyzer.libTypes.getInverseBindingListener(), ImportBag.EMPTY); SetterCall eventSetter = getSetterCall(bestSetter.description.event, viewType, listenerType, imports); if (eventSetter == null) { @@ -856,7 +857,7 @@ public class SetterStore { * another for an argument. It is assumed that both view types match the targeted view. * <p> * Note that this has different priorities than - * {@link #isBetterReturn(ModelClass, ModelClass, ModelClass, ModelClass, ModelClass, Map)} + * {@link #isBetterReturn(ModelClass, ModelClass, ModelClass, ModelClass, ModelClass, ImportBag)} * * @param argument The argument type being passed to the setter. * @param newViewType The type of the view in the BindingAdapter or setter method. @@ -873,7 +874,7 @@ public class SetterStore { private boolean isBetterParameter(@NonNull ModelClass argument, @NonNull ModelClass newViewType, @NonNull ModelClass newParameter, @Nullable ModelClass oldViewType, @Nullable ModelClass oldParameter, - @Nullable Map<String, String> imports) { + @Nullable ImportBag imports) { if (oldParameter == null) { // just validate that it can be converted return calculateConversionPriority(argument, newParameter, imports) >= 0; @@ -913,7 +914,7 @@ public class SetterStore { * than another for a method call. It is assumed that both view types match the targeted view. * <p> * Note that this has different priorities than - * {@link #isBetterParameter(ModelClass, ModelClass, ModelClass, ModelClass, ModelClass, Map)} + * {@link #isBetterParameter(ModelClass, ModelClass, ModelClass, ModelClass, ModelClass, ImportBag)} * * @param expected The type that is expected from the getter. * @param newViewType The type of the view in the InverseBindingAdapter or getter method. @@ -931,7 +932,7 @@ public class SetterStore { private boolean isBetterReturn(@NonNull ModelClass expected, @NonNull ModelClass newViewType, @NonNull ModelClass newReturnType, @Nullable ModelClass oldViewType, @Nullable ModelClass oldReturnType, - @Nullable Map<String, String> imports) { + @Nullable ImportBag imports) { if (oldReturnType == null) { // just validate that it can be converted return calculateConversionPriority(newReturnType, expected, imports) >= 0; @@ -980,7 +981,7 @@ public class SetterStore { * where {@code 0} is an exact match and greater numbers are progressively worse matches. */ private int calculateConversionPriority(@NonNull ModelClass from, @NonNull ModelClass to, - @Nullable Map<String, String> imports) { + @Nullable ImportBag imports) { if (to.equals(from)) { return 0; // exact match } @@ -1004,7 +1005,7 @@ public class SetterStore { } private MethodDescription getConversionMethod(ModelClass from, ModelClass to, - Map<String, String> imports) { + ImportBag imports) { if (from != null && to != null) { if (to.isObject()) { return null; diff --git a/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java b/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java index 1eb131ee..5ddaa2c7 100644 --- a/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java +++ b/compiler/src/test/java/android/databinding/tool/expr/ExprModelTest.java @@ -882,7 +882,7 @@ public class ExprModelTest { StaticIdentifierExpr id = (StaticIdentifierExpr) child; assertEquals(id.getResolvedType().getCanonicalName(), "android.view.View"); // on demand import - assertEquals("android.view.View", mExprModel.getImports().get("View")); + assertEquals("android.view.View", mExprModel.getImports().find("View")); } @Test @@ -895,12 +895,19 @@ public class ExprModelTest { final StaticIdentifierExpr id = mExprModel.staticIdentifierFor(myView.getResolvedType()); mExprModel.seal(); // on demand import with conflict - assertEquals("android.view.View", mExprModel.getImports().get("View1")); + assertEquals("android.view.View", mExprModel.getImports().find("View1")); assertEquals("View1", id.getName()); assertEquals("android.view.View", id.getUserDefinedType()); } @Test + public void testFindStatic() { + IdentifierExpr id = mExprModel.findIdentifier("String"); + assertNotNull(id); + assertEquals("java.lang.String", id.getUserDefinedType()); + } + + @Test public void testOnDemandImportAlreadyImported() { MockLayoutBinder lb = new MockLayoutBinder(); mExprModel = lb.getModel(); diff --git a/compiler/src/test/java/android/databinding/tool/reflection/ImportBagTest.kt b/compiler/src/test/java/android/databinding/tool/reflection/ImportBagTest.kt new file mode 100644 index 00000000..8e3a36b1 --- /dev/null +++ b/compiler/src/test/java/android/databinding/tool/reflection/ImportBagTest.kt @@ -0,0 +1,80 @@ +/* + * 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 org.junit.Assert.* +import org.junit.Test + +class MutableImportBagTest { + @Test + fun mutableWithImmutable() { + assertEquals(MutableImportBag(), ImportBag.EMPTY) + } + + @Test + fun mutableWithImmutable_modified() { + assertNotEquals(MutableImportBag().also { + it.put("foo", "bar") + }, ImportBag.EMPTY) + } + + @Test + fun normal() { + val imports = MutableImportBag() + assertNull(imports.find("Foo")) + imports.put("Foo", "bar.Foo") + assertEquals("bar.Foo", imports.find("Foo")) + assertEquals("java.lang.String", imports.find("String")) + } + + @Test + fun equals() { + val bag1 = MutableImportBag().apply { + put("foo", "Bar") + put("bar", "Foo") + } + val bag2 = MutableImportBag().apply { + put("foo", "Bar") + put("bar", "Foo") + } + assertEquals(bag1, bag2) + } + + @Test + fun equals_mismatch() { + val bag1 = MutableImportBag().apply { + put("foo", "Bar2") + put("bar", "Foo") + } + val bag2 = MutableImportBag().apply { + put("bar", "Foo") + put("foo", "Bar") + } + assertNotEquals(bag1, bag2) + } + + @Test + fun equals_missing() { + val bag1 = MutableImportBag().apply { + put("bar", "Foo") + } + val bag2 = MutableImportBag().apply { + put("foo", "Bar") + put("bar", "Foo") + } + assertNotEquals(bag1, bag2) + } +}
\ No newline at end of file diff --git a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java index 4940c47a..64364b1b 100644 --- a/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java +++ b/compiler/src/test/java/android/databinding/tool/reflection/java/JavaAnalyzer.java @@ -15,6 +15,7 @@ package android.databinding.tool.reflection.java; import android.databinding.tool.Context; import android.databinding.tool.LibTypes; +import android.databinding.tool.reflection.ImportBag; import android.databinding.tool.reflection.ModelAnalyzer; import android.databinding.tool.reflection.ModelClass; import android.databinding.tool.reflection.SdkUtil; @@ -88,7 +89,7 @@ public class JavaAnalyzer extends ModelAnalyzer { } @Override - public ModelClass findClassInternal(String className, Map<String, String> imports) { + public ModelClass findClassInternal(String className, ImportBag imports) { // TODO handle imports JavaClass loaded = mClassCache.get(className); if (loaded != null) { |