summaryrefslogtreecommitdiff
path: root/compiler/src/main/java/android/databinding
diff options
context:
space:
mode:
authorYigit Boyar <yboyar@google.com>2018-10-17 17:29:26 -0700
committerYigit Boyar <yboyar@google.com>2018-10-19 12:33:46 -0700
commit2ec6c75f754b0f6ffdf1ab87920c76cb5679aeba (patch)
tree99501ea98a6a47f5e4099da2c182a427a234dfc7 /compiler/src/main/java/android/databinding
parentb7e7521cbb76fd5705911d8813f273b107d69e15 (diff)
downloaddata-binding-2ec6c75f754b0f6ffdf1ab87920c76cb5679aeba.tar.gz
Move ModelClass to kotlin
Bug: 117808327 Test: existing tests pass Change-Id: Iec0d01d9c0d76d8889794ee1fa3a69cbca115128
Diffstat (limited to 'compiler/src/main/java/android/databinding')
-rw-r--r--compiler/src/main/java/android/databinding/tool/Binding.java5
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/CachedClass.java41
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java4
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.kt18
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java705
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/ModelClass.kt633
-rw-r--r--compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.kt197
7 files changed, 749 insertions, 854 deletions
diff --git a/compiler/src/main/java/android/databinding/tool/Binding.java b/compiler/src/main/java/android/databinding/tool/Binding.java
index 9e7dccb3..cfe1b1e9 100644
--- a/compiler/src/main/java/android/databinding/tool/Binding.java
+++ b/compiler/src/main/java/android/databinding/tool/Binding.java
@@ -122,7 +122,7 @@ public class Binding implements LocationScopeProvider {
ModelClass viewType = mTarget.getResolvedType();
if ("android:visibility".equals(mName) && viewType != null && viewType.isViewDataBinding()) {
mSetterCall = new IncludeVisibilityCall();
- } else if (viewType != null && viewType.extendsViewStub()) {
+ } else if (viewType != null && viewType.getExtendsViewStub()) {
mExpr = mExpr.unwrapObservableField();
if (isListenerAttribute(mName)) {
ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
@@ -136,7 +136,6 @@ public class Binding implements LocationScopeProvider {
mSetterCall = new ViewStubSetterCall(mName);
}
} else {
- ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
if (mExpr.getResolvedType().getObservableGetterName() != null) {
// If it is an ObservableField, try with the contents of it first.
Expr expr = mExpr.unwrapObservableField();
@@ -164,7 +163,7 @@ public class Binding implements LocationScopeProvider {
ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
ModelClass objectParameter = modelAnalyzer.findClass(Object.class);
SetterStore setterStore = SetterStore.get();
- if (viewType != null && viewType.extendsViewStub()) {
+ if (viewType != null && viewType.getExtendsViewStub()) {
if (isListenerAttribute(name)) {
ModelClass viewStubProxy = modelAnalyzer.getViewStubProxyType();
setterCall = SetterStore.get().getSetterCall(name,
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/CachedClass.java b/compiler/src/main/java/android/databinding/tool/reflection/CachedClass.java
deleted file mode 100644
index 84866093..00000000
--- a/compiler/src/main/java/android/databinding/tool/reflection/CachedClass.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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/InjectedClass.java b/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java
index 1ae22afa..c100bfd3 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java
+++ b/compiler/src/main/java/android/databinding/tool/reflection/InjectedClass.java
@@ -204,7 +204,7 @@ public class InjectedClass extends ModelClass {
}
@Override
- protected ModelField[] getDeclaredFields() {
+ public ModelField[] getDeclaredFields() {
ModelClass superClass = getSuperclass();
final ModelField[] superFields = superClass.getDeclaredFields();
final int initialCount = superFields.length;
@@ -217,7 +217,7 @@ public class InjectedClass extends ModelClass {
}
@Override
- protected ModelMethod[] getDeclaredMethods() {
+ public ModelMethod[] getDeclaredMethods() {
ModelClass superClass = getSuperclass();
final ModelMethod[] superMethods = superClass.getDeclaredMethods();
final int initialCount = superMethods.length;
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.kt b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.kt
index e60084da..4d0786ae 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.kt
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelAnalyzer.kt
@@ -28,24 +28,24 @@ import java.util.*
abstract class ModelAnalyzer protected constructor(@JvmField val libTypes: LibTypes) {
val mapType by lazy(LazyThreadSafetyMode.NONE) {
- loadClassErasure(MAP_CLASS_NAME)
+ loadClassErasure(MAP_CLASS_NAME)!!
}
val stringType by lazy(LazyThreadSafetyMode.NONE) {
- findClass(STRING_CLASS_NAME, null)
+ findClass(STRING_CLASS_NAME, null)!!
}
val objectType by lazy(LazyThreadSafetyMode.NONE) {
- findClass(OBJECT_CLASS_NAME, null)
+ findClass(OBJECT_CLASS_NAME, null)!!
}
- val observableType by lazy(LazyThreadSafetyMode.NONE) {
- findClass(libTypes.observable, null)
+ val observableType by lazy(LazyThreadSafetyMode.NONE) {
+ findClass(libTypes.observable, null)!!
}
val observableListType by lazy(LazyThreadSafetyMode.NONE) {
- loadClassErasure(libTypes.observableList)
+ loadClassErasure(libTypes.observableList)!!
}
val observableMapType by lazy(LazyThreadSafetyMode.NONE) {
- loadClassErasure(libTypes.observableMap)
+ loadClassErasure(libTypes.observableMap)!!
}
val liveDataType by lazy(LazyThreadSafetyMode.NONE) {
loadClassErasure(libTypes.liveData)
@@ -70,7 +70,7 @@ abstract class ModelAnalyzer protected constructor(@JvmField val libTypes: LibTy
/**
* If it is present, we annotate generated classes with @Generated.
*/
- val hasGeneratedAnnotation by lazy {
+ val hasGeneratedAnnotation by lazy(LazyThreadSafetyMode.NONE) {
findGeneratedAnnotation()
}
@@ -81,7 +81,7 @@ abstract class ModelAnalyzer protected constructor(@JvmField val libTypes: LibTy
.mapNotNull(this::loadClassErasure)
}
- protected val observableFieldTypes by lazy {
+ val observableFieldTypes by lazy(LazyThreadSafetyMode.NONE) {
libTypes.observableFields
.mapNotNull(this::loadClassErasure)
}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
deleted file mode 100644
index 557ad25a..00000000
--- a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.java
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
- * Copyright (C) 2015 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 android.databinding.tool.BindableCompat;
-import android.databinding.tool.ext.ExtKt;
-import android.databinding.tool.reflection.Callable.Type;
-import android.databinding.tool.util.L;
-import android.databinding.tool.util.StringUtils;
-
-import com.google.common.collect.ImmutableMap;
-import com.squareup.javapoet.TypeName;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-import static android.databinding.tool.reflection.Callable.CAN_BE_INVALIDATED;
-import static android.databinding.tool.reflection.Callable.DYNAMIC;
-import static android.databinding.tool.reflection.Callable.STATIC;
-
-public abstract class ModelClass {
- public static Map<Class, Class> BOX_MAPPING;
- public static Map<Class, Class> UNBOX_MAPPING;
- {
- BOX_MAPPING = ImmutableMap.<Class, Class>builder()
- .put(int.class, Integer.class)
- .put(long.class, Long.class)
- .put(short.class, Short.class)
- .put(byte.class, Byte.class)
- .put(char.class, Character.class)
- .put(double.class, Double.class)
- .put(float.class, Float.class)
- .put(boolean.class, Boolean.class)
- .build();
- ImmutableMap.Builder<Class, Class> reverseBuilder = ImmutableMap.<Class, Class>builder();
- for (Map.Entry<Class, Class> entry : BOX_MAPPING.entrySet()) {
- reverseBuilder.put(entry.getValue(), entry.getKey());
- }
- UNBOX_MAPPING = reverseBuilder.build();
- }
- public abstract String toJavaCode();
-
- /**
- * @return whether this ModelClass represents an array.
- */
- public abstract boolean isArray();
-
- /**
- * For arrays, lists, and maps, this returns the contained value. For other types, null
- * is returned.
- *
- * @return The component type for arrays, the value type for maps, and the element type
- * for lists.
- */
- public abstract ModelClass getComponentType();
-
- /**
- * @return Whether or not this ModelClass can be treated as a List. This means
- * it is a java.util.List, or one of the Sparse*Array classes.
- */
- public boolean isList() {
- for (ModelClass listType : ModelAnalyzer.getInstance().getListTypes()) {
- if (listType.isAssignableFrom(this)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return whether or not this ModelClass can be considered a Map or not.
- */
- public boolean isMap() {
- return ModelAnalyzer.getInstance().getMapType().isAssignableFrom(erasure());
- }
-
- /**
- * @return whether or not this ModelClass is a java.lang.String.
- */
- public boolean isString() {
- return ModelAnalyzer.getInstance().getStringType().equals(this);
- }
-
- /**
- * @return whether or not this ModelClass represents a Reference type.
- */
- public abstract boolean isNullable();
-
- /**
- * @return whether or not this ModelClass represents a primitive type.
- */
- public abstract boolean isPrimitive();
-
- /**
- * @return whether or not this ModelClass represents a Java boolean
- */
- public abstract boolean isBoolean();
-
- /**
- * @return whether or not this ModelClass represents a Java char
- */
- public abstract boolean isChar();
-
- /**
- * @return whether or not this ModelClass represents a Java byte
- */
- public abstract boolean isByte();
-
- /**
- * @return whether or not this ModelClass represents a Java short
- */
- public abstract boolean isShort();
-
- /**
- * @return whether or not this ModelClass represents a Java int
- */
- public abstract boolean isInt();
-
- /**
- * @return whether or not this ModelClass represents a Java long
- */
- public abstract boolean isLong();
-
- /**
- * @return whether or not this ModelClass represents a Java float
- */
- public abstract boolean isFloat();
-
- /**
- * @return whether or not this ModelClass represents a Java double
- */
- public abstract boolean isDouble();
-
- /**
- * @return whether or not this has type parameters
- */
- public abstract boolean isGeneric();
-
- /**
- * @return a list of Generic type paramters for the class. For example, if the class
- * is List&lt;T>, then the return value will be a list containing T. null is returned
- * if this is not a generic type
- */
- public abstract List<ModelClass> getTypeArguments();
-
- /**
- * @return whether this is a type variable. For example, in List&lt;T>, T is a type variable.
- * However, List&lt;String>, String is not a type variable.
- */
- public abstract boolean isTypeVar();
-
- /**
- * @return whether this is a wildcard type argument or not.
- */
- public abstract boolean isWildcard();
-
- /**
- * @return whether or not this ModelClass is java.lang.Object and not a primitive or subclass.
- */
- public boolean isObject() {
- return ModelAnalyzer.getInstance().getObjectType().equals(this);
- }
-
- /**
- * @return whether or not this ModelClass is an interface
- */
- public abstract boolean isInterface();
-
- /**
- * @return whether or not his is a ViewDataBinding subclass.
- */
- public boolean isViewDataBinding() {
- return ModelAnalyzer.getInstance().getViewDataBindingType().isAssignableFrom(this);
- }
-
- /**
- * @return whether or not this ModelClass type extends ViewStub.
- */
- public boolean extendsViewStub() {
- return ModelAnalyzer.getInstance().getViewStubType().isAssignableFrom(this);
- }
-
- /**
- * @return whether or not this is an Observable type such as ObservableMap, ObservableList,
- * or Observable.
- */
- public boolean isObservable() {
- ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
- return modelAnalyzer.getObservableType().isAssignableFrom(this)
- || modelAnalyzer.getObservableListType().isAssignableFrom(this)
- || modelAnalyzer.getObservableMapType().isAssignableFrom(this)
- || (modelAnalyzer.getLiveDataType() != null
- && modelAnalyzer.getLiveDataType().isAssignableFrom(this));
- }
-
- /**
- * @return whether or not this is an ObservableField, or any of the primitive versions
- * such as ObservableBoolean and ObservableInt
- */
- public boolean isObservableField() {
- ModelClass erasure = erasure();
- for (ModelClass observableField : ModelAnalyzer.getInstance().getObservableFieldTypes()) {
- if (observableField.isAssignableFrom(erasure)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return whether or not this is a LiveData
- */
- public boolean isLiveData() {
- ModelClass liveDataType = ModelAnalyzer.getInstance().getLiveDataType();
- return liveDataType != null && liveDataType.isAssignableFrom(erasure());
- }
-
- /**
- * @return whether or not this is a MutableLiveData
- */
- public boolean isMutableLiveData() {
- ModelClass mutableLiveDataType = ModelAnalyzer.getInstance().getMutableLiveDataType();
- return mutableLiveDataType != null && mutableLiveDataType.isAssignableFrom(erasure());
- }
-
- /**
- * @return the name of the simple getter method when this is an ObservableField or LiveData or
- * {@code null} for any other type
- */
- public String getObservableGetterName() {
- if (isObservableField()) {
- return "get";
- } else if (isLiveData()) {
- return "getValue";
- } else {
- return null;
- }
- }
-
- /**
- * @return the name of the simple setter method when this is an ObservableField or
- * MutableLiveData or {@code null} for any other type.
- */
- public String getObservableSetterName() {
- if (isObservableField()) {
- return "set";
- } else if (isMutableLiveData()) {
- return "setValue";
- } else {
- return null;
- }
- }
-
- /**
- * @return whether or not this ModelClass represents a void
- */
- public abstract boolean isVoid();
-
- /**
- * When this is a boxed type, such as Integer, this will return the unboxed value,
- * such as int. If this is not a boxed type, this is returned.
- *
- * @return The unboxed type of the class that this ModelClass represents or this if it isn't a
- * boxed type.
- */
- public abstract ModelClass unbox();
-
- /**
- * When this is a primitive type, such as boolean, this will return the boxed value,
- * such as Boolean. If this is not a primitive type, this is returned.
- *
- * @return The boxed type of the class that this ModelClass represents or this if it isn't a
- * primitive type.
- */
- public abstract ModelClass box();
-
- /**
- * Returns whether or not the type associated with <code>that</code> can be assigned to
- * the type associated with this ModelClass. If this and that only require boxing or unboxing
- * then true is returned.
- *
- * @param that the ModelClass to compare.
- * @return true if <code>that</code> requires only boxing or if <code>that</code> is an
- * implementation of or subclass of <code>this</code>.
- */
- public abstract boolean isAssignableFrom(ModelClass that);
-
- /**
- * Returns an array containing all public methods (or protected if allowProtected is true)
- * on the type represented by this ModelClass with the name <code>name</code> and can
- * take the passed-in types as arguments. This will also work if the arguments match
- * VarArgs parameter.
- *
- * @param name The name of the method to find.
- * @param args The types that the method should accept.
- * @param staticOnly Whether only static methods should be returned or both instance methods
- * and static methods are valid.
- * @param allowProtected true if the method can be protected as well as public.
- * @param unwrapObservableFields true if the method should check for auto-unwrapping the
- * observable field.
- *
- * @return An array containing all public methods with the name <code>name</code> and taking
- * <code>args</code> parameters.
- */
- public ModelMethod[] getMethods(String name, List<ModelClass> args, boolean staticOnly,
- boolean allowProtected, boolean unwrapObservableFields) {
- ModelMethod[] methods = getDeclaredMethods();
- ArrayList<ModelMethod> matching = new ArrayList<ModelMethod>();
- for (ModelMethod method : methods) {
- if ((method.isPublic() || (allowProtected && method.isProtected()))
- && (!staticOnly || method.isStatic()) && name.equals(method.getName())
- && method.acceptsArguments(args, unwrapObservableFields)) {
- matching.add(method);
- }
- }
- return matching.toArray(new ModelMethod[matching.size()]);
- }
-
- /**
- * Returns all public instance methods with the given name and number of parameters.
- *
- * @param name The name of the method to find.
- * @param numParameters The number of parameters that the method should take
- * @return An array containing all public methods with the given name and number of parameters.
- */
- public ModelMethod[] getMethods(String name, int numParameters) {
- ModelMethod[] methods = getDeclaredMethods();
- ArrayList<ModelMethod> matching = new ArrayList<ModelMethod>();
- for (ModelMethod method : methods) {
- if (method.isPublic() && !method.isStatic() &&
- name.equals(method.getName()) &&
- method.getParameterTypes().length == numParameters) {
- matching.add(method);
- }
- }
- return matching.toArray(new ModelMethod[matching.size()]);
- }
-
- /**
- * Returns the public method with the name <code>name</code> with the parameters that
- * best match args. <code>staticOnly</code> governs whether a static or instance method
- * will be returned. If no matching method was found, null is returned.
- *
- * @param name The method name to find
- * @param args The arguments that the method should accept
- * @param staticOnly true if the returned method must be static or false if it does not
- * matter.
- * @param allowProtected true if the method can be protected as well as public.
- */
- public ModelMethod getMethod(String name, List<ModelClass> args, boolean staticOnly,
- boolean allowProtected) {
- return getMethod(name, args, staticOnly, allowProtected, false);
- }
-
- /**
- * Returns the public method with the name <code>name</code> with the parameters that
- * best match args. <code>staticOnly</code> governs whether a static or instance method
- * will be returned. If no matching method was found, null is returned.
- *
- * @param name The method name to find
- * @param args The arguments that the method should accept
- * @param staticOnly true if the returned method must be static or false if it does not
- * matter.
- * @param allowProtected true if the method can be protected as well as public.
- * @param unwrapObservableFields true if the method should check for auto-unwrapping the
- * observable field.
- */
- public ModelMethod getMethod(String name, List<ModelClass> args, boolean staticOnly,
- boolean allowProtected, boolean unwrapObservableFields) {
- ModelMethod[] methods = getMethods(name, args, staticOnly, allowProtected,
- unwrapObservableFields);
- L.d("looking methods for %s. static only ? %s . method count: %d", name, staticOnly,
- methods.length);
- for (ModelMethod method : methods) {
- L.d("method: %s, %s", method.getName(), method.isStatic());
- }
- if (methods.length == 0) {
- return null;
- }
- ModelMethod bestMethod = methods[0];
- for (int i = 1; i < methods.length; i++) {
- if (methods[i].isBetterArgMatchThan(bestMethod, args)) {
- bestMethod = methods[i];
- }
- }
- return bestMethod;
- }
-
- /**
- * If this represents a class, the super class that it extends is returned. If this
- * represents an interface, the interface that this extends is returned.
- * <code>null</code> is returned if this is not a class or interface, such as an int, or
- * if it is java.lang.Object or an interface that does not extend any other type.
- *
- * @return The class or interface that this ModelClass extends or null.
- */
- public abstract ModelClass getSuperclass();
-
- /**
- * @return A String representation of the class or interface that this represents, not
- * including any type arguments.
- */
- public String getCanonicalName() {
- return erasure().toJavaCode();
- }
-
- /**
- * @return The class or interface name of this type or the primitive type if it isn't a
- * reference type.
- */
- public String getSimpleName() {
- final String canonicalName = getCanonicalName();
- final int dotIndex = canonicalName.lastIndexOf('.');
- if (dotIndex >= 0) {
- return canonicalName.substring(dotIndex + 1);
- }
- return canonicalName;
- }
-
- /**
- * Returns this class type without any generic type arguments.
- * @return this class type without any generic type arguments.
- */
- public abstract ModelClass erasure();
-
- /**
- * Since when this class is available. Important for Binding expressions so that we don't
- * call non-existing APIs when setting UI.
- *
- * @return The SDK_INT where this method was added. If it is not a framework method, should
- * return 1.
- */
- public int getMinApi() {
- return SdkUtil.get().getMinApi(this);
- }
-
- /**
- * Returns the JNI description of the method which can be used to lookup it in SDK.
- * @see TypeUtil
- */
- public abstract String getJniDescription();
-
- /**
- * Returns a list of all abstract methods in the type.
- */
- @NotNull
- public List<ModelMethod> getAbstractMethods() {
- ArrayList<ModelMethod> abstractMethods = new ArrayList<ModelMethod>();
- ModelMethod[] methods = getDeclaredMethods();
- for (ModelMethod method : methods) {
- if (method.isAbstract()) {
- abstractMethods.add(method);
- }
- }
- return abstractMethods;
- }
-
- /**
- * Returns the getter method or field that the name refers to.
- * @param name The name of the field or the body of the method name -- can be name(),
- * getName(), or isName().
- * @param staticOnly Whether this should look for static methods and fields or instance
- * versions
- * @return the getter method or field that the name refers to or null if none can be found.
- */
- public Callable findGetterOrField(String name, boolean staticOnly) {
- if ("length".equals(name) && isArray()) {
- return new Callable(Type.FIELD, name, null,
- ModelAnalyzer.getInstance().loadPrimitive("int"), 0, 0, null, null);
- }
- String capitalized = StringUtils.capitalize(name);
- String[] methodNames = {
- "get" + capitalized,
- "is" + capitalized,
- name
- };
- for (String methodName : methodNames) {
- ModelMethod[] methods =
- getMethods(methodName, new ArrayList<ModelClass>(), staticOnly, false, false);
- for (ModelMethod method : methods) {
- if (method.isPublic() && (!staticOnly || method.isStatic()) &&
- !method.getReturnType(Arrays.asList(method.getParameterTypes())).isVoid()) {
- int flags = DYNAMIC;
- if (method.isStatic()) {
- flags |= STATIC;
- }
- @Nullable
- final BindableCompat bindable;
- if (method.isBindable()) {
- flags |= CAN_BE_INVALIDATED;
- bindable = method.getBindableAnnotation();
- } else {
- // if method is not bindable, look for a backing field
- final ModelField backingField = getField(name, true, method.isStatic());
- L.d("backing field for method %s is %s", method.getName(),
- backingField == null ? "NOT FOUND" : backingField.getName());
- if (backingField != null && backingField.isBindable()) {
- flags |= CAN_BE_INVALIDATED;
- bindable = backingField.getBindableAnnotation();
- } else {
- bindable = null;
- }
- }
- final ModelMethod setterMethod = findSetter(method, name);
- final String setterName = setterMethod == null ? null : setterMethod.getName();
- final Callable result = new Callable(Callable.Type.METHOD, methodName,
- setterName, method.getReturnType(null), method.getParameterTypes().length,
- flags, method, bindable);
- return result;
- }
- }
- }
-
- // could not find a method. Look for a public field
- ModelField publicField = null;
- if (staticOnly) {
- publicField = getField(name, false, true);
- } else {
- // first check non-static
- publicField = getField(name, false, false);
- if (publicField == null) {
- // check for static
- publicField = getField(name, false, true);
- }
- }
- if (publicField == null) {
- return null;
- }
- ModelClass fieldType = publicField.getFieldType();
- int flags = 0;
- String setterFieldName = name;
- if (publicField.isStatic()) {
- flags |= STATIC;
- }
- if (!publicField.isFinal()) {
- setterFieldName = null;
- flags |= DYNAMIC;
- }
- if (publicField.isBindable()) {
- flags |= CAN_BE_INVALIDATED;
- }
- return new Callable(Callable.Type.FIELD, name, setterFieldName, fieldType, 0, flags, null,
- publicField.getBindableAnnotation());
- }
-
- public ModelMethod findInstanceGetter(String name) {
- String capitalized = StringUtils.capitalize(name);
- String[] methodNames = {
- "get" + capitalized,
- "is" + capitalized,
- name
- };
- for (String methodName : methodNames) {
- ModelMethod[] methods =
- getMethods(methodName, new ArrayList<ModelClass>(), false, false, false);
- for (ModelMethod method : methods) {
- if (method.isPublic() && !method.isStatic() &&
- !method.getReturnType(Arrays.asList(method.getParameterTypes())).isVoid()) {
- return method;
- }
- }
- }
- return null;
- }
-
- private ModelField getField(String name, boolean allowPrivate, boolean isStatic) {
- ModelField[] fields = getDeclaredFields();
- for (ModelField field : fields) {
- boolean nameMatch = name.equals(field.getName()) ||
- name.equals(stripFieldName(field.getName()));
- if (nameMatch && field.isStatic() == isStatic &&
- (allowPrivate || field.isPublic())) {
- return field;
- }
- }
- return null;
- }
-
- private ModelMethod findSetter(ModelMethod getter, String originalName) {
- final String capitalized = StringUtils.capitalize(originalName);
- final String[] possibleNames;
- if (originalName.equals(getter.getName())) {
- possibleNames = new String[] { originalName, "set" + capitalized };
- } else if (getter.getName().startsWith("is")){
- possibleNames = new String[] { "set" + capitalized, "setIs" + capitalized };
- } else {
- possibleNames = new String[] { "set" + capitalized };
- }
- for (String name : possibleNames) {
- List<ModelMethod> methods = findMethods(name, getter.isStatic());
- ModelClass param = getter.getReturnType(null);
- for (ModelMethod method : methods) {
- ModelClass[] parameterTypes = method.getParameterTypes();
- if (parameterTypes != null && parameterTypes.length == 1 &&
- parameterTypes[0].equals(param) &&
- method.isStatic() == getter.isStatic()) {
- return method;
- }
- }
- }
- return null;
- }
-
- /**
- * Finds public methods that matches the given name exactly. These may be resolved into
- * listener methods during Expr.resolveListeners.
- */
- @NotNull
- public List<ModelMethod> findMethods(String name, boolean staticOnly) {
- ModelMethod[] methods = getDeclaredMethods();
- ArrayList<ModelMethod> matching = new ArrayList<ModelMethod>();
- for (ModelMethod method : methods) {
- if (method.getName().equals(name) && (!staticOnly || method.isStatic()) &&
- method.isPublic()) {
- matching.add(method);
- }
- }
- return matching;
- }
-
- public boolean isIncomplete() {
- if (isTypeVar() || isWildcard()) {
- return true;
- }
- List<ModelClass> typeArgs = getTypeArguments();
- if (typeArgs != null) {
- for (ModelClass typeArg : typeArgs) {
- if (typeArg.isIncomplete()) {
- return true;
- }
- }
- }
- return false;
- }
-
- protected abstract ModelField[] getDeclaredFields();
-
- protected abstract ModelMethod[] getDeclaredMethods();
-
- private static String stripFieldName(String fieldName) {
- // TODO: Make this configurable through IntelliJ
- if (fieldName.length() > 2) {
- final char start = fieldName.charAt(2);
- if (fieldName.startsWith("m_") && Character.isJavaIdentifierStart(start)) {
- return Character.toLowerCase(start) + fieldName.substring(3);
- }
- }
- if (fieldName.length() > 1) {
- final char start = fieldName.charAt(1);
- final char fieldIdentifier = fieldName.charAt(0);
- final boolean strip;
- if (fieldIdentifier == '_') {
- strip = true;
- } else if (fieldIdentifier == 'm' && Character.isJavaIdentifierStart(start) &&
- !Character.isLowerCase(start)) {
- strip = true;
- } else {
- strip = false; // not mUppercase format
- }
- if (strip) {
- return Character.toLowerCase(start) + fieldName.substring(2);
- }
- }
- return fieldName;
- }
-
- public TypeName getTypeName() {
- // implementation only so that PSI model doesn't break
- return ExtKt.toTypeName(
- toJavaCode(),
- false);
- }
-
- @Override
- public boolean equals(Object that) {
- if (that instanceof ModelClass) {
- TypeName thisTypeName = getTypeName();
- TypeName thatTypeName = ((ModelClass) that).getTypeName();
- return thisTypeName.equals(thatTypeName);
- }
- return false;
- }
-
- public boolean isKotlinUnit() {
- return "kotlin.Unit".equals(getTypeName().toString());
- }
-}
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.kt b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.kt
new file mode 100644
index 00000000..a44fcf4c
--- /dev/null
+++ b/compiler/src/main/java/android/databinding/tool/reflection/ModelClass.kt
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2015 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 android.databinding.tool.BindableCompat
+import android.databinding.tool.ext.toTypeName
+import android.databinding.tool.reflection.Callable.Type
+import android.databinding.tool.util.L
+import android.databinding.tool.util.StringUtils
+
+import com.squareup.javapoet.TypeName
+
+import java.util.ArrayList
+import java.util.Arrays
+
+import android.databinding.tool.reflection.Callable.CAN_BE_INVALIDATED
+import android.databinding.tool.reflection.Callable.DYNAMIC
+import android.databinding.tool.reflection.Callable.STATIC
+
+@Suppress("EqualsOrHashCode")
+abstract class ModelClass {
+
+ /**
+ * @return whether this ModelClass represents an array.
+ */
+ abstract val isArray: Boolean
+
+ /**
+ * For arrays, lists, and maps, this returns the contained value. For other types, null
+ * is returned.
+ *
+ * @return The component type for arrays, the value type for maps, and the element type
+ * for lists.
+ */
+ abstract val componentType: ModelClass?
+
+ /**
+ * @return Whether or not this ModelClass can be treated as a List. This means
+ * it is a java.util.List, or one of the Sparse*Array classes.
+ */
+ val isList by lazy(LazyThreadSafetyMode.NONE) {
+ ModelAnalyzer.getInstance().listTypes.any {
+ it.isAssignableFrom(this)
+ }
+ }
+
+ /**
+ * @return whether or not this ModelClass can be considered a Map or not.
+ */
+ val isMap by lazy(LazyThreadSafetyMode.NONE) {
+ ModelAnalyzer.getInstance().mapType.isAssignableFrom(erasure())
+ }
+
+ /**
+ * @return whether or not this ModelClass is a java.lang.String.
+ */
+ val isString by lazy(LazyThreadSafetyMode.NONE) {
+ "java.lang.String" == typeName.toString()
+ }
+ /**
+ * @return whether or not this ModelClass represents a Reference type.
+ */
+ abstract val isNullable: Boolean
+
+ /**
+ * @return whether or not this ModelClass represents a primitive type.
+ */
+ abstract val isPrimitive: Boolean
+
+ /**
+ * @return whether or not this ModelClass represents a Java boolean
+ */
+ abstract val isBoolean: Boolean
+
+ /**
+ * @return whether or not this ModelClass represents a Java char
+ */
+ abstract val isChar: Boolean
+
+ /**
+ * @return whether or not this ModelClass represents a Java byte
+ */
+ abstract val isByte: Boolean
+
+ /**
+ * @return whether or not this ModelClass represents a Java short
+ */
+ abstract val isShort: Boolean
+
+ /**
+ * @return whether or not this ModelClass represents a Java int
+ */
+ abstract val isInt: Boolean
+
+ /**
+ * @return whether or not this ModelClass represents a Java long
+ */
+ abstract val isLong: Boolean
+
+ /**
+ * @return whether or not this ModelClass represents a Java float
+ */
+ abstract val isFloat: Boolean
+
+ /**
+ * @return whether or not this ModelClass represents a Java double
+ */
+ abstract val isDouble: Boolean
+
+ /**
+ * @return whether or not this has type parameters
+ */
+ abstract val isGeneric: Boolean
+
+ /**
+ * @return a list of Generic type parameters for the class. For example, if the class
+ * is List&lt;T>, then the return value will be a list containing T. null is returned
+ * if this is not a generic type
+ */
+ abstract val typeArguments: List<ModelClass>?
+
+ /**
+ * @return whether this is a type variable. For example, in List&lt;T>, T is a type variable.
+ * However, List&lt;String>, String is not a type variable.
+ */
+ abstract val isTypeVar: Boolean
+
+ /**
+ * @return whether this is a wildcard type argument or not.
+ */
+ abstract val isWildcard: Boolean
+
+ /**
+ * @return whether or not this ModelClass is java.lang.Object and not a primitive or subclass.
+ */
+ val isObject by lazy(LazyThreadSafetyMode.NONE) {
+ "java.lang.Object" == typeName.toString()
+ }
+
+ /**
+ * @return whether or not this ModelClass is an interface
+ */
+ abstract val isInterface: Boolean
+
+ /**
+ * @return whether or not his is a ViewDataBinding subclass.
+ */
+ val isViewDataBinding by lazy(LazyThreadSafetyMode.NONE) {
+ ModelAnalyzer.getInstance().viewDataBindingType!!.isAssignableFrom(this)
+ }
+
+ /**
+ * @return whether or not this is an Observable type such as ObservableMap, ObservableList,
+ * or Observable.
+ */
+ // open for injected
+ open val isObservable: Boolean
+ get() {
+ val modelAnalyzer = ModelAnalyzer.getInstance()
+ return modelAnalyzer.observableType.isAssignableFrom(this) ||
+ modelAnalyzer.observableListType.isAssignableFrom(this) ||
+ modelAnalyzer.observableMapType.isAssignableFrom(this) ||
+ (modelAnalyzer.liveDataType?.isAssignableFrom(this) ?: false)
+ }
+
+ /**
+ * @return whether or not this is an ObservableField, or any of the primitive versions
+ * such as ObservableBoolean and ObservableInt
+ */
+ val isObservableField by lazy(LazyThreadSafetyMode.NONE) {
+ val erasure = erasure()
+ ModelAnalyzer.getInstance().observableFieldTypes.any {
+ it.isAssignableFrom(erasure)
+ }
+ }
+
+ /**
+ * @return whether or not this is a LiveData
+ */
+ val isLiveData by lazy(LazyThreadSafetyMode.NONE) {
+ ModelAnalyzer.getInstance().liveDataType?.isAssignableFrom(erasure()) ?: false
+ }
+
+ /**
+ * @return whether or not this is a MutableLiveData
+ */
+ @Suppress("MemberVisibilityCanBePrivate")
+ val isMutableLiveData by lazy(LazyThreadSafetyMode.NONE) {
+ ModelAnalyzer.getInstance().mutableLiveDataType?.isAssignableFrom(erasure()) ?: false
+ }
+
+ /**
+ * @return the name of the simple getter method when this is an ObservableField or LiveData or
+ * `null` for any other type
+ */
+ val observableGetterName: String?
+ get() = when {
+ isObservableField -> "get"
+ isLiveData -> "getValue"
+ else -> null
+ }
+
+ /**
+ * @return the name of the simple setter method when this is an ObservableField or
+ * MutableLiveData or `null` for any other type.
+ */
+ val observableSetterName: String?
+ get() = when {
+ isObservableField -> "set"
+ isMutableLiveData -> "setValue"
+ else -> null
+ }
+
+ /**
+ * @return whether or not this ModelClass represents a void
+ */
+ abstract val isVoid: Boolean
+
+ /**
+ * If this represents a class, the super class that it extends is returned. If this
+ * represents an interface, the interface that this extends is returned.
+ * `null` is returned if this is not a class or interface, such as an int, or
+ * if it is java.lang.Object or an interface that does not extend any other type.
+ *
+ * @return The class or interface that this ModelClass extends or null.
+ */
+ abstract val superclass: ModelClass?
+
+ /**
+ * @return A String representation of the class or interface that this represents, not
+ * including any type arguments.
+ */
+ open val canonicalName: String by lazy(LazyThreadSafetyMode.NONE) {
+ erasure().toJavaCode()
+ }
+
+ /**
+ * @return The class or interface name of this type or the primitive type if it isn't a
+ * reference type.
+ */
+ val simpleName: String by lazy(LazyThreadSafetyMode.NONE) {
+ canonicalName.substringAfterLast('.')
+ }
+
+ /**
+ * Since when this class is available. Important for Binding expressions so that we don't
+ * call non-existing APIs when setting UI.
+ *
+ * @return The SDK_INT where this method was added. If it is not a framework method, should
+ * return 1.
+ */
+ open val minApi: Int by lazy(LazyThreadSafetyMode.NONE) {
+ SdkUtil.get().getMinApi(this)
+ }
+
+ /**
+ * Returns the JNI description of the method which can be used to lookup it in SDK.
+ * @see TypeUtil
+ */
+ abstract val jniDescription: String
+
+ /**
+ * Returns a list of all abstract methods in the type.
+ */
+ val abstractMethods: List<ModelMethod> by lazy(LazyThreadSafetyMode.NONE) {
+ declaredMethods.filter {
+ it.isAbstract
+ }
+ }
+
+ val isIncomplete: Boolean
+ get() {
+ if (isTypeVar || isWildcard) {
+ return true
+ }
+ val typeArgs = typeArguments
+ if (typeArgs != null) {
+ for (typeArg in typeArgs) {
+ if (typeArg.isIncomplete) {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
+ abstract val declaredFields: Array<ModelField>
+
+ abstract val declaredMethods: Array<ModelMethod>
+
+ // implementation only so that PSI model doesn't break
+ open val typeName: TypeName
+ get() = toJavaCode().toTypeName(false)
+
+ val isKotlinUnit by lazy(LazyThreadSafetyMode.NONE) {
+ "kotlin.Unit" == typeName.toString()
+ }
+
+ abstract fun toJavaCode(): String
+
+ /**
+ * @return whether or not this ModelClass type extends ViewStub.
+ */
+ val extendsViewStub by lazy(LazyThreadSafetyMode.NONE) {
+ ModelAnalyzer.getInstance().viewStubType!!.isAssignableFrom(this)
+ }
+
+ /**
+ * When this is a boxed type, such as Integer, this will return the unboxed value,
+ * such as int. If this is not a boxed type, this is returned.
+ *
+ * @return The unboxed type of the class that this ModelClass represents or this if it isn't a
+ * boxed type.
+ */
+ abstract fun unbox(): ModelClass
+
+ /**
+ * When this is a primitive type, such as boolean, this will return the boxed value,
+ * such as Boolean. If this is not a primitive type, this is returned.
+ *
+ * @return The boxed type of the class that this ModelClass represents or this if it isn't a
+ * primitive type.
+ */
+ abstract fun box(): ModelClass
+
+ /**
+ * Returns whether or not the type associated with `that` can be assigned to
+ * the type associated with this ModelClass. If this and that only require boxing or unboxing
+ * then true is returned.
+ *
+ * @param that the ModelClass to compare.
+ * @return true if `that` requires only boxing or if `that` is an
+ * implementation of or subclass of `this`.
+ */
+ abstract fun isAssignableFrom(that: ModelClass?): Boolean
+
+ /**
+ * Returns an array containing all public methods (or protected if allowProtected is true)
+ * on the type represented by this ModelClass with the name `name` and can
+ * take the passed-in types as arguments. This will also work if the arguments match
+ * VarArgs parameter.
+ *
+ * @param name The name of the method to find.
+ * @param args The types that the method should accept.
+ * @param staticOnly Whether only static methods should be returned or both instance methods
+ * and static methods are valid.
+ * @param allowProtected true if the method can be protected as well as public.
+ * @param unwrapObservableFields true if the method should check for auto-unwrapping the
+ * observable field.
+ *
+ * @return An array containing all public methods with the name `name` and taking
+ * `args` parameters.
+ */
+ private fun getMethods(name: String, args: List<ModelClass>, staticOnly: Boolean,
+ allowProtected: Boolean, unwrapObservableFields: Boolean): Array<ModelMethod> {
+ return declaredMethods.filter { method ->
+ (method.isPublic || (allowProtected && method.isProtected))
+ && (!staticOnly || method.isStatic)
+ && name == method.name
+ && method.acceptsArguments(args, unwrapObservableFields)
+ }.toTypedArray()
+ }
+
+ /**
+ * Returns all public instance methods with the given name and number of parameters.
+ *
+ * @param name The name of the method to find.
+ * @param numParameters The number of parameters that the method should take
+ * @return An array containing all public methods with the given name and number of parameters.
+ */
+ fun getMethods(name: String, numParameters: Int): Array<ModelMethod> {
+ return declaredMethods.filter { method ->
+ method.isPublic &&
+ !method.isStatic &&
+ name == method.name &&
+ method.parameterTypes.size == numParameters
+ }.toTypedArray()
+ }
+
+ /**
+ * Returns the public method with the name `name` with the parameters that
+ * best match args. `staticOnly` governs whether a static or instance method
+ * will be returned. If no matching method was found, null is returned.
+ *
+ * @param name The method name to find
+ * @param args The arguments that the method should accept
+ * @param staticOnly true if the returned method must be static or false if it does not
+ * matter.
+ * @param allowProtected true if the method can be protected as well as public.
+ * @param unwrapObservableFields true if the method should check for auto-unwrapping the
+ * observable field.
+ */
+ @JvmOverloads
+ fun getMethod(name: String,
+ args: List<ModelClass>,
+ staticOnly: Boolean,
+ allowProtected: Boolean,
+ unwrapObservableFields: Boolean = false
+ ): ModelMethod? {
+ val methods = getMethods(name = name,
+ args = args,
+ staticOnly = staticOnly,
+ allowProtected = allowProtected,
+ unwrapObservableFields = unwrapObservableFields)
+ L.d("looking methods for %s. static only ? %s . method count: %d", name, staticOnly,
+ methods.size)
+ for (method in methods) {
+ L.d("method: %s, %s", method.name, method.isStatic)
+ }
+ if (methods.isEmpty()) {
+ return null
+ }
+ var bestMethod = methods[0]
+ for (i in 1 until methods.size) {
+ if (methods[i].isBetterArgMatchThan(bestMethod, args)) {
+ bestMethod = methods[i]
+ }
+ }
+ return bestMethod
+ }
+
+ /**
+ * Returns this class type without any generic type arguments.
+ * @return this class type without any generic type arguments.
+ */
+ abstract fun erasure(): ModelClass
+
+ /**
+ * Returns the getter method or field that the name refers to.
+ * @param name The name of the field or the body of the method name -- can be name(),
+ * getName(), or isName().
+ * @param staticOnly Whether this should look for static methods and fields or instance
+ * versions
+ * @return the getter method or field that the name refers to or null if none can be found.
+ */
+ fun findGetterOrField(name: String, staticOnly: Boolean): Callable? {
+ if ("length" == name && isArray) {
+ return Callable(Type.FIELD, name, null,
+ ModelAnalyzer.getInstance().loadPrimitive("int"), 0, 0, null, null)
+ }
+ val capitalized = StringUtils.capitalize(name)
+ val methodNames = arrayOf("get" + capitalized!!, "is$capitalized", name)
+ for (methodName in methodNames) {
+ val methods = getMethods(methodName, ArrayList(), staticOnly, false, false)
+ for (method in methods) {
+ if (method.isPublic && (!staticOnly || method.isStatic) &&
+ !method.getReturnType(Arrays.asList(*method.parameterTypes)).isVoid) {
+ var flags = DYNAMIC
+ if (method.isStatic) {
+ flags = flags or STATIC
+ }
+ val bindable: BindableCompat?
+ if (method.isBindable) {
+ flags = flags or CAN_BE_INVALIDATED
+ bindable = method.bindableAnnotation
+ } else {
+ // if method is not bindable, look for a backing field
+ val backingField = getField(name, true, method.isStatic)
+ L.d("backing field for method %s is %s", method.name,
+ if (backingField == null) "NOT FOUND" else backingField.name)
+ if (backingField != null && backingField.isBindable) {
+ flags = flags or CAN_BE_INVALIDATED
+ bindable = backingField.bindableAnnotation
+ } else {
+ bindable = null
+ }
+ }
+ val setterMethod = findSetter(method, name)
+ val setterName = setterMethod?.name
+ return Callable(Type.METHOD, methodName,
+ setterName, method.getReturnType(null), method.parameterTypes.size,
+ flags, method, bindable)
+ }
+ }
+ }
+
+ // could not find a method. Look for a public field
+ var publicField: ModelField?
+ if (staticOnly) {
+ publicField = getField(name, false, true)
+ } else {
+ // first check non-static
+ publicField = getField(name, false, false)
+ if (publicField == null) {
+ // check for static
+ publicField = getField(name, false, true)
+ }
+ }
+ if (publicField == null) {
+ return null
+ }
+ val fieldType = publicField.fieldType
+ var flags = 0
+ var setterFieldName: String? = name
+ if (publicField.isStatic) {
+ flags = flags or STATIC
+ }
+ if (!publicField.isFinal) {
+ setterFieldName = null
+ flags = flags or DYNAMIC
+ }
+ if (publicField.isBindable) {
+ flags = flags or CAN_BE_INVALIDATED
+ }
+ return Callable(Callable.Type.FIELD, name, setterFieldName, fieldType, 0, flags, null,
+ publicField.bindableAnnotation)
+ }
+
+ fun findInstanceGetter(name: String): ModelMethod? {
+ val capitalized = StringUtils.capitalize(name)
+ val methodNames = arrayOf("get" + capitalized!!, "is$capitalized", name)
+ for (methodName in methodNames) {
+ val methods = getMethods(methodName, ArrayList(), false, false, false)
+ for (method in methods) {
+ if (method.isPublic && !method.isStatic &&
+ !method.getReturnType(Arrays.asList(*method.parameterTypes)).isVoid) {
+ return method
+ }
+ }
+ }
+ return null
+ }
+
+ private fun getField(name: String, allowPrivate: Boolean, isStatic: Boolean): ModelField? {
+ val fields = declaredFields
+ for (field in fields) {
+ val nameMatch = name == field.name || name == stripFieldName(field.name)
+ if (nameMatch && field.isStatic == isStatic &&
+ (allowPrivate || field.isPublic)) {
+ return field
+ }
+ }
+ return null
+ }
+
+ private fun findSetter(getter: ModelMethod, originalName: String): ModelMethod? {
+ val capitalized = StringUtils.capitalize(originalName)
+ val possibleNames: Array<String>
+ possibleNames = when {
+ originalName == getter.name -> arrayOf(originalName, "set" + capitalized!!)
+ getter.name.startsWith("is") -> arrayOf("set" + capitalized!!, "setIs$capitalized")
+ else -> arrayOf("set" + capitalized!!)
+ }
+ for (name in possibleNames) {
+ val methods = findMethods(name, getter.isStatic)
+ val param = getter.getReturnType(null)
+ for (method in methods) {
+ val parameterTypes = method.parameterTypes
+ if (parameterTypes != null && parameterTypes.size == 1 &&
+ parameterTypes[0] == param &&
+ method.isStatic == getter.isStatic) {
+ return method
+ }
+ }
+ }
+ return null
+ }
+
+ /**
+ * Finds public methods that matches the given name exactly. These may be resolved into
+ * listener methods during Expr.resolveListeners.
+ */
+ fun findMethods(name: String, staticOnly: Boolean): List<ModelMethod> {
+ val methods = declaredMethods
+ val matching = ArrayList<ModelMethod>()
+ for (method in methods) {
+ if (method.name == name && (!staticOnly || method.isStatic) &&
+ method.isPublic) {
+ matching.add(method)
+ }
+ }
+ return matching
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other is ModelClass) {
+ val thisTypeName = typeName
+ val thatTypeName = other.typeName
+ return thisTypeName == thatTypeName
+ }
+ return false
+ }
+
+ companion object {
+ @JvmField
+ val BOX_MAPPING = mapOf(
+ Int::class.javaPrimitiveType!! to java.lang.Integer::class.java,
+ Long::class.javaPrimitiveType!! to java.lang.Long::class.java,
+ Short::class.javaPrimitiveType!! to java.lang.Short::class.java,
+ Byte::class.javaPrimitiveType!! to java.lang.Byte::class.java,
+ Char::class.javaPrimitiveType!! to java.lang.Character::class.java,
+ Double::class.javaPrimitiveType!! to java.lang.Double::class.java,
+ Float::class.javaPrimitiveType!! to java.lang.Float::class.java,
+ Boolean::class.javaPrimitiveType!! to java.lang.Boolean::class.java
+ )
+
+ private fun stripFieldName(fieldName: String): String {
+ // TODO: Make this configurable through IntelliJ
+ if (fieldName.length > 2) {
+ val start = fieldName[2]
+ if (fieldName.startsWith("m_") && Character.isJavaIdentifierStart(start)) {
+ return Character.toLowerCase(start) + fieldName.substring(3)
+ }
+ }
+ if (fieldName.length > 1) {
+ val start = fieldName[1]
+ val fieldIdentifier = fieldName[0]
+ val strip: Boolean
+ strip = if (fieldIdentifier == '_') {
+ true
+ } else fieldIdentifier == 'm' && Character.isJavaIdentifierStart(start) &&
+ !Character.isLowerCase(start)
+ if (strip) {
+ return Character.toLowerCase(start) + fieldName.substring(2)
+ }
+ }
+ return fieldName
+ }
+ }
+} \ No newline at end of file
diff --git a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.kt b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.kt
index 56a4efc9..eeec3fe0 100644
--- a/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.kt
+++ b/compiler/src/main/java/android/databinding/tool/reflection/annotation/AnnotationClass.kt
@@ -15,25 +15,14 @@
*/
package android.databinding.tool.reflection.annotation
-import android.databinding.tool.reflection.ModelAnalyzer
-import android.databinding.tool.reflection.ModelClass
-import android.databinding.tool.reflection.ModelField
-import android.databinding.tool.reflection.ModelMethod
-import android.databinding.tool.reflection.TypeUtil
+import android.databinding.tool.reflection.*
import android.databinding.tool.util.L
-
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.TypeName
-
-import java.util.ArrayList
-
+import java.util.*
import javax.lang.model.element.ElementKind
import javax.lang.model.element.TypeElement
-import javax.lang.model.type.ArrayType
-import javax.lang.model.type.DeclaredType
-import javax.lang.model.type.PrimitiveType
-import javax.lang.model.type.TypeKind
-import javax.lang.model.type.TypeMirror
+import javax.lang.model.type.*
import javax.lang.model.util.ElementFilter
import javax.lang.model.util.Elements
import javax.lang.model.util.Types
@@ -61,7 +50,11 @@ class AnnotationClass(
}
}
- override fun getComponentType(): AnnotationClass? {
+ override val componentType by lazy(LazyThreadSafetyMode.NONE) {
+ computeComponentType() as ModelClass?
+ }
+
+ private fun computeComponentType(): AnnotationClass? {
val component: TypeMirror?
when {
isArray -> component = (typeMirror as ArrayType).componentType
@@ -118,59 +111,56 @@ class AnnotationClass(
return foundInterface as DeclaredType?
}
- override fun isNullable(): Boolean {
- return when (typeMirror.kind) {
+ override val isNullable: Boolean
+ get() = when (typeMirror.kind) {
TypeKind.ARRAY, TypeKind.DECLARED, TypeKind.NULL -> true
else -> false
}
- }
- override fun isPrimitive(): Boolean {
- return when (typeMirror.kind) {
+ override val isPrimitive: Boolean
+ get() = when (typeMirror.kind) {
TypeKind.BOOLEAN, TypeKind.BYTE, TypeKind.SHORT, TypeKind.INT,
TypeKind.LONG, TypeKind.CHAR, TypeKind.FLOAT, TypeKind.DOUBLE -> true
else -> false
}
- }
- override fun isArray() = typeMirror.kind == TypeKind.ARRAY
+ override val isArray = typeMirror.kind == TypeKind.ARRAY
- override fun isBoolean()= typeMirror.kind == TypeKind.BOOLEAN
+ override val isBoolean = typeMirror.kind == TypeKind.BOOLEAN
- override fun isChar() = typeMirror.kind == TypeKind.CHAR
+ override val isChar = typeMirror.kind == TypeKind.CHAR
- override fun isByte() = typeMirror.kind == TypeKind.BYTE
+ override val isByte = typeMirror.kind == TypeKind.BYTE
- override fun isShort() = typeMirror.kind == TypeKind.SHORT
+ override val isShort = typeMirror.kind == TypeKind.SHORT
- override fun isInt() = typeMirror.kind == TypeKind.INT
+ override val isInt = typeMirror.kind == TypeKind.INT
- override fun isLong() = typeMirror.kind == TypeKind.LONG
+ override val isLong = typeMirror.kind == TypeKind.LONG
- override fun isFloat() = typeMirror.kind == TypeKind.FLOAT
+ override val isFloat = typeMirror.kind == TypeKind.FLOAT
- override fun isDouble() = typeMirror.kind == TypeKind.DOUBLE
+ override val isDouble = typeMirror.kind == TypeKind.DOUBLE
- override fun isTypeVar() = typeMirror.kind == TypeKind.TYPEVAR
+ override val isTypeVar = typeMirror.kind == TypeKind.TYPEVAR
- override fun isWildcard() = typeMirror.kind == TypeKind.WILDCARD
+ override val isWildcard = typeMirror.kind == TypeKind.WILDCARD
- override fun isInterface() = typeMirror.kind == TypeKind.DECLARED &&
- (typeMirror as DeclaredType).asElement().kind == ElementKind.INTERFACE
+ override val isVoid = typeMirror.kind == TypeKind.VOID
- override fun isVoid() = typeMirror.kind == TypeKind.VOID
+ override val isInterface by lazy(LazyThreadSafetyMode.NONE) {
+ typeMirror.kind == TypeKind.DECLARED &&
+ (typeMirror as DeclaredType).asElement().kind == ElementKind.INTERFACE
+ }
- override fun isGeneric(): Boolean {
- var isGeneric = false
- if (typeMirror.kind == TypeKind.DECLARED) {
- val declaredType = typeMirror as DeclaredType
- val typeArguments = declaredType.typeArguments
- isGeneric = typeArguments != null && !typeArguments.isEmpty()
- }
- return isGeneric
+ override val isGeneric by lazy(LazyThreadSafetyMode.NONE) {
+ typeMirror.kind == TypeKind.DECLARED &&
+ (typeMirror as DeclaredType)
+ .typeArguments
+ .isNotEmpty()
}
- override fun getMinApi(): Int {
+ private fun extractTargetApi(): Int? {
if (typeMirror.kind == TypeKind.DECLARED) {
val declaredType = typeMirror as DeclaredType
val annotations = elementUtils.getAllAnnotationMirrors(declaredType.asElement())
@@ -186,45 +176,54 @@ class AnnotationClass(
}
}
}
- return super.getMinApi()
+ return null
}
- override fun getTypeArguments(): List<ModelClass>? {
- var types: MutableList<ModelClass>? = null
+ override val minApi by lazy(LazyThreadSafetyMode.NONE) {
+ extractTargetApi() ?: super.minApi
+ }
+
+ override val typeArguments by lazy(LazyThreadSafetyMode.NONE) {
if (typeMirror.kind == TypeKind.DECLARED) {
- val declaredType = typeMirror as DeclaredType
- val typeArguments = declaredType.typeArguments
- if (typeArguments != null && !typeArguments.isEmpty()) {
- types = ArrayList()
- for (typeMirror in typeArguments) {
- types.add(AnnotationClass(typeMirror))
+ (typeMirror as? DeclaredType)?.typeArguments?.map {
+ AnnotationClass(it)
+ }?.let {
+ if (it.isEmpty()) {
+ null
+ } else {
+ it
}
}
+ } else {
+ null
}
- return types
}
- override fun unbox(): AnnotationClass {
+ private val computedUnbox by lazy(LazyThreadSafetyMode.NONE) {
if (!isNullable) {
- return this
- }
- return try {
- AnnotationClass(typeUtils.unboxedType(typeMirror))
- } catch (e: IllegalArgumentException) {
- // I'm being lazy. This is much easier than checking every type.
this
+ } else {
+ try {
+ AnnotationClass(typeUtils.unboxedType(typeMirror))
+ } catch (e: IllegalArgumentException) {
+ // I'm being lazy. This is much easier than checking every type.
+ this
+ }
}
-
}
- override fun box(): AnnotationClass {
- return if (!isPrimitive) {
+ override fun unbox() = computedUnbox
+
+ private val computedBox by lazy(LazyThreadSafetyMode.NONE) {
+ if (!isPrimitive) {
this
} else {
AnnotationClass(typeUtils.boxedClass(typeMirror as PrimitiveType).asType())
}
}
+ override fun box() = computedBox
+
override fun isAssignableFrom(that: ModelClass?): Boolean {
var other: ModelClass? = that
while (other != null && other !is AnnotationClass) {
@@ -240,79 +239,89 @@ class AnnotationClass(
return typeUtils.isAssignable(thatAnnotationClass.typeMirror, this.typeMirror)
}
- public override fun getDeclaredMethods(): Array<ModelMethod> {
- return if (typeMirror.kind == TypeKind.DECLARED) {
+ override val declaredMethods by lazy(LazyThreadSafetyMode.NONE) {
+ if (typeMirror.kind == TypeKind.DECLARED) {
val declaredType = typeMirror as DeclaredType
val elementUtils = elementUtils
val typeElement = declaredType.asElement() as TypeElement
val members = elementUtils.getAllMembers(typeElement)
val methods = ElementFilter.methodsIn(members)
Array(methods.size) {
- AnnotationMethod(declaredType, methods[it])
+ AnnotationMethod(declaredType, methods[it]) as ModelMethod
}
} else {
emptyArray()
}
}
- override fun getSuperclass(): AnnotationClass? {
- if (typeMirror.kind == TypeKind.DECLARED) {
- val declaredType = typeMirror as DeclaredType
- val typeElement = declaredType.asElement() as TypeElement
- val superClass = typeElement.superclass
- if (superClass.kind == TypeKind.DECLARED) {
- return AnnotationClass(superClass)
- }
+ override val superclass by lazy(LazyThreadSafetyMode.NONE) {
+ val superClass = if (typeMirror.kind == TypeKind.DECLARED) {
+ ((typeMirror as DeclaredType).asElement() as? TypeElement)?.superclass
+ } else {
+ null
+ }
+ if (superClass?.kind == TypeKind.DECLARED) {
+ AnnotationClass(superClass)
+ } else {
+ null
}
- return null
}
- override fun getCanonicalName(): String {
- return AnnotationTypeUtil.getInstance().toJava(typeUtils.erasure(typeMirror))
+ private val computedCanonicalName by lazy(LazyThreadSafetyMode.NONE) {
+ AnnotationTypeUtil.getInstance().toJava(typeUtils.erasure(typeMirror))
}
- override fun erasure(): ModelClass {
+ override val canonicalName: String = computedCanonicalName
+
+ private val computedErasure by lazy(LazyThreadSafetyMode.NONE) {
val erasure = typeUtils.erasure(typeMirror)
- return if (erasure === typeMirror) {
+ if (erasure === typeMirror) {
this
} else {
AnnotationClass(erasure)
}
}
- override fun getJniDescription(): String {
- return TypeUtil.getInstance().getDescription(this)
+ override fun erasure(): ModelClass = computedErasure
+
+ private val computedJniDescription by lazy(LazyThreadSafetyMode.NONE) {
+ TypeUtil.getInstance().getDescription(this)
}
- override fun getDeclaredFields(): Array<ModelField> {
- val declaredFields: Array<ModelField>
- declaredFields = if (typeMirror.kind == TypeKind.DECLARED) {
+ override val jniDescription: String
+ get() = computedJniDescription
+
+ override val declaredFields by lazy(LazyThreadSafetyMode.NONE) {
+ if (typeMirror.kind == TypeKind.DECLARED) {
val declaredType = typeMirror as DeclaredType
val elementUtils = elementUtils
val typeElement = declaredType.asElement() as TypeElement
val members = elementUtils.getAllMembers(typeElement)
val fields = ElementFilter.fieldsIn(members)
Array(fields.size) {
- AnnotationField(declaredType, fields[it])
+ AnnotationField(declaredType, fields[it]) as ModelField
}
} else {
emptyArray()
}
- return declaredFields
}
- override fun toString(): String {
- return AnnotationTypeUtil.getInstance().toJava(typeMirror)
+ private val javaCodeRepresentation by lazy(LazyThreadSafetyMode.NONE) {
+ AnnotationTypeUtil.getInstance().toJava(typeMirror)
}
- override fun getTypeName(): TypeName {
- return ClassName.get(typeMirror)
- }
+ override fun toString() = javaCodeRepresentation
- override fun hashCode(): Int {
- return AnnotationTypeUtil.getInstance().toJava(typeMirror).hashCode()
+ private val computedTypeName by lazy(LazyThreadSafetyMode.NONE) {
+ ClassName.get(typeMirror)
}
+ override val typeName: TypeName
+ get() = computedTypeName
+
+ override fun hashCode() = javaCodeRepresentation.hashCode()
+
+
@Suppress("RedundantOverride")
override fun equals(other: Any?): Boolean {
// intentional delegation to super which implements this in data binding generic way.