diff options
author | mahsabadian <mahsabadian@google.com> | 2019-10-21 14:20:03 -0700 |
---|---|---|
committer | Colin Decker <cgdecker@gmail.com> | 2019-10-22 11:47:18 -0400 |
commit | 6c2c1a3ff8d2abc74755dfe18c07fa60ee1c7733 (patch) | |
tree | 08941e5a5bdb10795320b9557d83326ebe3c9a63 /common | |
parent | 7dc5450ec1abdd6473a5dd11dcb19f4115ff7a89 (diff) | |
download | auto-6c2c1a3ff8d2abc74755dfe18c07fa60ee1c7733.tar.gz |
Helper Methods in AnnotationValues
RELNOTES=Adding helper methods for getting different types of annotation values with AnnotationValueVisitor.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=275921910
Diffstat (limited to 'common')
-rw-r--r-- | common/src/main/java/com/google/auto/common/AnnotationValues.java | 355 | ||||
-rw-r--r-- | common/src/test/java/com/google/auto/common/AnnotationValuesTest.java | 357 |
2 files changed, 712 insertions, 0 deletions
diff --git a/common/src/main/java/com/google/auto/common/AnnotationValues.java b/common/src/main/java/com/google/auto/common/AnnotationValues.java index ac18879b..e2339018 100644 --- a/common/src/main/java/com/google/auto/common/AnnotationValues.java +++ b/common/src/main/java/com/google/auto/common/AnnotationValues.java @@ -15,10 +15,17 @@ */ package com.google.auto.common; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.ImmutableList.toImmutableList; + import com.google.common.base.Equivalence; +import com.google.common.collect.ImmutableList; import java.util.List; +import java.util.function.Function; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleAnnotationValueVisitor8; @@ -130,5 +137,353 @@ public final class AnnotationValues { return ANNOTATION_VALUE_EQUIVALENCE; } + private static class DefaultVisitor<T> extends SimpleAnnotationValueVisitor8<T, Void> { + final Class<T> clazz; + + DefaultVisitor(Class<T> clazz) { + this.clazz = checkNotNull(clazz); + } + + @Override + public T defaultAction(Object o, Void unused) { + throw new IllegalArgumentException( + "Expected a " + clazz.getSimpleName() + ", got instead: " + o); + } + } + + private static final class TypeMirrorVisitor extends DefaultVisitor<DeclaredType> { + static final TypeMirrorVisitor INSTANCE = new TypeMirrorVisitor(); + + TypeMirrorVisitor() { + super(DeclaredType.class); + } + + @Override + public DeclaredType visitType(TypeMirror value, Void unused) { + return MoreTypes.asDeclared(value); + } + } + ; + + /** + * Returns the value as a class. + * + * @throws IllegalArgumentException if the value is not a class. + */ + public static DeclaredType getTypeMirror(AnnotationValue value) { + return TypeMirrorVisitor.INSTANCE.visit(value); + } + + private static final class AnnotationMirrorVisitor extends DefaultVisitor<AnnotationMirror> { + static final AnnotationMirrorVisitor INSTANCE = new AnnotationMirrorVisitor(); + + AnnotationMirrorVisitor() { + super(AnnotationMirror.class); + } + + @Override + public AnnotationMirror visitAnnotation(AnnotationMirror value, Void unused) { + return value; + } + } + ; + + /** + * Returns the value as an AnnotationMirror. + * + * @throws IllegalArgumentException if the value is not an annotation. + */ + public static AnnotationMirror getAnnotationMirror(AnnotationValue value) { + return AnnotationMirrorVisitor.INSTANCE.visit(value); + } + + private static final class EnumVisitor extends DefaultVisitor<VariableElement> { + static final EnumVisitor INSTANCE = new EnumVisitor(); + + EnumVisitor() { + super(VariableElement.class); + } + + @Override + public VariableElement visitEnumConstant(VariableElement value, Void unused) { + return value; + } + } + ; + + /** + * Returns the value as a VariableElement. + * + * @throws IllegalArgumentException if the value is not an enum. + */ + public static VariableElement getEnum(AnnotationValue value) { + return EnumVisitor.INSTANCE.visit(value); + } + + private static <T> T valueOfType(AnnotationValue annotationValue, Class<T> type) { + Object value = annotationValue.getValue(); + if (!type.isInstance(value)) { + throw new IllegalArgumentException( + "Expected " + type.getSimpleName() + ", got instead: " + value); + } + return type.cast(value); + } + + /** + * Returns the value as a string. + * + * @throws IllegalArgumentException if the value is not a string. + */ + public static String getString(AnnotationValue value) { + return valueOfType(value, String.class); + } + + /** + * Returns the value as an int. + * + * @throws IllegalArgumentException if the value is not an int. + */ + public static int getInt(AnnotationValue value) { + return valueOfType(value, Integer.class); + } + + /** + * Returns the value as a long. + * + * @throws IllegalArgumentException if the value is not a long. + */ + public static long getLong(AnnotationValue value) { + return valueOfType(value, Long.class); + } + + /** + * Returns the value as a byte. + * + * @throws IllegalArgumentException if the value is not a byte. + */ + public static byte getByte(AnnotationValue value) { + return valueOfType(value, Byte.class); + } + + /** + * Returns the value as a short. + * + * @throws IllegalArgumentException if the value is not a short. + */ + public static short getShort(AnnotationValue value) { + return valueOfType(value, Short.class); + } + + /** + * Returns the value as a float. + * + * @throws IllegalArgumentException if the value is not a float. + */ + public static float getFloat(AnnotationValue value) { + return valueOfType(value, Float.class); + } + + /** + * Returns the value as a double. + * + * @throws IllegalArgumentException if the value is not a double. + */ + public static double getDouble(AnnotationValue value) { + return valueOfType(value, Double.class); + } + + /** + * Returns the value as a boolean. + * + * @throws IllegalArgumentException if the value is not a boolean. + */ + public static boolean getBoolean(AnnotationValue value) { + return valueOfType(value, Boolean.class); + } + + /** + * Returns the value as a char. + * + * @throws IllegalArgumentException if the value is not a char. + */ + public static char getChar(AnnotationValue value) { + return valueOfType(value, Character.class); + } + + private static final class ArrayVisitor<T> + extends SimpleAnnotationValueVisitor8<ImmutableList<T>, Void> { + final Function<AnnotationValue, T> visitT; + + ArrayVisitor(Function<AnnotationValue, T> visitT) { + this.visitT = checkNotNull(visitT); + } + + @Override + public ImmutableList<T> defaultAction(Object o, Void unused) { + throw new IllegalStateException("Expected an array, got instead: " + o); + } + + @Override + public ImmutableList<T> visitArray(List<? extends AnnotationValue> values, Void unused) { + return values.stream().map(visitT).collect(toImmutableList()); + } + } + + private static final ArrayVisitor<DeclaredType> TYPE_MIRRORS_VISITOR = + new ArrayVisitor<>(AnnotationValues::getTypeMirror); + + /** + * Returns the value as a list of classes. + * + * @throws IllegalArgumentException if the value is not an array of classes. + */ + public static ImmutableList<DeclaredType> getTypeMirrors(AnnotationValue value) { + return TYPE_MIRRORS_VISITOR.visit(value); + } + + private static final ArrayVisitor<AnnotationMirror> ANNOTATION_MIRRORS_VISITOR = + new ArrayVisitor<>(AnnotationValues::getAnnotationMirror); + + /** + * Returns the value as a list of annotations. + * + * @throws IllegalArgumentException if the value if not an array of annotations. + */ + public static ImmutableList<AnnotationMirror> getAnnotationMirrors(AnnotationValue value) { + return ANNOTATION_MIRRORS_VISITOR.visit(value); + } + + private static final ArrayVisitor<VariableElement> ENUMS_VISITOR = + new ArrayVisitor<>(AnnotationValues::getEnum); + + /** + * Returns the value as a list of enums. + * + * @throws IllegalArgumentException if the value is not an array of enums. + */ + public static ImmutableList<VariableElement> getEnums(AnnotationValue value) { + return ENUMS_VISITOR.visit(value); + } + + private static final ArrayVisitor<String> STRINGS_VISITOR = + new ArrayVisitor<>(AnnotationValues::getString); + + /** + * Returns the value as a list of strings. + * + * @throws IllegalArgumentException if the value is not an array of strings. + */ + public static ImmutableList<String> getStrings(AnnotationValue value) { + return STRINGS_VISITOR.visit(value); + } + + private static final ArrayVisitor<Integer> INTS_VISITOR = + new ArrayVisitor<>(AnnotationValues::getInt); + + /** + * Returns the value as a list of integers. + * + * @throws IllegalArgumentException if the value is not an array of ints. + */ + public static ImmutableList<Integer> getInts(AnnotationValue value) { + return INTS_VISITOR.visit(value); + } + + private static final ArrayVisitor<Long> LONGS_VISITOR = + new ArrayVisitor<>(AnnotationValues::getLong); + + /** + * Returns the value as a list of longs. + * + * @throws IllegalArgumentException if the value is not an array of longs. + */ + public static ImmutableList<Long> getLongs(AnnotationValue value) { + return LONGS_VISITOR.visit(value); + } + + private static final ArrayVisitor<Byte> BYTES_VISITOR = + new ArrayVisitor<>(AnnotationValues::getByte); + + /** + * Returns the value as a list of bytes. + * + * @throws IllegalArgumentException if the value is not an array of bytes. + */ + public static ImmutableList<Byte> getBytes(AnnotationValue value) { + return BYTES_VISITOR.visit(value); + } + + private static final ArrayVisitor<Short> SHORTS_VISITOR = + new ArrayVisitor<>(AnnotationValues::getShort); + /** + * Returns the value as a list of shorts. + * + * @throws IllegalArgumentException if the value is not an array of shorts. + */ + public static ImmutableList<Short> getShorts(AnnotationValue value) { + return SHORTS_VISITOR.visit(value); + } + + private static final ArrayVisitor<Float> FLOATS_VISITOR = + new ArrayVisitor<>(AnnotationValues::getFloat); + + /** + * Returns the value as a list of floats. + * + * @throws IllegalArgumentException if the value is not an array of floats. + */ + public static ImmutableList<Float> getFloats(AnnotationValue value) { + return FLOATS_VISITOR.visit(value); + } + + private static final ArrayVisitor<Double> DOUBLES_VISITOR = + new ArrayVisitor<>(AnnotationValues::getDouble); + + /** + * Returns the value as a list of doubles. + * + * @throws IllegalArgumentException if the value is not an array of doubles. + */ + public static ImmutableList<Double> getDoubles(AnnotationValue value) { + return DOUBLES_VISITOR.visit(value); + } + + private static final ArrayVisitor<Boolean> BOOLEANS_VISITOR = + new ArrayVisitor<>(AnnotationValues::getBoolean); + + /** + * Returns the value as a list of booleans. + * + * @throws IllegalArgumentException if the value is not an array of booleans. + */ + public static ImmutableList<Boolean> getBooleans(AnnotationValue value) { + return BOOLEANS_VISITOR.visit(value); + } + + private static final ArrayVisitor<Character> CHARS_VISITOR = + new ArrayVisitor<>(AnnotationValues::getChar); + + /** + * Returns the value as a list of characters. + * + * @throws IllegalArgumentException if the value is not an array of chars. + */ + public static ImmutableList<Character> getChars(AnnotationValue value) { + return CHARS_VISITOR.visit(value); + } + + private static final ArrayVisitor<AnnotationValue> ANNOTATION_VALUES_VISITOR = + new ArrayVisitor<>(x -> x); + + /** + * Returns the value as a list of {@link AnnotationValue}s. + * + * @throws IllegalArgumentException if the value is not an array. + */ + public static ImmutableList<AnnotationValue> getAnnotationValues(AnnotationValue value) { + return ANNOTATION_VALUES_VISITOR.visit(value); + } + private AnnotationValues() {} } + diff --git a/common/src/test/java/com/google/auto/common/AnnotationValuesTest.java b/common/src/test/java/com/google/auto/common/AnnotationValuesTest.java new file mode 100644 index 00000000..825c85af --- /dev/null +++ b/common/src/test/java/com/google/auto/common/AnnotationValuesTest.java @@ -0,0 +1,357 @@ +/* + * Copyright 2019 Google LLC + * + * 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 com.google.auto.common; + +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableList; +import com.google.common.truth.Correspondence; +import com.google.testing.compile.CompilationRule; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests {@link AnnotationValues}. */ +@RunWith(JUnit4.class) +public final class AnnotationValuesTest { + + private @interface InsideAnnotation { + int value(); + } + + private static class GenericClass<T> {} + + private static class InsideClassA {} + + private static class InsideClassB {} + + private @interface MultiValueAnnotation { + Class<InsideClassA> classValue(); + + Class<?>[] classValues(); + + Class<?> genericClassValue(); + + InsideAnnotation insideAnnotationValue(); + + InsideAnnotation[] insideAnnotationValues(); + + String stringValue(); + + String[] stringValues(); + + Foo enumValue(); + + Foo[] enumValues(); + + int intValue(); + + int[] intValues(); + + long longValue(); + + long[] longValues(); + + byte byteValue(); + + byte[] byteValues(); + + short shortValue(); + + short[] shortValues(); + + float floatValue(); + + float[] floatValues(); + + double doubleValue(); + + double[] doubleValues(); + + boolean booleanValue(); + + boolean[] booleanValues(); + + char charValue(); + + char[] charValues(); + } + + private enum Foo { + BAR, + BAZ, + BAH; + } + + @MultiValueAnnotation( + classValue = InsideClassA.class, + classValues = {InsideClassA.class, InsideClassB.class}, + genericClassValue = GenericClass.class, + insideAnnotationValue = @InsideAnnotation(19), + insideAnnotationValues = {@InsideAnnotation(20), @InsideAnnotation(21)}, + stringValue = "hello", + stringValues = {"it's", "me"}, + enumValue = Foo.BAR, + enumValues = {Foo.BAZ, Foo.BAH}, + intValue = 5, + intValues = {1, 2}, + longValue = 6L, + longValues = {3L, 4L}, + byteValue = (byte) 7, + byteValues = {(byte) 8, (byte) 9}, + shortValue = (short) 10, + shortValues = {(short) 11, (short) 12}, + floatValue = 13F, + floatValues = {14F, 15F}, + doubleValue = 16D, + doubleValues = {17D, 18D}, + booleanValue = true, + booleanValues = {true, false}, + charValue = 'a', + charValues = {'b', 'c'}) + private static class AnnotatedClass {} + + @Rule public final CompilationRule compilation = new CompilationRule(); + + private Elements elements; + private Types types; + private AnnotationMirror annotationMirror; + + @Before + public void setUp() { + elements = compilation.getElements(); + types = compilation.getTypes(); + TypeElement annotatedClass = getTypeElement(AnnotatedClass.class); + annotationMirror = + MoreElements.getAnnotationMirror(annotatedClass, MultiValueAnnotation.class).get(); + } + + @Test + public void getTypeMirror() { + TypeElement insideClassA = getTypeElement(InsideClassA.class); + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "classValue"); + assertThat(AnnotationValues.getTypeMirror(value).asElement()).isEqualTo(insideClassA); + } + + @Test + public void getTypeMirrorGenericClass() { + TypeElement genericClass = getTypeElement(GenericClass.class); + AnnotationValue gvalue = + AnnotationMirrors.getAnnotationValue(annotationMirror, "genericClassValue"); + assertThat(AnnotationValues.getTypeMirror(gvalue).asElement()).isEqualTo(genericClass); + } + + @Test + public void getTypeMirrors() { + TypeMirror insideClassA = getTypeElement(InsideClassA.class).asType(); + TypeMirror insideClassB = getTypeElement(InsideClassB.class).asType(); + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "classValues"); + ImmutableList<DeclaredType> valueElements = AnnotationValues.getTypeMirrors(value); + assertThat(valueElements) + .comparingElementsUsing(Correspondence.from(types::isSameType, "has Same Type")) + .containsExactly(insideClassA, insideClassB) + .inOrder(); + } + + @Test + public void getAnnotationMirror() { + TypeElement insideAnnotation = getTypeElement(InsideAnnotation.class); + AnnotationValue value = + AnnotationMirrors.getAnnotationValue(annotationMirror, "insideAnnotationValue"); + AnnotationMirror annotationMirror = AnnotationValues.getAnnotationMirror(value); + assertThat(annotationMirror.getAnnotationType().asElement()).isEqualTo(insideAnnotation); + assertThat(AnnotationMirrors.getAnnotationValue(annotationMirror, "value").getValue()) + .isEqualTo(19); + } + + @Test + public void getAnnotationMirrors() { + TypeElement insideAnnotation = getTypeElement(InsideAnnotation.class); + AnnotationValue value = + AnnotationMirrors.getAnnotationValue(annotationMirror, "insideAnnotationValues"); + ImmutableList<AnnotationMirror> annotationMirrors = + AnnotationValues.getAnnotationMirrors(value); + ImmutableList<Element> valueElements = + annotationMirrors.stream() + .map(AnnotationMirror::getAnnotationType) + .map(DeclaredType::asElement) + .collect(toImmutableList()); + assertThat(valueElements).containsExactly(insideAnnotation, insideAnnotation); + ImmutableList<Object> valuesStoredInAnnotation = + annotationMirrors.stream() + .map( + annotationMirror -> + AnnotationMirrors.getAnnotationValue(annotationMirror, "value").getValue()) + .collect(toImmutableList()); + assertThat(valuesStoredInAnnotation).containsExactly(20, 21).inOrder(); + } + + @Test + public void getString() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "stringValue"); + assertThat(AnnotationValues.getString(value)).isEqualTo("hello"); + } + + @Test + public void getStrings() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "stringValues"); + assertThat(AnnotationValues.getStrings(value)).containsExactly("it's", "me").inOrder(); + } + + @Test + public void getEnum() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "enumValue"); + assertThat(AnnotationValues.getEnum(value)).isEqualTo(value.getValue()); + } + + @Test + public void getEnums() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "enumValues"); + assertThat(getEnumNames(AnnotationValues.getEnums(value))) + .containsExactly(Foo.BAZ.name(), Foo.BAH.name()) + .inOrder(); + } + + @Test + public void getAnnotationValues() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "intValues"); + ImmutableList<AnnotationValue> values = AnnotationValues.getAnnotationValues(value); + assertThat(values) + .comparingElementsUsing(Correspondence.transforming(AnnotationValue::getValue, "has value")) + .containsExactly(1, 2) + .inOrder(); + } + + @Test + public void getInt() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "intValue"); + assertThat(AnnotationValues.getInt(value)).isEqualTo(5); + } + + @Test + public void getInts() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "intValues"); + assertThat(AnnotationValues.getInts(value)).containsExactly(1, 2).inOrder(); + } + + @Test + public void getLong() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "longValue"); + assertThat(AnnotationValues.getLong(value)).isEqualTo(6L); + } + + @Test + public void getLongs() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "longValues"); + assertThat(AnnotationValues.getLongs(value)).containsExactly(3L, 4L).inOrder(); + } + + @Test + public void getByte() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "byteValue"); + assertThat(AnnotationValues.getByte(value)).isEqualTo((byte) 7); + } + + @Test + public void getBytes() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "byteValues"); + assertThat(AnnotationValues.getBytes(value)).containsExactly((byte) 8, (byte) 9).inOrder(); + } + + @Test + public void getShort() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "shortValue"); + assertThat(AnnotationValues.getShort(value)).isEqualTo((short) 10); + } + + @Test + public void getShorts() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "shortValues"); + assertThat(AnnotationValues.getShorts(value)).containsExactly((short) 11, (short) 12).inOrder(); + } + + @Test + public void getFloat() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "floatValue"); + assertThat(AnnotationValues.getFloat(value)).isEqualTo(13F); + } + + @Test + public void getFloats() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "floatValues"); + assertThat(AnnotationValues.getFloats(value)).containsExactly(14F, 15F).inOrder(); + } + + @Test + public void getDouble() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "doubleValue"); + assertThat(AnnotationValues.getDouble(value)).isEqualTo(16D); + } + + @Test + public void getDoubles() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "doubleValues"); + assertThat(AnnotationValues.getDoubles(value)).containsExactly(17D, 18D).inOrder(); + } + + @Test + public void getBoolean() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "booleanValue"); + assertThat(AnnotationValues.getBoolean(value)).isTrue(); + } + + @Test + public void getBooleans() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "booleanValues"); + assertThat(AnnotationValues.getBooleans(value)).containsExactly(true, false).inOrder(); + } + + @Test + public void getChar() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "charValue"); + assertThat(AnnotationValues.getChar(value)).isEqualTo('a'); + } + + @Test + public void getChars() { + AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "charValues"); + assertThat(AnnotationValues.getChars(value)).containsExactly('b', 'c').inOrder(); + } + + private TypeElement getTypeElement(Class<?> clazz) { + return elements.getTypeElement(clazz.getCanonicalName()); + } + + private static ImmutableList<String> getEnumNames(ImmutableList<VariableElement> values) { + return values.stream() + .map(VariableElement::getSimpleName) + .map(Name::toString) + .collect(toImmutableList()); + } +} |