Skip to content

Commit

Permalink
Add String and TypeElement versions of auto-common APIs that currentl…
Browse files Browse the repository at this point in the history
…y take in Class.

These changes continue the effort to move away from referencing classes from the user API directly in processors, which can cause issues especially with Android dependencies.

Note: I decided not to do anything with isTypeOf yet because I'm not sure of a great way to support that.
RELNOTES=Add String and TypeElement versions of auto-common APIs that currently take in Class.
PiperOrigin-RevId: 377287397
  • Loading branch information
bcorso authored and Google Java Core Libraries committed Jun 3, 2021
1 parent 57fdeb9 commit dec3bf0
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 51 deletions.
45 changes: 29 additions & 16 deletions common/src/main/java/com/google/auto/common/AnnotationMirrors.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,20 @@
package com.google.auto.common;

import static com.google.auto.common.MoreElements.isAnnotationPresent;
import static com.google.auto.common.MoreStreams.toImmutableSet;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Equivalence;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
Expand Down Expand Up @@ -154,21 +153,35 @@ public static Map.Entry<ExecutableElement, AnnotationValue> getAnnotationElement
}

/**
* Returns all {@linkplain AnnotationMirror annotations} that are present on the given
* {@link Element} which are themselves annotated with {@code annotationType}.
* Returns all {@linkplain AnnotationMirror annotations} that are present on the given {@link
* Element} which are themselves annotated with {@code annotationClass}.
*/
public static ImmutableSet<? extends AnnotationMirror> getAnnotatedAnnotations(
Element element, final Class<? extends Annotation> annotationType) {
List<? extends AnnotationMirror> annotations = element.getAnnotationMirrors();
return FluentIterable.from(annotations)
.filter(
new Predicate<AnnotationMirror>() {
@Override
public boolean apply(AnnotationMirror input) {
return isAnnotationPresent(input.getAnnotationType().asElement(), annotationType);
}
})
.toSet();
Element element, Class<? extends Annotation> annotationClass) {
return getAnnotatedAnnotations(element, annotationClass.getCanonicalName());
}

/**
* Returns all {@linkplain AnnotationMirror annotations} that are present on the given {@link
* Element} which are themselves annotated with {@code annotation}.
*/
public static ImmutableSet<? extends AnnotationMirror> getAnnotatedAnnotations(
Element element, TypeElement annotation) {
return element.getAnnotationMirrors().stream()
.filter(input -> isAnnotationPresent(input.getAnnotationType().asElement(), annotation))
.collect(toImmutableSet());
}

/**
* Returns all {@linkplain AnnotationMirror annotations} that are present on the given {@link
* Element} which are themselves annotated with an annotation whose type's canonical name is
* {@code annotationName}.
*/
public static ImmutableSet<? extends AnnotationMirror> getAnnotatedAnnotations(
Element element, String annotationName) {
return element.getAnnotationMirrors().stream()
.filter(input -> isAnnotationPresent(input.getAnnotationType().asElement(), annotationName))
.collect(toImmutableSet());
}

private AnnotationMirrors() {}
Expand Down
59 changes: 53 additions & 6 deletions common/src/main/java/com/google/auto/common/MoreElements.java
Original file line number Diff line number Diff line change
Expand Up @@ -212,17 +212,38 @@ public static ExecutableElement asExecutable(Element element) {
}

/**
* Returns {@code true} iff the given element has an {@link AnnotationMirror} whose
* {@linkplain AnnotationMirror#getAnnotationType() annotation type} has the same canonical name
* as that of {@code annotationClass}. This method is a safer alternative to calling
* {@link Element#getAnnotation} and checking for {@code null} as it avoids any interaction with
* Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
* AnnotationMirror#getAnnotationType() annotation type} has the same canonical name as that of
* {@code annotationClass}. This method is a safer alternative to calling {@link
* Element#getAnnotation} and checking for {@code null} as it avoids any interaction with
* annotation proxies.
*/
public static boolean isAnnotationPresent(
Element element, Class<? extends Annotation> annotationClass) {
return getAnnotationMirror(element, annotationClass).isPresent();
}

/**
* Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
* AnnotationMirror#getAnnotationType() annotation type} has the same fully qualified name as that
* of {@code annotation}. This method is a safer alternative to calling {@link
* Element#getAnnotation} and checking for {@code null} as it avoids any interaction with
* annotation proxies.
*/
public static boolean isAnnotationPresent(Element element, TypeElement annotation) {
return getAnnotationMirror(element, annotation).isPresent();
}

