Skip to content

Commit 6d9cc58

Browse files
committed
Graduate SimpleAnnotationMirror and SimpleTypeAnnotationValue from Dagger's internal code to auto-common
RELNOTES=Added `SimpleAnnotationMirror` and `SimpleTypeAnnotationValue` to easily create new `AnnotationMirror`s in code. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=179840730
1 parent 8a81a85 commit 6d9cc58

File tree

4 files changed

+439
-0
lines changed

4 files changed

+439
-0
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright (C) 2017 Google, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.auto.common;
18+
19+
import static com.google.common.base.Preconditions.checkArgument;
20+
import static com.google.common.collect.ImmutableMap.toImmutableMap;
21+
import static javax.lang.model.util.ElementFilter.methodsIn;
22+
23+
import com.google.common.base.Joiner;
24+
import com.google.common.collect.ImmutableMap;
25+
import java.util.ArrayList;
26+
import java.util.LinkedHashMap;
27+
import java.util.List;
28+
import java.util.Map;
29+
import javax.lang.model.element.AnnotationMirror;
30+
import javax.lang.model.element.AnnotationValue;
31+
import javax.lang.model.element.ElementKind;
32+
import javax.lang.model.element.ExecutableElement;
33+
import javax.lang.model.element.TypeElement;
34+
import javax.lang.model.type.DeclaredType;
35+
36+
/**
37+
* A simple implementation of the {@link AnnotationMirror} interface.
38+
*
39+
* <p>This type implements {@link #equals(Object)} and {@link #hashCode()} using {@link
40+
* AnnotationMirrors#equivalence} in accordance with the {@link AnnotationMirror} spec. Some {@link
41+
* AnnotationMirror}s, however, do not correctly implement equals, you should always compare them
42+
* using {@link AnnotationMirrors#equivalence} anyway.
43+
*/
44+
public final class SimpleAnnotationMirror implements AnnotationMirror {
45+
private final TypeElement annotationType;
46+
private final ImmutableMap<String, ? extends AnnotationValue> namedValues;
47+
private final ImmutableMap<ExecutableElement, ? extends AnnotationValue> elementValues;
48+
49+
private SimpleAnnotationMirror(
50+
TypeElement annotationType, Map<String, ? extends AnnotationValue> namedValues) {
51+
checkArgument(
52+
annotationType.getKind().equals(ElementKind.ANNOTATION_TYPE),
53+
"annotationType must be an annotation: %s",
54+
annotationType);
55+
Map<String, AnnotationValue> values = new LinkedHashMap<>();
56+
Map<String, AnnotationValue> unusedValues = new LinkedHashMap<>(namedValues);
57+
List<String> missingMembers = new ArrayList<>();
58+
for (ExecutableElement method : methodsIn(annotationType.getEnclosedElements())) {
59+
String memberName = method.getSimpleName().toString();
60+
if (unusedValues.containsKey(memberName)) {
61+
values.put(memberName, unusedValues.remove(memberName));
62+
} else if (method.getDefaultValue() != null) {
63+
values.put(memberName, method.getDefaultValue());
64+
} else {
65+
missingMembers.add(memberName);
66+
}
67+
}
68+
69+
checkArgument(
70+
unusedValues.isEmpty(),
71+
"namedValues has entries for members that are not in %s: %s",
72+
annotationType,
73+
unusedValues);
74+
checkArgument(
75+
missingMembers.isEmpty(), "namedValues is missing entries for: %s", missingMembers);
76+
77+
this.annotationType = annotationType;
78+
this.namedValues = ImmutableMap.copyOf(namedValues);
79+
this.elementValues =
80+
methodsIn(annotationType.getEnclosedElements())
81+
.stream()
82+
.collect(toImmutableMap(e -> e, e -> values.get(e.getSimpleName().toString())));
83+
}
84+
85+
/**
86+
* An object representing an {@linkplain ElementKind#ANNOTATION_TYPE annotation} instance. If
87+
* {@code annotationType} has any annotation members, they must have default values.
88+
*/
89+
public static AnnotationMirror of(TypeElement annotationType) {
90+
return of(annotationType, ImmutableMap.of());
91+
}
92+
93+
/**
94+
* An object representing an {@linkplain ElementKind#ANNOTATION_TYPE annotation} instance. If
95+
* {@code annotationType} has any annotation members, they must either be present in {@code
96+
* namedValues} or have default values.
97+
*/
98+
public static AnnotationMirror of(
99+
TypeElement annotationType, Map<String, ? extends AnnotationValue> namedValues) {
100+
return new SimpleAnnotationMirror(annotationType, namedValues);
101+
}
102+
103+
@Override
104+
public DeclaredType getAnnotationType() {
105+
return MoreTypes.asDeclared(annotationType.asType());
106+
}
107+
108+
@Override
109+
public Map<ExecutableElement, ? extends AnnotationValue> getElementValues() {
110+
return elementValues;
111+
}
112+
113+
@Override
114+
public String toString() {
115+
StringBuilder builder = new StringBuilder("@").append(annotationType.getQualifiedName());
116+
if (!namedValues.isEmpty()) {
117+
builder
118+
.append('(')
119+
.append(Joiner.on(", ").withKeyValueSeparator(" = ").join(namedValues))
120+
.append(')');
121+
}
122+
return builder.toString();
123+
}
124+
125+
@Override
126+
public boolean equals(Object other) {
127+
return other instanceof AnnotationMirror
128+
&& AnnotationMirrors.equivalence().equivalent(this, (AnnotationMirror) other);
129+
}
130+
131+
@Override
132+
public int hashCode() {
133+
return AnnotationMirrors.equivalence().hash(this);
134+
}
135+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (C) 2017 Google, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.auto.common;
18+
19+
import static com.google.common.base.Preconditions.checkArgument;
20+
import static javax.lang.model.type.TypeKind.ARRAY;
21+
import static javax.lang.model.type.TypeKind.DECLARED;
22+
23+
import javax.lang.model.element.AnnotationValue;
24+
import javax.lang.model.element.AnnotationValueVisitor;
25+
import javax.lang.model.type.TypeMirror;
26+
27+
/**
28+
* A simple implementation of the {@link AnnotationValue} interface for a class literal, e.g. an
29+
* annotation member of type {@code Class<?>} or {@code Class<? extends Foo>}.
30+
*/
31+
public final class SimpleTypeAnnotationValue implements AnnotationValue {
32+
private final TypeMirror value;
33+
34+
private SimpleTypeAnnotationValue(TypeMirror value) {
35+
checkArgument(
36+
value.getKind().isPrimitive()
37+
|| value.getKind().equals(DECLARED)
38+
|| value.getKind().equals(ARRAY),
39+
"value must be a primitive, array, or declared type, but was %s (%s)",
40+
value.getKind(),
41+
value);
42+
if (value.getKind().equals(DECLARED)) {
43+
checkArgument(
44+
MoreTypes.asDeclared(value).getTypeArguments().isEmpty(),
45+
"value must not be a parameterized type: %s",
46+
value);
47+
}
48+
this.value = value;
49+
}
50+
51+
/**
52+
* An object representing an annotation value instance.
53+
*
54+
* @param value a primitive, array, or non-parameterized declared type
55+
*/
56+
public static AnnotationValue of(TypeMirror value) {
57+
return new SimpleTypeAnnotationValue(value);
58+
}
59+
60+
@Override
61+
public TypeMirror getValue() {
62+
return value;
63+
}
64+
65+
@Override
66+
public String toString() {
67+
return value + ".class";
68+
}
69+
70+
@Override
71+
public <R, P> R accept(AnnotationValueVisitor<R, P> visitor, P parameter) {
72+
return visitor.visitType(getValue(), parameter);
73+
}
74+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright (C) 2017 Google, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.auto.common;
18+
19+
import static com.google.common.collect.Iterables.getOnlyElement;
20+
import static com.google.common.truth.Truth.assertThat;
21+
import static org.junit.Assert.fail;
22+
23+
import com.google.common.collect.ImmutableMap;
24+
import com.google.testing.compile.CompilationRule;
25+
import java.util.HashMap;
26+
import java.util.Map;
27+
import javax.lang.model.element.AnnotationMirror;
28+
import javax.lang.model.element.AnnotationValue;
29+
import javax.lang.model.element.AnnotationValueVisitor;
30+
import javax.lang.model.element.TypeElement;
31+
import org.junit.Rule;
32+
import org.junit.Test;
33+
import org.junit.runner.RunWith;
34+
import org.junit.runners.JUnit4;
35+
36+
/** Tests for {@link SimpleAnnotationMirror}. */
37+
@RunWith(JUnit4.class)
38+
public class SimpleAnnotationMirrorTest {
39+
@Rule public final CompilationRule compilation = new CompilationRule();
40+
41+
@interface EmptyAnnotation {}
42+
43+
@interface AnnotationWithDefault {
44+
int value() default 3;
45+
}
46+
47+
@interface MultipleValues {
48+
int value1();
49+
int value2();
50+
}
51+
52+
@Test
53+
public void emptyAnnotation() {
54+
TypeElement emptyAnnotation = getTypeElement(EmptyAnnotation.class);
55+
AnnotationMirror simpleAnnotation = SimpleAnnotationMirror.of(emptyAnnotation);
56+
assertThat(simpleAnnotation.getAnnotationType()).isEqualTo(emptyAnnotation.asType());
57+
assertThat(simpleAnnotation.getElementValues()).isEmpty();
58+
}
59+
60+
@Test
61+
public void multipleValues() {
62+
TypeElement multipleValues = getTypeElement(MultipleValues.class);
63+
Map<String, AnnotationValue> values = new HashMap<>();
64+
values.put("value1", intValue(1));
65+
values.put("value2", intValue(2));
66+
assertThat(SimpleAnnotationMirror.of(multipleValues, values).getElementValues()).hasSize(2);
67+
}
68+
69+
@Test
70+
public void extraValues() {
71+
TypeElement multipleValues = getTypeElement(MultipleValues.class);
72+
Map<String, AnnotationValue> values = new HashMap<>();
73+
values.put("value1", intValue(1));
74+
values.put("value2", intValue(2));
75+
values.put("value3", intValue(3));
76+
expectThrows(() -> SimpleAnnotationMirror.of(multipleValues, values));
77+
}
78+
79+
@Test
80+
public void defaultValue() {
81+
TypeElement withDefaults = getTypeElement(AnnotationWithDefault.class);
82+
AnnotationMirror annotation = SimpleAnnotationMirror.of(withDefaults);
83+
assertThat(annotation.getElementValues()).hasSize(1);
84+
assertThat(getOnlyElement(annotation.getElementValues().values()).getValue()).isEqualTo(3);
85+
}
86+
87+
@Test
88+
public void overriddenDefaultValue() {
89+
TypeElement withDefaults = getTypeElement(AnnotationWithDefault.class);
90+
AnnotationMirror annotation =
91+
SimpleAnnotationMirror.of(withDefaults, ImmutableMap.of("value", intValue(4)));
92+
assertThat(annotation.getElementValues()).hasSize(1);
93+
assertThat(getOnlyElement(annotation.getElementValues().values()).getValue()).isEqualTo(4);
94+
}
95+
96+
@Test
97+
public void missingValues() {
98+
TypeElement multipleValues = getTypeElement(MultipleValues.class);
99+
expectThrows(() -> SimpleAnnotationMirror.of(multipleValues));
100+
}
101+
102+
@Test
103+
public void notAnAnnotation() {
104+
TypeElement stringElement = getTypeElement(String.class);
105+
expectThrows(() -> SimpleAnnotationMirror.of(stringElement));
106+
}
107+
108+
private TypeElement getTypeElement(Class<?> clazz) {
109+
return compilation.getElements().getTypeElement(clazz.getCanonicalName());
110+
}
111+
112+
private static void expectThrows(Runnable throwingRunnable) {
113+
try {
114+
throwingRunnable.run();
115+
fail("Expected an IllegalArgumentException");
116+
} catch (IllegalArgumentException expected) {
117+
}
118+
}
119+
120+
private static AnnotationValue intValue(int value) {
121+
return new AnnotationValue() {
122+
@Override
123+
public Object getValue() {
124+
return value;
125+
}
126+
127+
@Override
128+
public <R, P> R accept(AnnotationValueVisitor<R, P> annotationValueVisitor, P p) {
129+
return annotationValueVisitor.visitInt(value, p);
130+
}
131+
};
132+
}
133+
}

0 commit comments

Comments
 (0)