diff options
author | Yigit Boyar <yboyar@google.com> | 2018-10-17 17:29:26 -0700 |
---|---|---|
committer | Yigit Boyar <yboyar@google.com> | 2018-10-19 12:33:46 -0700 |
commit | 2ec6c75f754b0f6ffdf1ab87920c76cb5679aeba (patch) | |
tree | 99501ea98a6a47f5e4099da2c182a427a234dfc7 /compiler/src/main/java/android/databinding | |
parent | b7e7521cbb76fd5705911d8813f273b107d69e15 (diff) | |
download | data-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')
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<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<T>, T is a type variable. - * However, List<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<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<T>, T is a type variable. + * However, List<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. |