/**
* Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
* AnnotationMirror#getAnnotationType() annotation type} has {@code annotationName} as its
* canonical name. This method is a safer alternative to calling {@link Element#getAnnotation} and
* checking for {@code null} as it avoids any interaction with annotation proxies.
*/
public static boolean isAnnotationPresent(Element element, String annotationName) {
return getAnnotationMirror(element, annotationName).isPresent();
}

/**
* Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClass} on
* {@code element}, or {@link Optional#absent()} if no such annotation exists. This method is a
Expand All @@ -231,10 +252,36 @@ public static boolean isAnnotationPresent(
*/
public static Optional<AnnotationMirror> getAnnotationMirror(
Element element, Class<? extends Annotation> annotationClass) {
String annotationClassName = annotationClass.getCanonicalName();
return getAnnotationMirror(element, annotationClass.getCanonicalName());
}

/**
* Returns an {@link AnnotationMirror} for the annotation of type {@code annotation} on {@code
* element}, or {@link Optional#absent()} if no such annotation exists. This method is a safer
* alternative to calling {@link Element#getAnnotation} as it avoids any interaction with
* annotation proxies.
*/
public static Optional<AnnotationMirror> getAnnotationMirror(
Element element, TypeElement annotation) {
for (AnnotationMirror elementAnnotation : element.getAnnotationMirrors()) {
if (elementAnnotation.getAnnotationType().asElement().equals(annotation)) {
return Optional.of(elementAnnotation);
}
}
return Optional.absent();
}

/**
* Returns an {@link AnnotationMirror} for the annotation whose type's canonical name is on {@code
* element}, or {@link Optional#absent()} if no such annotation exists. This method is a safer
* alternative to calling {@link Element#getAnnotation} as it avoids any interaction with
* annotation proxies.
*/
public static Optional<AnnotationMirror> getAnnotationMirror(
Element element, String annotationName) {
for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
TypeElement annotationTypeElement = asType(annotationMirror.getAnnotationType().asElement());
if (annotationTypeElement.getQualifiedName().contentEquals(annotationClassName)) {
if (annotationTypeElement.getQualifiedName().contentEquals(annotationName)) {
return Optional.of(annotationMirror);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@
import static com.google.testing.compile.CompilationSubject.assertThat;
import static org.junit.Assert.fail;

import com.google.common.collect.ImmutableSet;
import com.google.common.testing.EquivalenceTester;
import com.google.common.truth.Correspondence;
import com.google.testing.compile.CompilationRule;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleAnnotationValueVisitor6;
Expand Down Expand Up @@ -252,4 +257,53 @@ public void testGetValueEntryFailure() {
private AnnotationMirror annotationOn(Class<?> clazz) {
return getOnlyElement(elements.getTypeElement(clazz.getCanonicalName()).getAnnotationMirrors());
}

@Retention(RetentionPolicy.RUNTIME)
private @interface AnnotatingAnnotation {}

@AnnotatingAnnotation
@Retention(RetentionPolicy.RUNTIME)
private @interface AnnotatedAnnotation1 {}

@AnnotatingAnnotation
@Retention(RetentionPolicy.RUNTIME)
private @interface AnnotatedAnnotation2 {}

@Retention(RetentionPolicy.RUNTIME)
private @interface NotAnnotatedAnnotation {}

@AnnotatedAnnotation1
@NotAnnotatedAnnotation
@AnnotatedAnnotation2
private static final class AnnotatedClass {}

@Test
public void getAnnotatedAnnotations() {
TypeElement element = elements.getTypeElement(AnnotatedClass.class.getCanonicalName());

// Test Class API
getAnnotatedAnnotationsAsserts(
AnnotationMirrors.getAnnotatedAnnotations(element, AnnotatingAnnotation.class));

// Test String API
String annotatingAnnotationName = AnnotatingAnnotation.class.getCanonicalName();
getAnnotatedAnnotationsAsserts(
AnnotationMirrors.getAnnotatedAnnotations(element, annotatingAnnotationName));

// Test TypeElement API
TypeElement annotatingAnnotationElement = elements.getTypeElement(annotatingAnnotationName);
getAnnotatedAnnotationsAsserts(
AnnotationMirrors.getAnnotatedAnnotations(element, annotatingAnnotationElement));
}

private void getAnnotatedAnnotationsAsserts(
ImmutableSet<? extends AnnotationMirror> annotatedAnnotations) {
assertThat(annotatedAnnotations)
.comparingElementsUsing(
Correspondence.transforming(
(AnnotationMirror a) -> MoreTypes.asTypeElement(a.getAnnotationType()), "has type"))
.containsExactly(
elements.getTypeElement(AnnotatedAnnotation1.class.getCanonicalName()),
elements.getTypeElement(AnnotatedAnnotation2.class.getCanonicalName()));
}
}

0 comments on commit dec3bf0

Please sign in to comment.