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