• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 Square, 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 package com.squareup.javapoet;
17 
18 import com.google.testing.compile.CompilationRule;
19 import java.lang.annotation.Annotation;
20 import java.lang.annotation.Inherited;
21 import java.lang.annotation.Retention;
22 import java.lang.annotation.RetentionPolicy;
23 import java.util.Arrays;
24 
25 import javax.lang.model.element.TypeElement;
26 import org.junit.Rule;
27 import org.junit.Test;
28 
29 import static com.google.common.truth.Truth.assertThat;
30 import static org.junit.Assert.fail;
31 
32 public final class AnnotationSpecTest {
33 
34   @Retention(RetentionPolicy.RUNTIME)
35   public @interface AnnotationA {
36   }
37 
38   @Inherited
39   @Retention(RetentionPolicy.RUNTIME)
40   public @interface AnnotationB {
41   }
42 
43   @Retention(RetentionPolicy.RUNTIME)
44   public @interface AnnotationC {
value()45     String value();
46   }
47 
48   public enum Breakfast {
49     WAFFLES, PANCAKES;
toString()50     public String toString() { return name() + " with cherries!"; };
51   }
52 
53   @Retention(RetentionPolicy.RUNTIME)
54   public @interface HasDefaultsAnnotation {
55 
a()56     byte a() default 5;
57 
b()58     short b() default 6;
59 
c()60     int c() default 7;
61 
d()62     long d() default 8;
63 
e()64     float e() default 9.0f;
65 
f()66     double f() default 10.0;
67 
g()68     char[] g() default {0, 0xCAFE, 'z', '€', 'ℕ', '"', '\'', '\t', '\n'};
69 
h()70     boolean h() default true;
71 
i()72     Breakfast i() default Breakfast.WAFFLES;
73 
j()74     AnnotationA j() default @AnnotationA();
75 
k()76     String k() default "maple";
77 
l()78     Class<? extends Annotation> l() default AnnotationB.class;
79 
m()80     int[] m() default {1, 2, 3};
81 
n()82     Breakfast[] n() default {Breakfast.WAFFLES, Breakfast.PANCAKES};
83 
o()84     Breakfast o();
85 
p()86     int p();
87 
q()88     AnnotationC q() default @AnnotationC("foo");
89 
r()90     Class<? extends Number>[] r() default {Byte.class, Short.class, Integer.class, Long.class};
91 
92   }
93 
94   @HasDefaultsAnnotation(
95       o = Breakfast.PANCAKES,
96       p = 1701,
97       f = 11.1,
98       m = {9, 8, 1},
99       l = Override.class,
100       j = @AnnotationA,
101       q = @AnnotationC("bar"),
102       r = {Float.class, Double.class})
103   public class IsAnnotated {
104     // empty
105   }
106 
107   @Rule public final CompilationRule compilation = new CompilationRule();
108 
equalsAndHashCode()109   @Test public void equalsAndHashCode() {
110     AnnotationSpec a = AnnotationSpec.builder(AnnotationC.class).build();
111     AnnotationSpec b = AnnotationSpec.builder(AnnotationC.class).build();
112     assertThat(a.equals(b)).isTrue();
113     assertThat(a.hashCode()).isEqualTo(b.hashCode());
114     a = AnnotationSpec.builder(AnnotationC.class).addMember("value", "$S", "123").build();
115     b = AnnotationSpec.builder(AnnotationC.class).addMember("value", "$S", "123").build();
116     assertThat(a.equals(b)).isTrue();
117     assertThat(a.hashCode()).isEqualTo(b.hashCode());
118   }
119 
defaultAnnotation()120   @Test public void defaultAnnotation() {
121     String name = IsAnnotated.class.getCanonicalName();
122     TypeElement element = compilation.getElements().getTypeElement(name);
123     AnnotationSpec annotation = AnnotationSpec.get(element.getAnnotationMirrors().get(0));
124 
125     TypeSpec taco = TypeSpec.classBuilder("Taco")
126         .addAnnotation(annotation)
127         .build();
128     assertThat(toString(taco)).isEqualTo(""
129         + "package com.squareup.tacos;\n"
130         + "\n"
131         + "import com.squareup.javapoet.AnnotationSpecTest;\n"
132         + "import java.lang.Double;\n"
133         + "import java.lang.Float;\n"
134         + "import java.lang.Override;\n"
135         + "\n"
136         + "@AnnotationSpecTest.HasDefaultsAnnotation(\n"
137         + "    o = AnnotationSpecTest.Breakfast.PANCAKES,\n"
138         + "    p = 1701,\n"
139         + "    f = 11.1,\n"
140         + "    m = {\n"
141         + "        9,\n"
142         + "        8,\n"
143         + "        1\n"
144         + "    },\n"
145         + "    l = Override.class,\n"
146         + "    j = @AnnotationSpecTest.AnnotationA,\n"
147         + "    q = @AnnotationSpecTest.AnnotationC(\"bar\"),\n"
148         + "    r = {\n"
149         + "        Float.class,\n"
150         + "        Double.class\n"
151         + "    }\n"
152         + ")\n"
153         + "class Taco {\n"
154         + "}\n");
155   }
156 
defaultAnnotationWithImport()157   @Test public void defaultAnnotationWithImport() {
158     String name = IsAnnotated.class.getCanonicalName();
159     TypeElement element = compilation.getElements().getTypeElement(name);
160     AnnotationSpec annotation = AnnotationSpec.get(element.getAnnotationMirrors().get(0));
161     TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(IsAnnotated.class.getSimpleName());
162     typeBuilder.addAnnotation(annotation);
163     JavaFile file = JavaFile.builder("com.squareup.javapoet", typeBuilder.build()).build();
164     assertThat(file.toString()).isEqualTo(
165         "package com.squareup.javapoet;\n"
166             + "\n"
167             + "import java.lang.Double;\n"
168             + "import java.lang.Float;\n"
169             + "import java.lang.Override;\n"
170             + "\n"
171             + "@AnnotationSpecTest.HasDefaultsAnnotation(\n"
172             + "    o = AnnotationSpecTest.Breakfast.PANCAKES,\n"
173             + "    p = 1701,\n"
174             + "    f = 11.1,\n"
175             + "    m = {\n"
176             + "        9,\n"
177             + "        8,\n"
178             + "        1\n"
179             + "    },\n"
180             + "    l = Override.class,\n"
181             + "    j = @AnnotationSpecTest.AnnotationA,\n"
182             + "    q = @AnnotationSpecTest.AnnotationC(\"bar\"),\n"
183             + "    r = {\n"
184             + "        Float.class,\n"
185             + "        Double.class\n"
186             + "    }\n"
187             + ")\n"
188             + "class IsAnnotated {\n"
189             + "}\n"
190     );
191   }
192 
emptyArray()193   @Test public void emptyArray() {
194     AnnotationSpec.Builder builder = AnnotationSpec.builder(HasDefaultsAnnotation.class);
195     builder.addMember("n", "$L", "{}");
196     assertThat(builder.build().toString()).isEqualTo(
197         "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation(" + "n = {}" + ")");
198     builder.addMember("m", "$L", "{}");
199     assertThat(builder.build().toString())
200         .isEqualTo(
201             "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation("
202                 + "n = {}, m = {}"
203                 + ")");
204   }
205 
dynamicArrayOfEnumConstants()206   @Test public void dynamicArrayOfEnumConstants() {
207     AnnotationSpec.Builder builder = AnnotationSpec.builder(HasDefaultsAnnotation.class);
208     builder.addMember("n", "$T.$L", Breakfast.class, Breakfast.PANCAKES.name());
209     assertThat(builder.build().toString()).isEqualTo(
210         "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation("
211             + "n = com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES"
212             + ")");
213 
214     // builder = AnnotationSpec.builder(HasDefaultsAnnotation.class);
215     builder.addMember("n", "$T.$L", Breakfast.class, Breakfast.WAFFLES.name());
216     builder.addMember("n", "$T.$L", Breakfast.class, Breakfast.PANCAKES.name());
217     assertThat(builder.build().toString()).isEqualTo(
218         "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation("
219             + "n = {"
220             + "com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES"
221             + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.WAFFLES"
222             + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES"
223             + "})");
224 
225     builder = builder.build().toBuilder(); // idempotent
226     assertThat(builder.build().toString()).isEqualTo(
227         "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation("
228             + "n = {"
229             + "com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES"
230             + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.WAFFLES"
231             + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES"
232             + "})");
233 
234     builder.addMember("n", "$T.$L", Breakfast.class, Breakfast.WAFFLES.name());
235     assertThat(builder.build().toString()).isEqualTo(
236         "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation("
237             + "n = {"
238             + "com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES"
239             + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.WAFFLES"
240             + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES"
241             + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.WAFFLES"
242             + "})");
243   }
244 
defaultAnnotationToBuilder()245   @Test public void defaultAnnotationToBuilder() {
246     String name = IsAnnotated.class.getCanonicalName();
247     TypeElement element = compilation.getElements().getTypeElement(name);
248     AnnotationSpec.Builder builder = AnnotationSpec.get(element.getAnnotationMirrors().get(0))
249         .toBuilder();
250     builder.addMember("m", "$L", 123);
251     assertThat(builder.build().toString()).isEqualTo(
252         "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation("
253             + "o = com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES"
254             + ", p = 1701"
255             + ", f = 11.1"
256             + ", m = {9, 8, 1, 123}"
257             + ", l = java.lang.Override.class"
258             + ", j = @com.squareup.javapoet.AnnotationSpecTest.AnnotationA"
259             + ", q = @com.squareup.javapoet.AnnotationSpecTest.AnnotationC(\"bar\")"
260             + ", r = {java.lang.Float.class, java.lang.Double.class}"
261             + ")");
262   }
263 
reflectAnnotation()264   @Test public void reflectAnnotation() {
265     HasDefaultsAnnotation annotation = IsAnnotated.class.getAnnotation(HasDefaultsAnnotation.class);
266     AnnotationSpec spec = AnnotationSpec.get(annotation);
267     TypeSpec taco = TypeSpec.classBuilder("Taco")
268         .addAnnotation(spec)
269         .build();
270     assertThat(toString(taco)).isEqualTo(""
271         + "package com.squareup.tacos;\n"
272         + "\n"
273         + "import com.squareup.javapoet.AnnotationSpecTest;\n"
274         + "import java.lang.Double;\n"
275         + "import java.lang.Float;\n"
276         + "import java.lang.Override;\n"
277         + "\n"
278         + "@AnnotationSpecTest.HasDefaultsAnnotation(\n"
279         + "    f = 11.1,\n"
280         + "    l = Override.class,\n"
281         + "    m = {\n"
282         + "        9,\n"
283         + "        8,\n"
284         + "        1\n"
285         + "    },\n"
286         + "    o = AnnotationSpecTest.Breakfast.PANCAKES,\n"
287         + "    p = 1701,\n"
288         + "    q = @AnnotationSpecTest.AnnotationC(\"bar\"),\n"
289         + "    r = {\n"
290         + "        Float.class,\n"
291         + "        Double.class\n"
292         + "    }\n"
293         + ")\n"
294         + "class Taco {\n"
295         + "}\n");
296   }
297 
reflectAnnotationWithDefaults()298   @Test public void reflectAnnotationWithDefaults() {
299     HasDefaultsAnnotation annotation = IsAnnotated.class.getAnnotation(HasDefaultsAnnotation.class);
300     AnnotationSpec spec = AnnotationSpec.get(annotation, true);
301     TypeSpec taco = TypeSpec.classBuilder("Taco")
302         .addAnnotation(spec)
303         .build();
304     assertThat(toString(taco)).isEqualTo(""
305         + "package com.squareup.tacos;\n"
306         + "\n"
307         + "import com.squareup.javapoet.AnnotationSpecTest;\n"
308         + "import java.lang.Double;\n"
309         + "import java.lang.Float;\n"
310         + "import java.lang.Override;\n"
311         + "\n"
312         + "@AnnotationSpecTest.HasDefaultsAnnotation(\n"
313         + "    a = 5,\n"
314         + "    b = 6,\n"
315         + "    c = 7,\n"
316         + "    d = 8,\n"
317         + "    e = 9.0f,\n"
318         + "    f = 11.1,\n"
319         + "    g = {\n"
320         + "        '\\u0000',\n"
321         + "        '쫾',\n"
322         + "        'z',\n"
323         + "        '€',\n"
324         + "        'ℕ',\n"
325         + "        '\"',\n"
326         + "        '\\'',\n"
327         + "        '\\t',\n"
328         + "        '\\n'\n"
329         + "    },\n"
330         + "    h = true,\n"
331         + "    i = AnnotationSpecTest.Breakfast.WAFFLES,\n"
332         + "    j = @AnnotationSpecTest.AnnotationA,\n"
333         + "    k = \"maple\",\n"
334         + "    l = Override.class,\n"
335         + "    m = {\n"
336         + "        9,\n"
337         + "        8,\n"
338         + "        1\n"
339         + "    },\n"
340         + "    n = {\n"
341         + "        AnnotationSpecTest.Breakfast.WAFFLES,\n"
342         + "        AnnotationSpecTest.Breakfast.PANCAKES\n"
343         + "    },\n"
344         + "    o = AnnotationSpecTest.Breakfast.PANCAKES,\n"
345         + "    p = 1701,\n"
346         + "    q = @AnnotationSpecTest.AnnotationC(\"bar\"),\n"
347         + "    r = {\n"
348         + "        Float.class,\n"
349         + "        Double.class\n"
350         + "    }\n"
351         + ")\n"
352         + "class Taco {\n"
353         + "}\n");
354   }
355 
disallowsNullMemberName()356   @Test public void disallowsNullMemberName() {
357     AnnotationSpec.Builder builder = AnnotationSpec.builder(HasDefaultsAnnotation.class);
358     try {
359       AnnotationSpec.Builder $L = builder.addMember(null, "$L", "");
360       fail($L.build().toString());
361     } catch (NullPointerException e) {
362       assertThat(e).hasMessageThat().isEqualTo("name == null");
363     }
364   }
365 
requiresValidMemberName()366   @Test public void requiresValidMemberName() {
367     AnnotationSpec.Builder builder = AnnotationSpec.builder(HasDefaultsAnnotation.class);
368     try {
369       AnnotationSpec.Builder $L = builder.addMember("@", "$L", "");
370       fail($L.build().toString());
371     } catch (IllegalArgumentException e) {
372       assertThat(e).hasMessageThat().isEqualTo("not a valid name: @");
373     }
374   }
375 
modifyMembers()376   @Test public void modifyMembers() {
377     AnnotationSpec.Builder builder = AnnotationSpec.builder(SuppressWarnings.class)
378             .addMember("value", "$S", "Foo");
379 
380     builder.members.clear();
381     builder.members.put("value", Arrays.asList(CodeBlock.of("$S", "Bar")));
382 
383     assertThat(builder.build().toString()).isEqualTo("@java.lang.SuppressWarnings(\"Bar\")");
384   }
385 
toString(TypeSpec typeSpec)386   private String toString(TypeSpec typeSpec) {
387     return JavaFile.builder("com.squareup.tacos", typeSpec).build().toString();
388   }
389 }
390