Port InstrospectionHelper to Kotlin.
Bug: 234116803
Test: AppSearchCompilerTest
Change-Id: I3819d8f33e588cd6f0a86ac121b2017b4fe43b5e
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/AnnotatedGetterOrField.kt b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/AnnotatedGetterOrField.kt
index fcf22cd..8217ee7 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/AnnotatedGetterOrField.kt
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/AnnotatedGetterOrField.kt
@@ -195,12 +195,12 @@
val jvmType = IntrospectionHelper.getPropertyType(getterOrField)
val typeUtils = env.typeUtils
val helper = IntrospectionHelper(env)
- return if (typeUtils.isAssignable(typeUtils.erasure(jvmType), helper.mCollectionType)) {
+ return if (typeUtils.isAssignable(typeUtils.erasure(jvmType), helper.collectionType)) {
ElementTypeCategory.COLLECTION
} else if (
jvmType.kind == TypeKind.ARRAY &&
- !typeUtils.isSameType(jvmType, helper.mBytePrimitiveArrayType) &&
- !typeUtils.isSameType(jvmType, helper.mByteBoxArrayType)
+ !typeUtils.isSameType(jvmType, helper.bytePrimitiveArrayType) &&
+ !typeUtils.isSameType(jvmType, helper.byteBoxArrayType)
) {
// byte[] has a native representation in Icing and should be considered a
// primitive by itself.
@@ -379,7 +379,7 @@
MetadataPropertyAnnotation.NAMESPACE ->
requireTypeIsOneOf(
getterOrField,
- listOf(helper.mStringType),
+ listOf(helper.stringType),
env,
allowRepeated = false
)
@@ -388,10 +388,10 @@
requireTypeIsOneOf(
getterOrField,
listOf(
- helper.mLongPrimitiveType,
- helper.mIntPrimitiveType,
- helper.mLongBoxType,
- helper.mIntegerBoxType
+ helper.longPrimitiveType,
+ helper.intPrimitiveType,
+ helper.longBoxType,
+ helper.integerBoxType
),
env,
allowRepeated = false
@@ -399,7 +399,7 @@
MetadataPropertyAnnotation.SCORE ->
requireTypeIsOneOf(
getterOrField,
- listOf(helper.mIntPrimitiveType, helper.mIntegerBoxType),
+ listOf(helper.intPrimitiveType, helper.integerBoxType),
env,
allowRepeated = false
)
@@ -433,7 +433,7 @@
} else {
requireTypeIsOneOf(
getterOrField,
- listOf(helper.mStringType),
+ listOf(helper.stringType),
env,
allowRepeated = true
)
@@ -453,10 +453,10 @@
requireTypeIsOneOf(
getterOrField,
listOf(
- helper.mLongPrimitiveType,
- helper.mIntPrimitiveType,
- helper.mLongBoxType,
- helper.mIntegerBoxType
+ helper.longPrimitiveType,
+ helper.intPrimitiveType,
+ helper.longBoxType,
+ helper.integerBoxType
),
env,
allowRepeated = true
@@ -467,10 +467,10 @@
requireTypeIsOneOf(
getterOrField,
listOf(
- helper.mDoublePrimitiveType,
- helper.mFloatPrimitiveType,
- helper.mDoubleBoxType,
- helper.mFloatBoxType
+ helper.doublePrimitiveType,
+ helper.floatPrimitiveType,
+ helper.doubleBoxType,
+ helper.floatBoxType
),
env,
allowRepeated = true
@@ -478,28 +478,28 @@
DataPropertyAnnotation.Kind.BOOLEAN_PROPERTY ->
requireTypeIsOneOf(
getterOrField,
- listOf(helper.mBooleanPrimitiveType, helper.mBooleanBoxType),
+ listOf(helper.booleanPrimitiveType, helper.booleanBoxType),
env,
allowRepeated = true
)
DataPropertyAnnotation.Kind.BYTES_PROPERTY ->
requireTypeIsOneOf(
getterOrField,
- listOf(helper.mBytePrimitiveArrayType),
+ listOf(helper.bytePrimitiveArrayType),
env,
allowRepeated = true
)
DataPropertyAnnotation.Kind.EMBEDDING_PROPERTY ->
requireTypeIsOneOf(
getterOrField,
- listOf(helper.mEmbeddingType),
+ listOf(helper.embeddingType),
env,
allowRepeated = true
)
DataPropertyAnnotation.Kind.BLOB_HANDLE_PROPERTY ->
requireTypeIsOneOf(
getterOrField,
- listOf(helper.mBlobHandleType),
+ listOf(helper.blobHandleType),
env,
allowRepeated = true
)
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java
deleted file mode 100644
index db31d5a..0000000
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java
+++ /dev/null
@@ -1,592 +0,0 @@
-/*
- * Copyright 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 androidx.appsearch.compiler;
-
-import static com.google.auto.common.MoreTypes.asTypeElement;
-
-import static java.util.stream.Collectors.toCollection;
-
-import androidx.annotation.RestrictTo;
-import androidx.appsearch.compiler.annotationwrapper.DataPropertyAnnotation;
-import androidx.appsearch.compiler.annotationwrapper.DocumentPropertyAnnotation;
-import androidx.appsearch.compiler.annotationwrapper.PropertyAnnotation;
-
-import com.google.auto.value.AutoValue;
-import com.squareup.javapoet.ClassName;
-
-import kotlin.Metadata;
-import kotlin.metadata.Attributes;
-import kotlin.metadata.KmClass;
-import kotlin.metadata.KmProperty;
-import kotlin.metadata.jvm.KotlinClassHeader;
-import kotlin.metadata.jvm.KotlinClassMetadata;
-
-import org.jspecify.annotations.NonNull;
-import org.jspecify.annotations.Nullable;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.WeakHashMap;
-import java.util.stream.Stream;
-
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-
-/**
- * Utilities for working with data structures representing parsed Java code.
- *
- * @exportToFramework:hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class IntrospectionHelper {
- static final String GEN_CLASS_PREFIX = "$$__AppSearch__";
- static final String APPSEARCH_PKG = "androidx.appsearch.app";
-
- public static final ClassName APPSEARCH_SCHEMA_CLASS =
- ClassName.get(APPSEARCH_PKG, "AppSearchSchema");
-
- static final ClassName PROPERTY_CONFIG_CLASS =
- APPSEARCH_SCHEMA_CLASS.nestedClass("PropertyConfig");
-
- static final String APPSEARCH_EXCEPTION_PKG = "androidx.appsearch.exceptions";
-
- static final ClassName APPSEARCH_EXCEPTION_CLASS =
- ClassName.get(APPSEARCH_EXCEPTION_PKG, "AppSearchException");
-
- public static final String APPSEARCH_ANNOTATION_PKG = "androidx.appsearch.annotation";
-
- public static final String DOCUMENT_ANNOTATION_SIMPLE_CLASS_NAME = "Document";
-
- public static final ClassName DOCUMENT_ANNOTATION_CLASS =
- ClassName.get(APPSEARCH_ANNOTATION_PKG, DOCUMENT_ANNOTATION_SIMPLE_CLASS_NAME);
-
- public static final ClassName GENERIC_DOCUMENT_CLASS =
- ClassName.get(APPSEARCH_PKG, "GenericDocument");
-
- public static final ClassName EMBEDDING_VECTOR_CLASS =
- ClassName.get(APPSEARCH_PKG, "EmbeddingVector");
-
- public static final ClassName APPSEARCH_BLOB_HANDLE_CLASS =
- ClassName.get(APPSEARCH_PKG, "AppSearchBlobHandle");
-
- public static final ClassName BUILDER_PRODUCER_CLASS =
- DOCUMENT_ANNOTATION_CLASS.nestedClass("BuilderProducer");
-
- static final ClassName DOCUMENT_CLASS_FACTORY_CLASS =
- ClassName.get(APPSEARCH_PKG, "DocumentClassFactory");
-
- static final ClassName RESTRICT_TO_ANNOTATION_CLASS =
- ClassName.get("androidx.annotation", "RestrictTo");
-
- static final ClassName RESTRICT_TO_SCOPE_CLASS =
- RESTRICT_TO_ANNOTATION_CLASS.nestedClass("Scope");
-
- static final ClassName DOCUMENT_CLASS_MAPPING_CONTEXT_CLASS =
- ClassName.get(APPSEARCH_PKG, "DocumentClassMappingContext");
-
- public final TypeMirror mStringType;
- public final TypeMirror mLongPrimitiveType;
- public final TypeMirror mIntPrimitiveType;
- public final TypeMirror mBooleanPrimitiveType;
- public final TypeMirror mBytePrimitiveArrayType;
- public final TypeMirror mGenericDocumentType;
- public final TypeMirror mEmbeddingType;
- public final TypeMirror mDoublePrimitiveType;
- public final TypeMirror mBlobHandleType;
- final TypeMirror mCollectionType;
- final TypeMirror mListType;
- final TypeMirror mIntegerBoxType;
- final TypeMirror mLongBoxType;
- final TypeMirror mFloatBoxType;
- final TypeMirror mFloatPrimitiveType;
- final TypeMirror mDoubleBoxType;
- final TypeMirror mBooleanBoxType;
- final TypeMirror mByteBoxType;
- final TypeMirror mByteBoxArrayType;
- final TypeMirror mBytePrimitiveType;
- private final ProcessingEnvironment mEnv;
- private final Types mTypeUtils;
- private final Elements mElementUtils;
-
- private final WeakHashMap<TypeElement, LinkedHashSet<ExecutableElement>> mAllMethodsCache =
- new WeakHashMap<>();
-
- IntrospectionHelper(ProcessingEnvironment env) {
- mEnv = env;
-
- mElementUtils = env.getElementUtils();
- mTypeUtils = env.getTypeUtils();
- mCollectionType = mElementUtils.getTypeElement(Collection.class.getName()).asType();
- mListType = mElementUtils.getTypeElement(List.class.getName()).asType();
- mStringType = mElementUtils.getTypeElement(String.class.getName()).asType();
- mIntegerBoxType = mElementUtils.getTypeElement(Integer.class.getName()).asType();
- mIntPrimitiveType = mTypeUtils.unboxedType(mIntegerBoxType);
- mLongBoxType = mElementUtils.getTypeElement(Long.class.getName()).asType();
- mLongPrimitiveType = mTypeUtils.unboxedType(mLongBoxType);
- mFloatBoxType = mElementUtils.getTypeElement(Float.class.getName()).asType();
- mFloatPrimitiveType = mTypeUtils.unboxedType(mFloatBoxType);
- mDoubleBoxType = mElementUtils.getTypeElement(Double.class.getName()).asType();
- mDoublePrimitiveType = mTypeUtils.unboxedType(mDoubleBoxType);
- mBooleanBoxType = mElementUtils.getTypeElement(Boolean.class.getName()).asType();
- mBooleanPrimitiveType = mTypeUtils.unboxedType(mBooleanBoxType);
- mByteBoxType = mElementUtils.getTypeElement(Byte.class.getName()).asType();
- mByteBoxArrayType = mTypeUtils.getArrayType(mByteBoxType);
- mBytePrimitiveType = mTypeUtils.unboxedType(mByteBoxType);
- mBytePrimitiveArrayType = mTypeUtils.getArrayType(mBytePrimitiveType);
- mGenericDocumentType =
- mElementUtils.getTypeElement(GENERIC_DOCUMENT_CLASS.canonicalName()).asType();
- mEmbeddingType = mElementUtils.getTypeElement(
- EMBEDDING_VECTOR_CLASS.canonicalName()).asType();
- mBlobHandleType = mElementUtils.getTypeElement(
- APPSEARCH_BLOB_HANDLE_CLASS.canonicalName()).asType();
- }
-
- /**
- * Returns {@code androidx.appsearch.annotation.Document} annotation element from the input
- * element's annotations. Returns null if no such annotation is found.
- */
- public static @Nullable AnnotationMirror getDocumentAnnotation(@NonNull Element element) {
- Objects.requireNonNull(element);
- List<? extends AnnotationMirror> annotations = getAnnotations(element,
- DOCUMENT_ANNOTATION_CLASS);
- if (annotations.isEmpty()) {
- return null;
- } else {
- return annotations.get(0);
- }
- }
-
- /**
- * Returns a list of annotations of a given kind from the input element's annotations,
- * specified by the annotation's class name. Returns null if no annotation of such kind is
- * found.
- */
- public static @NonNull List<? extends AnnotationMirror> getAnnotations(@NonNull Element element,
- @NonNull ClassName className) {
- Objects.requireNonNull(element);
- Objects.requireNonNull(className);
- return element.getAnnotationMirrors()
- .stream()
- .filter(annotation -> annotation.getAnnotationType().toString()
- .equals(className.canonicalName()))
- .toList();
- }
-
- /**
- * Returns the document property annotation that matches the given property name from a given
- * class or interface element.
- *
- * <p>Returns null if the property cannot be found in the class or interface, or if the
- * property matching the property name is not a document property.
- */
- public @Nullable DocumentPropertyAnnotation getDocumentPropertyAnnotation(
- @NonNull TypeElement clazz, @NonNull String propertyName) throws ProcessingException {
- Objects.requireNonNull(clazz);
- Objects.requireNonNull(propertyName);
- for (Element enclosedElement : clazz.getEnclosedElements()) {
- AnnotatedGetterOrField getterOrField =
- AnnotatedGetterOrField.tryCreateFor(enclosedElement, mEnv);
- if (getterOrField == null || !(getterOrField.getAnnotation().getPropertyKind()
- == PropertyAnnotation.Kind.DATA_PROPERTY)) {
- continue;
- }
- if (((DataPropertyAnnotation) getterOrField.getAnnotation()).getDataPropertyKind()
- == DataPropertyAnnotation.Kind.DOCUMENT_PROPERTY) {
- DocumentPropertyAnnotation documentPropertyAnnotation =
- (DocumentPropertyAnnotation) getterOrField.getAnnotation();
- if (documentPropertyAnnotation.getName().equals(propertyName)) {
- return documentPropertyAnnotation;
- }
- }
- }
- return null;
- }
-
- /**
- * Returns the property type of the given property. Properties are represented by an
- * annotated Java element that is either a Java field or a getter method.
- */
- public static @NonNull TypeMirror getPropertyType(@NonNull Element property) {
- Objects.requireNonNull(property);
-
- TypeMirror propertyType = property.asType();
- if (property.getKind() == ElementKind.METHOD) {
- propertyType = ((ExecutableType) propertyType).getReturnType();
- }
- return propertyType;
- }
-
- /** Checks whether the property data type is one of the valid types. */
- public boolean isFieldOfExactType(
- @NonNull Element property, TypeMirror @NonNull ... validTypes) {
- TypeMirror propertyType = getPropertyType(property);
- for (TypeMirror validType : validTypes) {
- if (propertyType.getKind() == TypeKind.ARRAY) {
- if (mTypeUtils.isSameType(
- ((ArrayType) propertyType).getComponentType(), validType)) {
- return true;
- }
- } else if (mTypeUtils.isAssignable(mTypeUtils.erasure(propertyType), mCollectionType)) {
- if (mTypeUtils.isSameType(
- ((DeclaredType) propertyType).getTypeArguments().get(0), validType)) {
- return true;
- }
- } else if (mTypeUtils.isSameType(propertyType, validType)) {
- return true;
- }
- }
- return false;
- }
-
- /** Checks whether the property data type is of boolean type. */
- public boolean isFieldOfBooleanType(@NonNull Element property) {
- return isFieldOfExactType(property, mBooleanBoxType, mBooleanPrimitiveType);
- }
-
- /**
- * Returns the annotation's params as a map. Includes the default values.
- */
- public @NonNull Map<String, Object> getAnnotationParams(@NonNull AnnotationMirror annotation) {
- Map<? extends ExecutableElement, ? extends AnnotationValue> values =
- mEnv.getElementUtils().getElementValuesWithDefaults(annotation);
- Map<String, Object> ret = new HashMap<>();
- for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry :
- values.entrySet()) {
- String key = entry.getKey().getSimpleName().toString();
- ret.put(key, entry.getValue().getValue());
- }
- return ret;
- }
-
- /**
- * Creates the name of output class. $$__AppSearch__Foo for Foo, $$__AppSearch__Foo$$__Bar
- * for inner class Foo.Bar.
- */
- public static @NonNull ClassName getDocumentClassFactoryForClass(
- @NonNull String pkg, @NonNull String className) {
- String genClassName = GEN_CLASS_PREFIX + className.replace(".", "$$__");
- return ClassName.get(pkg, genClassName);
- }
-
- /**
- * Creates the name of output class. $$__AppSearch__Foo for Foo, $$__AppSearch__Foo$$__Bar
- * for inner class Foo.Bar.
- */
- public static @NonNull ClassName getDocumentClassFactoryForClass(@NonNull ClassName clazz) {
- String className = clazz.canonicalName().substring(clazz.packageName().length() + 1);
- return getDocumentClassFactoryForClass(clazz.packageName(), className);
- }
-
- /**
- * Returns all the methods within a class, whether inherited or declared directly.
- *
- * <p>Caches results internally, so it is cheap to call subsequently for the same input.
- */
- public @NonNull LinkedHashSet<ExecutableElement> getAllMethods(@NonNull TypeElement clazz) {
- return mAllMethodsCache.computeIfAbsent(
- clazz,
- type -> mEnv.getElementUtils().getAllMembers(type).stream()
- .filter(element -> element.getKind() == ElementKind.METHOD)
- .map(element -> (ExecutableElement) element)
- .collect(toCollection(LinkedHashSet::new)));
- }
-
- /**
- * Whether a type is the same as {@code long[]}.
- */
- public boolean isPrimitiveLongArray(@NonNull TypeMirror type) {
- return isArrayOf(type, mLongPrimitiveType);
- }
-
- /**
- * Whether a type is the same as {@code double[]}.
- */
- public boolean isPrimitiveDoubleArray(@NonNull TypeMirror type) {
- return isArrayOf(type, mDoublePrimitiveType);
- }
-
- /**
- * Whether a type is the same as {@code boolean[]}.
- */
- public boolean isPrimitiveBooleanArray(@NonNull TypeMirror type) {
- return isArrayOf(type, mBooleanPrimitiveType);
- }
-
- private boolean isArrayOf(@NonNull TypeMirror type, @NonNull TypeMirror arrayComponentType) {
- return mTypeUtils.isSameType(type, mTypeUtils.getArrayType(arrayComponentType));
- }
-
- /**
- * Get a list of super classes of element annotated with @Document, in order starting with the
- * class at the top of the hierarchy and descending down the class hierarchy. Note that this
- * ordering is important because super classes must appear first in the list than child classes
- * to make property overrides work.
- */
- public static @NonNull List<TypeElement> generateClassHierarchy(
- @NonNull TypeElement element) throws ProcessingException {
- Deque<TypeElement> hierarchy = new ArrayDeque<>();
- if (element.getAnnotation(AutoValue.class) != null) {
- // We don't allow classes annotated with both Document and AutoValue to extend classes.
- // Because of how AutoValue is set up, there is no way to add a constructor to
- // populate fields of super classes.
- // There should just be the generated class and the original annotated class
- TypeElement superClass = asTypeElement(element.getSuperclass());
- if (!superClass.getQualifiedName().contentEquals(Object.class.getCanonicalName())) {
- throw new ProcessingException(
- "A class annotated with AutoValue and Document cannot have a superclass",
- element);
- }
- hierarchy.add(element);
- } else {
- Set<TypeElement> visited = new HashSet<>();
- generateClassHierarchyHelper(element, element, hierarchy, visited);
- }
- return new ArrayList<>(hierarchy);
- }
-
- /**
- * Checks if a method is a valid getter and returns any errors.
- *
- * <p>Returns an empty list if no errors i.e. the method is a valid getter.
- */
- public static @NonNull List<ProcessingException> validateIsGetter(
- @NonNull ExecutableElement method) {
- List<ProcessingException> errors = new ArrayList<>();
- if (!method.getParameters().isEmpty()) {
- errors.add(new ProcessingException(
- "Getter cannot be used: should take no parameters", method));
- }
- if (method.getModifiers().contains(Modifier.PRIVATE)) {
- errors.add(new ProcessingException(
- "Getter cannot be used: private visibility", method));
- }
- if (method.getModifiers().contains(Modifier.STATIC)) {
- errors.add(new ProcessingException(
- "Getter cannot be used: must not be static", method));
- }
- return errors;
- }
-
- /**
- * Same as {@link #validateIsGetter} but additionally verifies that the getter returns the
- * specified type.
- */
- public @NonNull List<ProcessingException> validateIsGetterThatReturns(
- @NonNull ExecutableElement method, @NonNull TypeMirror expectedReturnType) {
- List<ProcessingException> errors = validateIsGetter(method);
- if (!mTypeUtils.isAssignable(method.getReturnType(), expectedReturnType)) {
- errors.add(new ProcessingException(
- "Getter cannot be used: Does not return " + expectedReturnType, method));
- }
- return errors;
- }
-
- /**
- * A method's type and element (i.e. declaration).
- *
- * <p>Note: The parameter and return types may differ between the type and the element.
- * For example,
- *
- * <pre>
- * {@code
- * public class StringSet implements Set<String> {...}
- * }
- * </pre>
- *
- * <p>Here, the type of {@code StringSet.add()} is {@code (String) -> boolean} and the element
- * points to the generic declaration within {@code Set<T>} with a return type of
- * {@code boolean} and a single parameter of type {@code T}.
- */
- public static class MethodTypeAndElement {
- private final ExecutableType mType;
- private final ExecutableElement mElement;
-
- public MethodTypeAndElement(
- @NonNull ExecutableType type, @NonNull ExecutableElement element) {
- mType = type;
- mElement = element;
- }
-
- public @NonNull ExecutableType getType() {
- return mType;
- }
-
- public @NonNull ExecutableElement getElement() {
- return mElement;
- }
- }
-
- /**
- * Returns a stream of all the methods (including inherited) within a {@link DeclaredType}.
- *
- * <p>Does not include constructors.
- */
- public @NonNull Stream<MethodTypeAndElement> getAllMethods(@NonNull DeclaredType type) {
- return mElementUtils.getAllMembers((TypeElement) type.asElement()).stream()
- .filter(el -> el.getKind() == ElementKind.METHOD)
- .map(el -> new MethodTypeAndElement(
- (ExecutableType) mTypeUtils.asMemberOf(type, el),
- (ExecutableElement) el));
- }
-
- /**
- * Whether the method returns the specified type (or subtype).
- */
- public boolean isReturnTypeMatching(
- @NonNull ExecutableType method, @NonNull TypeMirror type) {
- return mTypeUtils.isAssignable(method.getReturnType(), type);
- }
-
- /**
- * Returns a type that the source type should be casted to coerce it to the target type.
- *
- * <p>Handles the following cases:
- * <pre>
- * {@code
- * long|Long -> int|Integer = (int) ...
- * double|Double -> float|Float = (float) ...
- * }
- * </pre>
- *
- * <p>Returns null if no cast is necessary.
- */
- public @Nullable TypeMirror getNarrowingCastType(
- @NonNull TypeMirror sourceType, @NonNull TypeMirror targetType) {
- if (mTypeUtils.isSameType(targetType, mIntPrimitiveType)
- || mTypeUtils.isSameType(targetType, mIntegerBoxType)) {
- if (mTypeUtils.isSameType(sourceType, mLongPrimitiveType)
- || mTypeUtils.isSameType(sourceType, mLongBoxType)) {
- return mIntPrimitiveType;
- }
- }
- if (mTypeUtils.isSameType(targetType, mFloatPrimitiveType)
- || mTypeUtils.isSameType(targetType, mFloatBoxType)) {
- if (mTypeUtils.isSameType(sourceType, mDoublePrimitiveType)
- || mTypeUtils.isSameType(sourceType, mDoubleBoxType)) {
- return mFloatPrimitiveType;
- }
- }
- return null;
- }
-
- /**
- * Whether the element is a static method that returns the class it's enclosed within.
- */
- public boolean isStaticFactoryMethod(@NonNull Element element) {
- if (element.getKind() != ElementKind.METHOD
- || !element.getModifiers().contains(Modifier.STATIC)) {
- return false;
- }
- ExecutableElement method = (ExecutableElement) element;
- TypeMirror enclosingType = method.getEnclosingElement().asType();
- return mTypeUtils.isSameType(method.getReturnType(), enclosingType);
- }
-
- private static void generateClassHierarchyHelper(@NonNull TypeElement leafElement,
- @NonNull TypeElement currentClass, @NonNull Deque<TypeElement> hierarchy,
- @NonNull Set<TypeElement> visited)
- throws ProcessingException {
- if (currentClass.getQualifiedName().contentEquals(Object.class.getCanonicalName())) {
- return;
- }
- // If you inherit from an AutoValue class, you have to implement the static methods.
- // That defeats the purpose of AutoValue
- if (currentClass.getAnnotation(AutoValue.class) != null) {
- throw new ProcessingException(
- "A class annotated with Document cannot inherit from a class "
- + "annotated with AutoValue", leafElement);
- }
-
- // It's possible to revisit the same interface more than once, so this check exists to
- // catch that.
- if (visited.contains(currentClass)) {
- return;
- }
- visited.add(currentClass);
-
- if (getDocumentAnnotation(currentClass) != null) {
- hierarchy.addFirst(currentClass);
- }
- TypeMirror superclass = currentClass.getSuperclass();
- // If currentClass is an interface, then superclass could be NONE.
- if (superclass.getKind() != TypeKind.NONE) {
- generateClassHierarchyHelper(leafElement, asTypeElement(superclass),
- hierarchy, visited);
- }
- for (TypeMirror implementedInterface : currentClass.getInterfaces()) {
- generateClassHierarchyHelper(leafElement, asTypeElement(implementedInterface),
- hierarchy, visited);
- }
- }
-
- /**
- * Determines if a field is from Kotlin and is NonNull by checking for a Metadata annotation.
- */
- public static boolean isNonNullKotlinField(@NonNull AnnotatedGetterOrField getterOrField) {
- Objects.requireNonNull(getterOrField);
- Metadata metadata = getterOrField.getElement().getEnclosingElement()
- .getAnnotation(Metadata.class);
- if (metadata != null) {
- // The kotlin metadata annotation contains information about the class, but we first
- // need to parse it with KotlinClassHeader
- KotlinClassHeader header = new KotlinClassHeader(
- /*kind=*/metadata.k(),
- /*metadataVersion=*/metadata.mv(),
- /*data1=*/metadata.d1(),
- /*data2=*/metadata.d2(),
- /*extraString=*/metadata.xs(),
- /*packageName=*/metadata.pn(),
- /*extraInt=*/metadata.xi()
- );
- KotlinClassMetadata kotlinMetadata = KotlinClassMetadata.readStrict(header);
-
- if (kotlinMetadata instanceof KotlinClassMetadata.Class) {
- KmClass kmClass = ((KotlinClassMetadata.Class) kotlinMetadata).getKmClass();
-
- List<KmProperty> properties = kmClass.getProperties();
- for (KmProperty property : properties) {
- if (property.getName().equals(getterOrField.getJvmName())) {
- return !Attributes.isNullable(property.getReturnType());
- }
- }
- }
- }
- // It is not a kotlin property.
- return false;
- }
-}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.kt b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.kt
new file mode 100644
index 0000000..81cd239
--- /dev/null
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.kt
@@ -0,0 +1,543 @@
+/*
+ * Copyright 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 androidx.appsearch.compiler
+
+import androidx.appsearch.compiler.AnnotatedGetterOrField.Companion.tryCreateFor
+import androidx.appsearch.compiler.annotationwrapper.DataPropertyAnnotation
+import androidx.appsearch.compiler.annotationwrapper.DocumentPropertyAnnotation
+import androidx.appsearch.compiler.annotationwrapper.PropertyAnnotation
+import com.google.auto.common.MoreTypes
+import com.google.auto.value.AutoValue
+import com.squareup.javapoet.ClassName
+import java.util.ArrayDeque
+import java.util.Deque
+import java.util.WeakHashMap
+import java.util.stream.Collectors
+import java.util.stream.Stream
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.AnnotationMirror
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.ArrayType
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.ExecutableType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.util.Elements
+import javax.lang.model.util.Types
+import kotlin.metadata.isNullable
+import kotlin.metadata.jvm.KotlinClassMetadata
+
+/** Utilities for working with data structures representing parsed Java code. */
+class IntrospectionHelper internal constructor(private val env: ProcessingEnvironment) {
+ private val typeUtils: Types = env.typeUtils
+ private val elementUtils: Elements = env.elementUtils
+
+ // Non-boxable objects
+ val blobHandleType: TypeMirror =
+ elementUtils.getTypeElement(APPSEARCH_BLOB_HANDLE_CLASS.canonicalName()).asType()
+ val collectionType: TypeMirror =
+ elementUtils.getTypeElement(java.util.Collection::class.java.name).asType()
+ val embeddingType: TypeMirror =
+ elementUtils.getTypeElement(EMBEDDING_VECTOR_CLASS.canonicalName()).asType()
+ val genericDocumentType: TypeMirror =
+ elementUtils.getTypeElement(GENERIC_DOCUMENT_CLASS.canonicalName()).asType()
+ val listType: TypeMirror = elementUtils.getTypeElement(java.util.List::class.java.name).asType()
+ @JvmField
+ val stringType: TypeMirror =
+ elementUtils.getTypeElement(java.lang.String::class.java.name).asType()
+
+ // Boxable objects
+ val booleanBoxType: TypeMirror =
+ elementUtils.getTypeElement(java.lang.Boolean::class.java.name).asType()
+ val byteBoxType: TypeMirror =
+ elementUtils.getTypeElement(java.lang.Byte::class.java.name).asType()
+ val byteBoxArrayType: TypeMirror = typeUtils.getArrayType(byteBoxType)
+ val doubleBoxType: TypeMirror =
+ elementUtils.getTypeElement(java.lang.Double::class.java.name).asType()
+ val floatBoxType: TypeMirror =
+ elementUtils.getTypeElement(java.lang.Float::class.java.name).asType()
+ val integerBoxType: TypeMirror =
+ elementUtils.getTypeElement(java.lang.Integer::class.java.name).asType()
+ val longBoxType: TypeMirror =
+ elementUtils.getTypeElement(java.lang.Long::class.java.name).asType()
+
+ // Primitive versions of boxable objects
+ @JvmField val booleanPrimitiveType: TypeMirror = typeUtils.unboxedType(booleanBoxType)
+ val bytePrimitiveType: TypeMirror = typeUtils.unboxedType(byteBoxType)
+ @JvmField val bytePrimitiveArrayType: TypeMirror = typeUtils.getArrayType(bytePrimitiveType)
+ @JvmField val doublePrimitiveType: TypeMirror = typeUtils.unboxedType(doubleBoxType)
+ val floatPrimitiveType: TypeMirror = typeUtils.unboxedType(floatBoxType)
+ @JvmField val intPrimitiveType: TypeMirror = typeUtils.unboxedType(integerBoxType)
+ @JvmField val longPrimitiveType: TypeMirror = typeUtils.unboxedType(longBoxType)
+
+ private val allMethodsCache = WeakHashMap<TypeElement, LinkedHashSet<ExecutableElement>>()
+
+ companion object {
+ const val GEN_CLASS_PREFIX: String = "$\$__AppSearch__"
+ const val APPSEARCH_PKG: String = "androidx.appsearch.app"
+
+ @JvmField
+ val APPSEARCH_SCHEMA_CLASS: ClassName = ClassName.get(APPSEARCH_PKG, "AppSearchSchema")
+
+ @JvmField
+ val PROPERTY_CONFIG_CLASS: ClassName = APPSEARCH_SCHEMA_CLASS.nestedClass("PropertyConfig")
+
+ const val APPSEARCH_EXCEPTION_PKG: String = "androidx.appsearch.exceptions"
+
+ @JvmField
+ val APPSEARCH_EXCEPTION_CLASS: ClassName =
+ ClassName.get(APPSEARCH_EXCEPTION_PKG, "AppSearchException")
+
+ const val APPSEARCH_ANNOTATION_PKG: String = "androidx.appsearch.annotation"
+
+ const val DOCUMENT_ANNOTATION_SIMPLE_CLASS_NAME: String = "Document"
+
+ @JvmField
+ val DOCUMENT_ANNOTATION_CLASS: ClassName =
+ ClassName.get(APPSEARCH_ANNOTATION_PKG, DOCUMENT_ANNOTATION_SIMPLE_CLASS_NAME)
+
+ @JvmField
+ val GENERIC_DOCUMENT_CLASS: ClassName = ClassName.get(APPSEARCH_PKG, "GenericDocument")
+
+ val EMBEDDING_VECTOR_CLASS: ClassName = ClassName.get(APPSEARCH_PKG, "EmbeddingVector")
+
+ val APPSEARCH_BLOB_HANDLE_CLASS: ClassName =
+ ClassName.get(APPSEARCH_PKG, "AppSearchBlobHandle")
+
+ val BUILDER_PRODUCER_CLASS: ClassName =
+ DOCUMENT_ANNOTATION_CLASS.nestedClass("BuilderProducer")
+
+ @JvmField
+ val DOCUMENT_CLASS_FACTORY_CLASS: ClassName =
+ ClassName.get(APPSEARCH_PKG, "DocumentClassFactory")
+
+ @JvmField
+ val RESTRICT_TO_ANNOTATION_CLASS: ClassName =
+ ClassName.get("androidx.annotation", "RestrictTo")
+
+ @JvmField
+ val RESTRICT_TO_SCOPE_CLASS: ClassName = RESTRICT_TO_ANNOTATION_CLASS.nestedClass("Scope")
+
+ @JvmField
+ val DOCUMENT_CLASS_MAPPING_CONTEXT_CLASS: ClassName =
+ ClassName.get(APPSEARCH_PKG, "DocumentClassMappingContext")
+
+ /**
+ * Returns `androidx.appsearch.annotation.Document` annotation element from the input
+ * element's annotations. Returns null if no such annotation is found.
+ */
+ @JvmStatic
+ fun getDocumentAnnotation(element: Element): AnnotationMirror? {
+ val annotations: List<AnnotationMirror> =
+ getAnnotations(element, DOCUMENT_ANNOTATION_CLASS)
+ return annotations.firstOrNull()
+ }
+
+ /**
+ * Returns a list of annotations of a given kind from the input element's annotations,
+ * specified by the annotation's class name. Returns null if no annotation of such kind is
+ * found.
+ */
+ fun getAnnotations(element: Element, className: ClassName): List<AnnotationMirror> {
+ return element.annotationMirrors
+ .stream()
+ .filter { it.annotationType.toString() == className.canonicalName() }
+ .toList()
+ }
+
+ /**
+ * Returns the property type of the given property. Properties are represented by an
+ * annotated Java element that is either a Java field or a getter method.
+ */
+ fun getPropertyType(property: Element): TypeMirror {
+ var propertyType = property.asType()
+ if (property.kind == ElementKind.METHOD) {
+ propertyType = (propertyType as ExecutableType).returnType
+ }
+ return propertyType
+ }
+
+ /**
+ * Creates the name of output class. $$__AppSearch__Foo for Foo, $$__AppSearch__Foo$$__Bar
+ * for inner class Foo.Bar.
+ */
+ @JvmStatic
+ fun getDocumentClassFactoryForClass(pkg: String, className: String): ClassName {
+ val genClassName: String = GEN_CLASS_PREFIX + className.replace(".", "$\$__")
+ return ClassName.get(pkg, genClassName)
+ }
+
+ /**
+ * Creates the name of output class. $$__AppSearch__Foo for Foo, $$__AppSearch__Foo$$__Bar
+ * for inner class Foo.Bar.
+ */
+ @JvmStatic
+ fun getDocumentClassFactoryForClass(clazz: ClassName): ClassName {
+ val className = clazz.canonicalName().substring(clazz.packageName().length + 1)
+ return getDocumentClassFactoryForClass(clazz.packageName(), className)
+ }
+
+ /**
+ * Get a list of super classes of element annotated with @Document, in order starting with
+ * the class at the top of the hierarchy and descending down the class hierarchy. Note that
+ * this ordering is important because super classes must appear first in the list than child
+ * classes to make property overrides work.
+ */
+ @Throws(ProcessingException::class)
+ @JvmStatic
+ fun generateClassHierarchy(element: TypeElement): List<TypeElement> {
+ val hierarchy: Deque<TypeElement> = ArrayDeque<TypeElement>()
+ if (element.getAnnotation(AutoValue::class.java) != null) {
+ // We don't allow classes annotated with both Document and AutoValue to extend
+ // classes.
+ // Because of how AutoValue is set up, there is no way to add a constructor to
+ // populate fields of super classes.
+ // There should just be the generated class and the original annotated class
+ val superClass = MoreTypes.asTypeElement(element.superclass)
+ if (
+ !superClass.qualifiedName.contentEquals(
+ java.lang.Object::class.java.canonicalName
+ )
+ ) {
+ throw ProcessingException(
+ "A class annotated with AutoValue and Document cannot have a superclass",
+ element
+ )
+ }
+ hierarchy.add(element)
+ } else {
+ val visited = mutableSetOf<TypeElement>()
+ generateClassHierarchyHelper(element, element, hierarchy, visited)
+ }
+ return hierarchy.toList()
+ }
+
+ /**
+ * Checks if a method is a valid getter and returns any errors.
+ *
+ * Returns an empty list if no errors i.e. the method is a valid getter.
+ */
+ fun validateIsGetter(method: ExecutableElement): MutableList<ProcessingException> {
+ val errors = mutableListOf<ProcessingException>()
+ if (method.parameters.isNotEmpty()) {
+ errors.add(
+ ProcessingException("Getter cannot be used: should take no parameters", method)
+ )
+ }
+ if (method.modifiers.contains(Modifier.PRIVATE)) {
+ errors.add(ProcessingException("Getter cannot be used: private visibility", method))
+ }
+ if (method.modifiers.contains(Modifier.STATIC)) {
+ errors.add(ProcessingException("Getter cannot be used: must not be static", method))
+ }
+ return errors
+ }
+
+ @Throws(ProcessingException::class)
+ private fun generateClassHierarchyHelper(
+ leafElement: TypeElement,
+ currentClass: TypeElement,
+ hierarchy: Deque<TypeElement>,
+ visited: MutableSet<TypeElement>
+ ) {
+ if (
+ currentClass.qualifiedName.contentEquals(java.lang.Object::class.java.canonicalName)
+ ) {
+ return
+ }
+ // If you inherit from an AutoValue class, you have to implement the static methods.
+ // That defeats the purpose of AutoValue
+ if (currentClass.getAnnotation(AutoValue::class.java) != null) {
+ throw ProcessingException(
+ "A class annotated with Document cannot inherit from a class " +
+ "annotated with AutoValue",
+ leafElement
+ )
+ }
+
+ // It's possible to revisit the same interface more than once, so this check exists to
+ // catch that.
+ if (visited.contains(currentClass)) {
+ return
+ }
+ visited.add(currentClass)
+
+ if (getDocumentAnnotation(currentClass) != null) {
+ hierarchy.addFirst(currentClass)
+ }
+ val superclass = currentClass.superclass
+ // If currentClass is an interface, then superclass could be NONE.
+ if (superclass.kind != TypeKind.NONE) {
+ generateClassHierarchyHelper(
+ leafElement,
+ MoreTypes.asTypeElement(superclass),
+ hierarchy,
+ visited
+ )
+ }
+ for (implementedInterface in currentClass.interfaces) {
+ generateClassHierarchyHelper(
+ leafElement,
+ MoreTypes.asTypeElement(implementedInterface),
+ hierarchy,
+ visited
+ )
+ }
+ }
+
+ /**
+ * Determines if a field is from Kotlin and is NonNull by checking for a Metadata
+ * annotation.
+ */
+ @JvmStatic
+ fun isNonNullKotlinField(getterOrField: AnnotatedGetterOrField): Boolean {
+ val metadata =
+ getterOrField.element.enclosingElement.getAnnotation(Metadata::class.java)
+ if (metadata != null) {
+ val kotlinMetadata: KotlinClassMetadata = KotlinClassMetadata.readStrict(metadata)
+ if (kotlinMetadata is KotlinClassMetadata.Class) {
+ val kmClass = kotlinMetadata.kmClass
+ val properties = kmClass.properties
+ for (property in properties) {
+ if (property.name == getterOrField.jvmName) {
+ return !property.returnType.isNullable
+ }
+ }
+ }
+ }
+ // It is not a kotlin property.
+ return false
+ }
+ }
+
+ /**
+ * Returns the document property annotation that matches the given property name from a given
+ * class or interface element.
+ *
+ * Returns null if the property cannot be found in the class or interface, or if the property
+ * matching the property name is not a document property.
+ */
+ @Throws(ProcessingException::class)
+ fun getDocumentPropertyAnnotation(
+ clazz: TypeElement,
+ propertyName: String
+ ): DocumentPropertyAnnotation? {
+ for (enclosedElement in clazz.enclosedElements) {
+ val getterOrField = tryCreateFor(enclosedElement, env)
+ if (
+ getterOrField == null ||
+ getterOrField.annotation.propertyKind != PropertyAnnotation.Kind.DATA_PROPERTY
+ ) {
+ continue
+ }
+ if (
+ (getterOrField.annotation as DataPropertyAnnotation).dataPropertyKind ==
+ DataPropertyAnnotation.Kind.DOCUMENT_PROPERTY
+ ) {
+ val documentPropertyAnnotation =
+ getterOrField.annotation as DocumentPropertyAnnotation
+ if (documentPropertyAnnotation.name == propertyName) {
+ return documentPropertyAnnotation
+ }
+ }
+ }
+ return null
+ }
+
+ /** Checks whether the property data type is one of the valid types. */
+ fun isFieldOfExactType(property: Element, vararg validTypes: TypeMirror): Boolean {
+ val propertyType: TypeMirror = getPropertyType(property)
+ for (validType in validTypes) {
+ if (propertyType.kind == TypeKind.ARRAY) {
+ if (typeUtils.isSameType((propertyType as ArrayType).componentType, validType)) {
+ return true
+ }
+ } else if (typeUtils.isAssignable(typeUtils.erasure(propertyType), collectionType)) {
+ if (
+ typeUtils.isSameType(
+ (propertyType as DeclaredType).typeArguments.first(),
+ validType
+ )
+ ) {
+ return true
+ }
+ } else if (typeUtils.isSameType(propertyType, validType)) {
+ return true
+ }
+ }
+ return false
+ }
+
+ /** Checks whether the property data type is of boolean type. */
+ fun isFieldOfBooleanType(property: Element): Boolean {
+ return isFieldOfExactType(property, booleanBoxType, booleanPrimitiveType)
+ }
+
+ /** Returns the annotation's params as a map. Includes the default values. */
+ fun getAnnotationParams(annotation: AnnotationMirror): Map<String, Any?> {
+ val values = env.elementUtils.getElementValuesWithDefaults(annotation)
+ val ret = mutableMapOf<String, Any?>()
+ for (entry in values.entries) {
+ val key = entry.key.simpleName.toString()
+ ret[key] = entry.value.value
+ }
+ return ret
+ }
+
+ /**
+ * Returns all the methods within a class, whether inherited or declared directly.
+ *
+ * Caches results internally, so it is cheap to call subsequently for the same input.
+ */
+ fun getAllMethods(clazz: TypeElement): LinkedHashSet<ExecutableElement> {
+ return allMethodsCache.computeIfAbsent(clazz) { type: TypeElement ->
+ env.elementUtils
+ .getAllMembers(type)
+ .stream()
+ .filter { it.kind == ElementKind.METHOD }
+ .map { it as ExecutableElement }
+ .collect(Collectors.toCollection { LinkedHashSet() })
+ }
+ }
+
+ /** Whether a type is the same as `long[]`. */
+ fun isPrimitiveLongArray(type: TypeMirror): Boolean {
+ return isArrayOf(type, longPrimitiveType)
+ }
+
+ /** Whether a type is the same as `double[]`. */
+ fun isPrimitiveDoubleArray(type: TypeMirror): Boolean {
+ return isArrayOf(type, doublePrimitiveType)
+ }
+
+ /** Whether a type is the same as `boolean[]`. */
+ fun isPrimitiveBooleanArray(type: TypeMirror): Boolean {
+ return isArrayOf(type, booleanPrimitiveType)
+ }
+
+ private fun isArrayOf(type: TypeMirror, arrayComponentType: TypeMirror): Boolean {
+ return typeUtils.isSameType(type, typeUtils.getArrayType(arrayComponentType))
+ }
+
+ /**
+ * Same as [.validateIsGetter] but additionally verifies that the getter returns the specified
+ * type.
+ */
+ fun validateIsGetterThatReturns(
+ method: ExecutableElement,
+ expectedReturnType: TypeMirror
+ ): MutableList<ProcessingException> {
+ val errors: MutableList<ProcessingException> = validateIsGetter(method)
+ if (!typeUtils.isAssignable(method.returnType, expectedReturnType)) {
+ errors.add(
+ ProcessingException(
+ "Getter cannot be used: Does not return $expectedReturnType",
+ method
+ )
+ )
+ }
+ return errors
+ }
+
+ /**
+ * A method's type and element (i.e. declaration).
+ *
+ * Note: The parameter and return types may differ between the type and the element. For
+ * example,
+ * <pre>
+ * public class StringSet implements Set<String> {...}
+ * </pre>
+ *
+ * Here, the type of `StringSet.add()` is `(String) -> boolean` and the element points to the
+ * generic declaration within `Set<T>` with a return type of `boolean` and a single parameter of
+ * type `T`.
+ */
+ class MethodTypeAndElement(val type: ExecutableType, val element: ExecutableElement)
+
+ /**
+ * Returns a stream of all the methods (including inherited) within a [DeclaredType].
+ *
+ * Does not include constructors.
+ */
+ fun getAllMethods(type: DeclaredType): Stream<MethodTypeAndElement> {
+ return elementUtils
+ .getAllMembers(type.asElement() as TypeElement)
+ .stream()
+ .filter { it.kind == ElementKind.METHOD }
+ .map {
+ MethodTypeAndElement(
+ typeUtils.asMemberOf(type, it) as ExecutableType,
+ it as ExecutableElement
+ )
+ }
+ }
+
+ /** Whether the method returns the specified type (or subtype). */
+ fun isReturnTypeMatching(method: ExecutableType, type: TypeMirror): Boolean {
+ return typeUtils.isAssignable(method.returnType, type)
+ }
+
+ /**
+ * Returns a type that the source type should be casted to coerce it to the target type.
+ *
+ * Handles the following cases:
+ * <pre>
+ * long|Long -> int|Integer = (int) ...
+ * double|Double -> float|Float = (float) ...
+ * </pre>
+ *
+ * Returns null if no cast is necessary.
+ */
+ fun getNarrowingCastType(sourceType: TypeMirror, targetType: TypeMirror): TypeMirror? {
+ if (
+ typeUtils.isSameType(targetType, intPrimitiveType) ||
+ typeUtils.isSameType(targetType, integerBoxType)
+ ) {
+ if (
+ typeUtils.isSameType(sourceType, longPrimitiveType) ||
+ typeUtils.isSameType(sourceType, longBoxType)
+ ) {
+ return intPrimitiveType
+ }
+ }
+ if (
+ typeUtils.isSameType(targetType, floatPrimitiveType) ||
+ typeUtils.isSameType(targetType, floatBoxType)
+ ) {
+ if (
+ typeUtils.isSameType(sourceType, doublePrimitiveType) ||
+ typeUtils.isSameType(sourceType, doubleBoxType)
+ ) {
+ return floatPrimitiveType
+ }
+ }
+ return null
+ }
+
+ /** Whether the element is a static method that returns the class it's enclosed within. */
+ fun isStaticFactoryMethod(element: Element): Boolean {
+ if (element.kind != ElementKind.METHOD || !element.modifiers.contains(Modifier.STATIC)) {
+ return false
+ }
+ val method = element as ExecutableElement
+ val enclosingType = method.enclosingElement.asType()
+ return typeUtils.isSameType(method.returnType, enclosingType)
+ }
+}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/PropertyAccessor.kt b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/PropertyAccessor.kt
index 84d27ef..96a6c47 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/PropertyAccessor.kt
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/PropertyAccessor.kt
@@ -130,8 +130,8 @@
val isBooleanField =
helper.isFieldOfExactType(
privateField.element,
- helper.mBooleanPrimitiveType,
- helper.mBooleanBoxType
+ helper.booleanPrimitiveType,
+ helper.booleanBoxType
)
if (isBooleanField && privateField.elementTypeCategory == ElementTypeCategory.SINGLE) {
getterNames.add("is$upperCamelCase")
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/ToGenericDocumentCodeGenerator.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/ToGenericDocumentCodeGenerator.java
index f0d8d01..0d0c11a 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/ToGenericDocumentCodeGenerator.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/ToGenericDocumentCodeGenerator.java
@@ -281,7 +281,7 @@
return collectionForLoopAssign(
annotation,
getterOrField,
- /* targetArrayComponentType= */mHelper.mLongPrimitiveType);
+ /* targetArrayComponentType= */mHelper.longPrimitiveType);
}
case ARRAY:
if (longSerializer != null) { // CustomType[]: 2d
@@ -293,7 +293,7 @@
return arrayForLoopAssign(
annotation,
getterOrField,
- /* targetArrayComponentType= */mHelper.mLongPrimitiveType);
+ /* targetArrayComponentType= */mHelper.longPrimitiveType);
}
case SINGLE:
if (longSerializer != null) { // CustomType: 3d
@@ -313,7 +313,7 @@
return collectionForLoopAssign(
annotation,
getterOrField,
- /* targetArrayComponentType= */mHelper.mDoublePrimitiveType);
+ /* targetArrayComponentType= */mHelper.doublePrimitiveType);
case ARRAY:
if (mHelper.isPrimitiveDoubleArray(getterOrField.getJvmType())) {
return arrayUseDirectly(annotation, getterOrField); // double[]: 2b
@@ -322,7 +322,7 @@
return arrayForLoopAssign(
annotation,
getterOrField,
- /* targetArrayComponentType= */mHelper.mDoublePrimitiveType);
+ /* targetArrayComponentType= */mHelper.doublePrimitiveType);
}
case SINGLE:
if (getterOrField.getJvmType() instanceof PrimitiveType) {
@@ -341,7 +341,7 @@
return collectionForLoopAssign(
annotation,
getterOrField,
- /* targetArrayComponentType= */mHelper.mBooleanPrimitiveType);
+ /* targetArrayComponentType= */mHelper.booleanPrimitiveType);
case ARRAY:
if (mHelper.isPrimitiveBooleanArray(getterOrField.getJvmType())) {
return arrayUseDirectly(annotation, getterOrField); // boolean[]: 2b
@@ -350,7 +350,7 @@
return arrayForLoopAssign(
annotation,
getterOrField,
- /* targetArrayComponentType= */mHelper.mBooleanPrimitiveType);
+ /* targetArrayComponentType= */mHelper.booleanPrimitiveType);
}
case SINGLE:
if (getterOrField.getJvmType() instanceof PrimitiveType) {
@@ -369,7 +369,7 @@
return collectionForLoopAssign(
annotation,
getterOrField,
- /* targetArrayComponentType= */mHelper.mBytePrimitiveArrayType);
+ /* targetArrayComponentType= */mHelper.bytePrimitiveArrayType);
case ARRAY: // byte[][]: 2b
return arrayUseDirectly(annotation, getterOrField);
case SINGLE: // byte[]: 2e
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BlobHandlePropertyAnnotation.kt b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BlobHandlePropertyAnnotation.kt
index a02ee10..5702026 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BlobHandlePropertyAnnotation.kt
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BlobHandlePropertyAnnotation.kt
@@ -63,5 +63,5 @@
get() = Kind.BLOB_HANDLE_PROPERTY
override fun getUnderlyingTypeWithinGenericDoc(helper: IntrospectionHelper): TypeMirror =
- helper.mBlobHandleType
+ helper.blobHandleType
}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BooleanPropertyAnnotation.kt b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BooleanPropertyAnnotation.kt
index 88535e8..a00e4e8 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BooleanPropertyAnnotation.kt
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BooleanPropertyAnnotation.kt
@@ -58,5 +58,5 @@
get() = Kind.BOOLEAN_PROPERTY
override fun getUnderlyingTypeWithinGenericDoc(helper: IntrospectionHelper): TypeMirror =
- helper.mBooleanPrimitiveType
+ helper.booleanPrimitiveType
}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BytesPropertyAnnotation.kt b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BytesPropertyAnnotation.kt
index efe3f25..4896524 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BytesPropertyAnnotation.kt
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BytesPropertyAnnotation.kt
@@ -58,5 +58,5 @@
get() = Kind.BYTES_PROPERTY
override fun getUnderlyingTypeWithinGenericDoc(helper: IntrospectionHelper): TypeMirror =
- helper.mBytePrimitiveArrayType
+ helper.bytePrimitiveArrayType
}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DocumentPropertyAnnotation.kt b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DocumentPropertyAnnotation.kt
index 0b6646d..21c08c8 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DocumentPropertyAnnotation.kt
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DocumentPropertyAnnotation.kt
@@ -59,7 +59,7 @@
* params do not mention an explicit name.
*/
fun parse(
- annotationParams: Map<String?, Any?>,
+ annotationParams: Map<String, Any?>,
defaultName: String
): DocumentPropertyAnnotation {
val name = annotationParams["name"] as? String
@@ -85,5 +85,5 @@
get() = Kind.DOCUMENT_PROPERTY
override fun getUnderlyingTypeWithinGenericDoc(helper: IntrospectionHelper): TypeMirror =
- helper.mGenericDocumentType
+ helper.genericDocumentType
}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DoublePropertyAnnotation.kt b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DoublePropertyAnnotation.kt
index 262bc95..90f062e 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DoublePropertyAnnotation.kt
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DoublePropertyAnnotation.kt
@@ -58,5 +58,5 @@
get() = Kind.DOUBLE_PROPERTY
override fun getUnderlyingTypeWithinGenericDoc(helper: IntrospectionHelper): TypeMirror =
- helper.mDoublePrimitiveType
+ helper.doublePrimitiveType
}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/EmbeddingPropertyAnnotation.kt b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/EmbeddingPropertyAnnotation.kt
index dd8654b..4c02902 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/EmbeddingPropertyAnnotation.kt
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/EmbeddingPropertyAnnotation.kt
@@ -70,5 +70,5 @@
get() = Kind.EMBEDDING_PROPERTY
override fun getUnderlyingTypeWithinGenericDoc(helper: IntrospectionHelper): TypeMirror =
- helper.mEmbeddingType
+ helper.embeddingType
}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/LongPropertyAnnotation.kt b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/LongPropertyAnnotation.kt
index adb5c5c8..ebb6e19 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/LongPropertyAnnotation.kt
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/LongPropertyAnnotation.kt
@@ -94,5 +94,5 @@
get() = Kind.LONG_PROPERTY
override fun getUnderlyingTypeWithinGenericDoc(helper: IntrospectionHelper): TypeMirror =
- helper.mLongPrimitiveType
+ helper.longPrimitiveType
}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/MetadataPropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/MetadataPropertyAnnotation.java
index 8990fb5..92bf1d4 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/MetadataPropertyAnnotation.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/MetadataPropertyAnnotation.java
@@ -101,12 +101,12 @@
switch (this) {
case ID: // fall-through
case NAMESPACE:
- return helper.mStringType;
+ return helper.stringType;
case CREATION_TIMESTAMP_MILLIS: // fall-through
case TTL_MILLIS:
- return helper.mLongPrimitiveType;
+ return helper.longPrimitiveType;
case SCORE:
- return helper.mIntPrimitiveType;
+ return helper.intPrimitiveType;
default:
throw new IllegalStateException("Unhandled metadata property annotation: " + this);
}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/StringPropertyAnnotation.kt b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/StringPropertyAnnotation.kt
index 52ad643..b0dc14d 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/StringPropertyAnnotation.kt
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/StringPropertyAnnotation.kt
@@ -102,5 +102,5 @@
get() = Kind.STRING_PROPERTY
override fun getUnderlyingTypeWithinGenericDoc(helper: IntrospectionHelper): TypeMirror =
- helper.mStringType
+ helper.stringType
}