1 /* 2 * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8164819 27 * @summary Test of toString on normal annotations 28 * @library /tools/javac/lib 29 * @build JavacTestingAbstractProcessor AnnotationToStringTest 30 * @compile -processor AnnotationToStringTest -proc:only AnnotationToStringTest.java 31 */ 32 33 // See also the sibling core reflection test 34 // test/jdk/java/lang/annotation/AnnotationToStringTest.java 35 36 import java.lang.annotation.*; 37 import java.lang.reflect.*; 38 import java.util.*; 39 import javax.annotation.processing.*; 40 import javax.lang.model.AnnotatedConstruct; 41 import javax.lang.model.element.*; 42 import javax.lang.model.util.*; 43 44 /** 45 * The expected string values are stored in @ExpectedString 46 * annotations. The essence of the test is comparing the toString() 47 * result of annotations to the corresponding ExpectedString.value(). 48 * 49 * Two flavors of comparison are made: 50 * 51 * 1) Against the AnnotationMirror value from getAnnotationMirrors() 52 * 53 * 2) Against the *Annotation* from getAnnotation(Class<A>) 54 * 55 * These have separate but related implementations. 56 */ 57 public class AnnotationToStringTest extends JavacTestingAbstractProcessor { 58 public boolean process(Set<? extends TypeElement> annotations, 59 RoundEnvironment roundEnv) { 60 if (!roundEnv.processingOver()) { 61 62 int failures = 0; 63 64 TypeElement primHostElt = 65 Objects.requireNonNull(elements.getTypeElement("AnnotationToStringTest.PrimHost")); 66 67 List<? extends AnnotationMirror> annotMirrors = primHostElt.getAnnotationMirrors(); 68 69 String expectedString = primHostElt.getAnnotation(MostlyPrimitive.class).toString(); 70 71 failures += check(expectedString, 72 primHostElt.getAnnotation(ExpectedString.class).value()); 73 74 failures += check(expectedString, 75 retrieveAnnotationMirrorAsString(primHostElt, 76 "MostlyPrimitive")); 77 failures += classyTest(); 78 failures += arrayAnnotationTest(); 79 80 if (failures > 0) 81 throw new RuntimeException(failures + " failures"); 82 } 83 return true; 84 } 85 86 /** 87 * Examine annotation mirrors, find the one that matches 88 * annotationName, and return its toString value. 89 */ 90 private String retrieveAnnotationMirrorAsString(AnnotatedConstruct annotated, 91 String annotationName) { 92 return retrieveAnnotationMirror(annotated, annotationName).toString(); 93 } 94 95 private String retrieveAnnotationMirrorValue(AnnotatedConstruct annotated, 96 String annotationName) { 97 AnnotationMirror annotationMirror = 98 retrieveAnnotationMirror(annotated, annotationName); 99 for (var entry : annotationMirror.getElementValues().entrySet()) { 100 if (entry.getKey().getSimpleName().contentEquals("value")) { 101 return entry.getValue().toString(); 102 } 103 } 104 throw new RuntimeException("Annotation value() method not found: " + 105 annotationMirror.toString()); 106 } 107 108 private AnnotationMirror retrieveAnnotationMirror(AnnotatedConstruct annotated, 109 String annotationName) { 110 for (AnnotationMirror annotationMirror : annotated.getAnnotationMirrors()) { 111 System.out.println(annotationMirror.getAnnotationType()); 112 if (annotationMirror 113 .getAnnotationType() 114 .toString() 115 .equals(annotationName) ) { 116 return annotationMirror; 117 } 118 } 119 throw new RuntimeException("Annotation " + annotationName + " not found."); 120 } 121 122 private static int check(String expected, String actual) { 123 if (!expected.equals(actual)) { 124 System.err.printf("ERROR: Expected ''%s'';%ngot ''%s''.\n", 125 expected, actual); 126 return 1; 127 } else 128 return 0; 129 } 130 131 @ExpectedString( 132 "@MostlyPrimitive(c0='a', "+ 133 "c1='\\'', " + 134 "i0=1, " + 135 "i1=2, " + 136 "f0=1.0f, " + 137 "f1=0.0f/0.0f, " + 138 "d0=0.0, " + 139 "d1=1.0/0.0, " + 140 "l0=5L, " + 141 "l1=9223372036854775807L, " + 142 "l2=-9223372036854775808L, " + 143 "l3=-2147483648L, " + 144 "s0=\"Hello world.\", " + 145 "s1=\"a\\\"b\", " + 146 "class0=Obj[].class, " + 147 "classArray={Obj[].class})") 148 @MostlyPrimitive( 149 c0='a', 150 c1='\'', 151 i0=1, 152 i1=2, 153 f0=1.0f, 154 f1=Float.NaN, 155 d0=0.0, 156 d1=2.0/0.0, 157 l0=5, 158 l1=Long.MAX_VALUE, 159 l2=Long.MIN_VALUE, 160 l3=Integer.MIN_VALUE, 161 s0="Hello world.", 162 s1="a\"b", 163 class0=Obj[].class, 164 classArray={Obj[].class} 165 ) 166 static class PrimHost{} 167 168 private int classyTest() { 169 int failures = 0; 170 171 TypeElement annotationHostElt = 172 Objects.requireNonNull(elements.getTypeElement("AnnotationToStringTest.AnnotationHost")); 173 174 for (VariableElement f : ElementFilter.fieldsIn(annotationHostElt.getEnclosedElements())) { 175 String expected = f.getAnnotation(ExpectedString.class).value(); 176 Annotation a = f.getAnnotation(Classy.class); 177 178 System.out.println(a); 179 failures += check(expected, a.toString()); 180 181 // The toString of an AnnotationMirror does a better job 182 // of eliding "value=" when not needed then the toString 183 // from an Annotation. Strip "value=" from the expected 184 // string to allow the comparison to proceed successfully. 185 failures += check(expected.replace("value=", ""), 186 retrieveAnnotationMirrorAsString(f, "Classy") ); 187 } 188 return failures; 189 } 190 191 static class AnnotationHost { 192 @ExpectedString( 193 "@Classy(value=Obj.class)") 194 @Classy(value=Obj.class) 195 public int f0; 196 197 @ExpectedString( 198 "@Classy(value=Obj[].class)") 199 @Classy(value=Obj[].class) 200 public int f1; 201 202 @ExpectedString( 203 "@Classy(value=Obj[][].class)") 204 @Classy(value=Obj[][].class) 205 public int f2; 206 207 @ExpectedString( 208 "@Classy(value=Obj[][][].class)") 209 @Classy(value=Obj[][][].class) 210 public int f3; 211 212 @ExpectedString( 213 "@Classy(value=int.class)") 214 @Classy(value=int.class) 215 public int f4; 216 217 @ExpectedString( 218 "@Classy(value=int[][][].class)") 219 @Classy(value=int[][][].class) 220 public int f5; 221 } 222 223 /** 224 * Each field should have two annotations, the first being 225 * @ExpectedString and the second the annotation under test. 226 */ 227 private int arrayAnnotationTest() { 228 int failures = 0; 229 230 TypeElement arrayAnnotationHostElt = 231 Objects.requireNonNull(elements 232 .getTypeElement("AnnotationToStringTest.ArrayAnnotationHost")); 233 234 for (VariableElement f : 235 ElementFilter.fieldsIn(arrayAnnotationHostElt.getEnclosedElements())) { 236 var annotations = f.getAnnotationMirrors(); 237 // String expected = retrieveAnnotationMirrorValue(f, "ExpectedString"); 238 String expected = f.getAnnotation(ExpectedString.class).value(); 239 240 // Problem with 241 // Need a de-quote method... 242 // expected = expected.substring(1, expected.length() - 1); 243 244 // The toString of an AnnotationMirror does a better job 245 // of eliding "value=" when not needed then the toString 246 // from an Annotation. Strip "value=" from the expected 247 // string to allow the comparison to proceed successfully. 248 failures += 249 check(expected.replace("value=", ""), 250 annotations.get(1).toString()); 251 252 // Get the array-valued annotation as an annotation 253 failures += 254 check(expected.replace("value=", ""), 255 retrieveAnnotationMirrorAsString(f, 256 annotations.get(1) 257 .getAnnotationType().toString())); 258 } 259 return failures; 260 } 261 262 static class ArrayAnnotationHost { 263 @ExpectedString( 264 "@BooleanArray(value={true, false, true})") 265 @BooleanArray(value={true, false, true}) 266 public boolean[] f0; 267 268 @ExpectedString( 269 "@FloatArray(value={3.0f, 4.0f, 0.0f/0.0f, -1.0f/0.0f, 1.0f/0.0f})") 270 @FloatArray(value={3.0f, 4.0f, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY}) 271 public float[] f1; 272 273 @ExpectedString( 274 "@DoubleArray(value={1.0, 2.0, 0.0/0.0, 1.0/0.0, -1.0/0.0})") 275 @DoubleArray(value={1.0, 2.0, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,}) 276 public double[] f2; 277 278 279 @ExpectedString( 280 "@ByteArray(value={(byte)0x0a, (byte)0x0b, (byte)0x0c})") 281 @ByteArray(value={10, 11, 12}) 282 public byte[] f3; 283 284 @ExpectedString( 285 "@ShortArray(value={0, 4, 5})") 286 @ShortArray(value={0, 4, 5}) 287 public short[] f4; 288 289 @ExpectedString( 290 "@CharArray(value={'a', 'b', 'c', '\\''})") 291 @CharArray(value={'a', 'b', 'c', '\''}) 292 public char[] f5; 293 294 @ExpectedString( 295 "@IntArray(value={1})") 296 @IntArray(value={1}) 297 public int[] f6; 298 299 @ExpectedString( 300 "@LongArray(value={-9223372036854775808L, -2147483649L, -2147483648L," + 301 " -2147483647L, 2147483648L, 9223372036854775807L})") 302 @LongArray(value={Long.MIN_VALUE, Integer.MIN_VALUE-1L, Integer.MIN_VALUE, 303 -Integer.MAX_VALUE, Integer.MAX_VALUE+1L, Long.MAX_VALUE}) 304 public long[] f7; 305 306 @ExpectedString( 307 "@StringArray(value={\"A\", \"B\", \"C\", \"\\\"Quote\\\"\"})") 308 @StringArray(value={"A", "B", "C", "\"Quote\""}) 309 public String[] f8; 310 311 @ExpectedString( 312 "@ClassArray(value={int.class, Obj[].class})") 313 @ClassArray(value={int.class, Obj[].class}) 314 public Class<?>[] f9; 315 316 @ExpectedString( 317 "@EnumArray(value={java.lang.annotation.RetentionPolicy.SOURCE})") 318 @EnumArray(value={RetentionPolicy.SOURCE}) 319 public RetentionPolicy[] f10; 320 } 321 } 322 323 // ------------ Supporting types ------------ 324 325 class Obj {} 326 327 @Retention(RetentionPolicy.RUNTIME) 328 @interface ExpectedString { 329 String value(); 330 } 331 332 @Retention(RetentionPolicy.RUNTIME) 333 @interface Classy { 334 Class<?> value(); 335 } 336 337 @Retention(RetentionPolicy.RUNTIME) 338 @interface BooleanArray { 339 boolean[] value(); 340 } 341 342 @Retention(RetentionPolicy.RUNTIME) 343 @interface FloatArray { 344 float[] value(); 345 } 346 347 @Retention(RetentionPolicy.RUNTIME) 348 @interface DoubleArray { 349 double[] value(); 350 } 351 352 @Retention(RetentionPolicy.RUNTIME) 353 @interface ByteArray { 354 byte[] value(); 355 } 356 357 @Retention(RetentionPolicy.RUNTIME) 358 @interface ShortArray { 359 short[] value(); 360 } 361 362 @Retention(RetentionPolicy.RUNTIME) 363 @interface CharArray { 364 char[] value(); 365 } 366 367 @Retention(RetentionPolicy.RUNTIME) 368 @interface IntArray { 369 int[] value(); 370 } 371 372 @Retention(RetentionPolicy.RUNTIME) 373 @interface LongArray { 374 long[] value(); 375 } 376 377 @Retention(RetentionPolicy.RUNTIME) 378 @interface ClassArray { 379 Class<?>[] value() default {int.class, Obj[].class}; 380 } 381 382 @Retention(RetentionPolicy.RUNTIME) 383 @interface StringArray { 384 String[] value(); 385 } 386 387 @Retention(RetentionPolicy.RUNTIME) 388 @interface EnumArray { 389 RetentionPolicy[] value(); 390 } 391 392 @Retention(RetentionPolicy.RUNTIME) 393 @interface MostlyPrimitive { 394 char c0(); 395 char c1(); 396 int i0(); 397 int i1(); 398 float f0(); 399 float f1(); 400 double d0(); 401 double d1(); 402 long l0(); 403 long l1(); 404 long l2(); 405 long l3(); 406 String s0(); 407 String s1(); 408 Class<?> class0(); 409 Class<?>[] classArray(); 410 }