• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Google Inc. All Rights Reserved.
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.turbine.processing;
18 
19 import static com.google.common.collect.ImmutableList.toImmutableList;
20 import static com.google.common.collect.ImmutableMap.toImmutableMap;
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import com.google.auto.common.AnnotationValues;
24 import com.google.common.base.Joiner;
25 import com.google.common.collect.ImmutableList;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.collect.ListMultimap;
28 import com.google.common.collect.MultimapBuilder;
29 import com.google.common.collect.Multimaps;
30 import com.google.turbine.binder.Binder;
31 import com.google.turbine.binder.bound.TypeBoundClass;
32 import com.google.turbine.binder.env.CompoundEnv;
33 import com.google.turbine.binder.env.Env;
34 import com.google.turbine.binder.env.SimpleEnv;
35 import com.google.turbine.binder.sym.ClassSymbol;
36 import com.google.turbine.lower.IntegrationTestSupport;
37 import com.google.turbine.lower.IntegrationTestSupport.TestInput;
38 import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
39 import com.google.turbine.testing.TestClassPaths;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Optional;
43 import java.util.stream.Stream;
44 import javax.lang.model.element.AnnotationMirror;
45 import javax.lang.model.element.AnnotationValue;
46 import javax.lang.model.element.Element;
47 import javax.lang.model.element.ElementKind;
48 import javax.lang.model.element.ExecutableElement;
49 import javax.lang.model.element.TypeElement;
50 import javax.lang.model.element.VariableElement;
51 import javax.lang.model.type.TypeMirror;
52 import javax.lang.model.util.AbstractAnnotationValueVisitor8;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 import org.junit.runners.JUnit4;
56 
57 @RunWith(JUnit4.class)
58 public class TurbineAnnotationMirrorTest {
59 
getAnnotation( List<? extends AnnotationMirror> annotationMirrors, String name)60   private AnnotationMirror getAnnotation(
61       List<? extends AnnotationMirror> annotationMirrors, String name) {
62     return annotationMirrors.stream()
63         .filter(x -> x.getAnnotationType().asElement().getSimpleName().contentEquals(name))
64         .findFirst()
65         .get();
66   }
67 
values(AnnotationMirror a)68   private ImmutableMap<String, Object> values(AnnotationMirror a) {
69     return values(a.getElementValues());
70   }
71 
72   /**
73    * Returns a map from the name of annotation elements to their values, see also {@link
74    * #getValue(AnnotationValue)}.
75    */
values( Map<? extends ExecutableElement, ? extends AnnotationValue> values)76   private ImmutableMap<String, Object> values(
77       Map<? extends ExecutableElement, ? extends AnnotationValue> values) {
78     return values.entrySet().stream()
79         .collect(
80             toImmutableMap(
81                 e -> e.getKey().getSimpleName().toString(), e -> getValue(e.getValue())));
82   }
83 
84   /**
85    * Returns the given annotation value as an Object (for primitives), or a list (for arrays), or
86    * strings (for compound annotations, enums, and class literals).
87    */
getValue(AnnotationValue value)88   static Object getValue(AnnotationValue value) {
89     return value.accept(
90         new AbstractAnnotationValueVisitor8<Object, Void>() {
91           @Override
92           public Object visitBoolean(boolean b, Void unused) {
93             return b;
94           }
95 
96           @Override
97           public Object visitByte(byte b, Void unused) {
98             return b;
99           }
100 
101           @Override
102           public Object visitChar(char c, Void unused) {
103             return c;
104           }
105 
106           @Override
107           public Object visitDouble(double d, Void unused) {
108             return d;
109           }
110 
111           @Override
112           public Object visitFloat(float f, Void unused) {
113             return f;
114           }
115 
116           @Override
117           public Object visitInt(int i, Void unused) {
118             return i;
119           }
120 
121           @Override
122           public Object visitLong(long i, Void unused) {
123             return i;
124           }
125 
126           @Override
127           public Object visitShort(short s, Void unused) {
128             return s;
129           }
130 
131           @Override
132           public Object visitString(String s, Void unused) {
133             return s;
134           }
135 
136           @Override
137           public Object visitType(TypeMirror t, Void unused) {
138             return AnnotationValues.toString(value);
139           }
140 
141           @Override
142           public Object visitEnumConstant(VariableElement c, Void unused) {
143             return AnnotationValues.toString(value);
144           }
145 
146           @Override
147           public Object visitAnnotation(AnnotationMirror a, Void unused) {
148             return AnnotationValues.toString(value);
149           }
150 
151           @Override
152           public Object visitArray(List<? extends AnnotationValue> vals, Void unused) {
153             return vals.stream().map(v -> v.accept(this, null)).collect(toImmutableList());
154           }
155         },
156         null);
157   }
158 
159   private static Stream<String> typeAnnotationNames(Element e) {
160     return e.asType().getAnnotationMirrors().stream()
161         .map(anno -> anno.getAnnotationType().asElement().getSimpleName().toString());
162   }
163 
164   @Test
165   public void test() throws Exception {
166     TestInput input =
167         TestInput.parse(
168             Joiner.on('\n')
169                 .join(
170                     "=== Test.java ===",
171                     "import java.lang.annotation.ElementType;",
172                     "import java.lang.annotation.Retention;",
173                     "import java.lang.annotation.RetentionPolicy;",
174                     "import java.lang.annotation.Target;",
175                     "import java.util.Map;",
176                     "import java.util.Map.Entry;",
177                     "@Retention(RetentionPolicy.RUNTIME)",
178                     "@interface A {",
179                     "  int x() default 0;",
180                     "  int y() default 1;",
181                     "  int[] z() default {};",
182                     "}",
183                     "@interface B {",
184                     "  Class<?> c() default String.class;",
185                     "  ElementType e() default ElementType.TYPE_USE;",
186                     "  A f() default @A;",
187                     "}",
188                     "@Retention(RetentionPolicy.RUNTIME)",
189                     "@Target(ElementType.TYPE_USE)",
190                     "@interface T {}",
191                     "@Target(ElementType.TYPE_USE)",
192                     "@interface V {}",
193                     "",
194                     "@A(y = 42, z = {43})",
195                     "@B",
196                     "class Test {",
197                     "  class I {}",
198                     "  @T Test. @V I f;",
199                     "  Map. @T Entry g;",
200                     "  @T Entry h;",
201                     "}",
202                     ""));
203 
204     Binder.BindingResult bound =
205         IntegrationTestSupport.turbineAnalysis(
206             input.sources,
207             ImmutableList.of(),
208             TestClassPaths.TURBINE_BOOTCLASSPATH,
209             Optional.empty());
210 
211     Env<ClassSymbol, TypeBoundClass> env =
212         CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
213             .append(new SimpleEnv<>(bound.units()));
214     ModelFactory factory = new ModelFactory(env, ClassLoader.getSystemClassLoader(), bound.tli());
215     TurbineTypes turbineTypes = new TurbineTypes(factory);
216     TurbineElements turbineElements = new TurbineElements(factory, turbineTypes);
217 
218     TurbineTypeElement te = factory.typeElement(new ClassSymbol("Test"));
219 
220     AnnotationMirror a = getAnnotation(te.getAnnotationMirrors(), "A");
221     ((TypeElement) a.getAnnotationType().asElement()).getQualifiedName().contentEquals("A");
222     assertThat(values(a)).containsExactly("y", 42, "z", ImmutableList.of(43));
223     assertThat(values(turbineElements.getElementValuesWithDefaults(a)))
224         .containsExactly(
225             "x", 0,
226             "y", 42,
227             "z", ImmutableList.of(43));
228 
229     AnnotationMirror b = getAnnotation(te.getAnnotationMirrors(), "B");
230     assertThat(values(turbineElements.getElementValuesWithDefaults(b)))
231         .containsExactly(
232             "c", "java.lang.String.class",
233             "e", "java.lang.annotation.ElementType.TYPE_USE",
234             "f", "@A");
235 
236     ListMultimap<String, String> fieldTypeAnnotations =
237         te.getEnclosedElements().stream()
238             .filter(e -> e.getKind().equals(ElementKind.FIELD))
239             .collect(
240                 Multimaps.flatteningToMultimap(
241                     e -> e.getSimpleName().toString(),
242                     e -> typeAnnotationNames(e),
243                     MultimapBuilder.linkedHashKeys().arrayListValues()::build));
244     assertThat(fieldTypeAnnotations)
245         .containsExactly(
246             "f", "V",
247             "g", "T",
248             "h", "T");
249   }
250 }
251