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         "b0=(byte)0x01, " +
 135         "i0=1, " +
 136         "i1=2, " +
 137         "f0=1.0f, " +
 138         "f1=0.0f/0.0f, " +
 139         "d0=0.0, " +
 140         "d1=1.0/0.0, " +
 141         "l0=5L, " +
 142         "l1=9223372036854775807L, " +
 143         "l2=-9223372036854775808L, " +
 144         "l3=-2147483648L, " +
 145         "s0=\"Hello world.\", " +
 146         "s1=\"a\\\"b\", " +
 147         "class0=Obj[].class, " +
 148         "classArray={Obj[].class})")
 149     @MostlyPrimitive(
 150         c0='a',
 151         c1='\'',
 152         b0=1,
 153         i0=1,
 154         i1=2,
 155         f0=1.0f,
 156         f1=Float.NaN,
 157         d0=0.0,
 158         d1=2.0/0.0,
 159         l0=5,
 160         l1=Long.MAX_VALUE,
 161         l2=Long.MIN_VALUE,
 162         l3=Integer.MIN_VALUE,
 163         s0="Hello world.",
 164         s1="a\"b",
 165         class0=Obj[].class,
 166         classArray={Obj[].class}
 167     )
 168     static class PrimHost{}
 169 
 170     private int classyTest() {
 171         int failures = 0;
 172 
 173         TypeElement annotationHostElt =
 174             Objects.requireNonNull(elements.getTypeElement("AnnotationToStringTest.AnnotationHost"));
 175 
 176         for (VariableElement f : ElementFilter.fieldsIn(annotationHostElt.getEnclosedElements())) {
 177             String expected = f.getAnnotation(ExpectedString.class).value();
 178             Annotation a = f.getAnnotation(Classy.class);
 179 
 180             System.out.println(a);
 181             failures += check(expected, a.toString());
 182 
 183             failures += check(expected,
 184                               retrieveAnnotationMirrorAsString(f, "Classy") );
 185         }
 186         return failures;
 187     }
 188 
 189     static class AnnotationHost {
 190         @ExpectedString(
 191        "@Classy(Obj.class)")
 192         @Classy(Obj.class)
 193         public int f0;
 194 
 195         @ExpectedString(
 196        "@Classy(Obj[].class)")
 197         @Classy(Obj[].class)
 198         public int f1;
 199 
 200         @ExpectedString(
 201        "@Classy(Obj[][].class)")
 202         @Classy(Obj[][].class)
 203         public int f2;
 204 
 205         @ExpectedString(
 206        "@Classy(Obj[][][].class)")
 207         @Classy(Obj[][][].class)
 208         public int f3;
 209 
 210         @ExpectedString(
 211        "@Classy(int.class)")
 212         @Classy(int.class)
 213         public int f4;
 214 
 215         @ExpectedString(
 216        "@Classy(int[][][].class)")
 217         @Classy(int[][][].class)
 218         public int f5;
 219     }
 220 
 221     /**
 222      * Each field should have two annotations, the first being
 223      * @ExpectedString and the second the annotation under test.
 224      */
 225     private int arrayAnnotationTest() {
 226         int failures = 0;
 227 
 228         TypeElement arrayAnnotationHostElt =
 229             Objects.requireNonNull(elements
 230                                    .getTypeElement("AnnotationToStringTest.ArrayAnnotationHost"));
 231 
 232         for (VariableElement f :
 233                  ElementFilter.fieldsIn(arrayAnnotationHostElt.getEnclosedElements())) {
 234             var annotations = f.getAnnotationMirrors();
 235             // String expected = retrieveAnnotationMirrorValue(f, "ExpectedString");
 236             String expected = f.getAnnotation(ExpectedString.class).value();
 237 
 238             // Problem with 
 239             // Need a de-quote method...
 240             // expected = expected.substring(1, expected.length() - 1);
 241 
 242               failures +=
 243                   check(expected,
 244                         annotations.get(1).toString());
 245 
 246             // Get the array-valued annotation as an annotation
 247               failures +=
 248                   check(expected,
 249                         retrieveAnnotationMirrorAsString(f,
 250                                                          annotations.get(1)
 251                                                          .getAnnotationType().toString()));
 252         }
 253         return failures;
 254     }
 255 
 256     static class ArrayAnnotationHost {
 257         @ExpectedString(
 258        "@BooleanArray({true, false, true})")
 259         @BooleanArray({true, false, true})
 260         public boolean[]   f0;
 261 
 262         @ExpectedString(
 263        "@FloatArray({3.0f, 4.0f, 0.0f/0.0f, -1.0f/0.0f, 1.0f/0.0f})")
 264         @FloatArray({3.0f, 4.0f, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY})
 265         public float[]     f1;
 266 
 267         @ExpectedString(
 268        "@DoubleArray({1.0, 2.0, 0.0/0.0, 1.0/0.0, -1.0/0.0})")
 269         @DoubleArray({1.0, 2.0, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,})
 270         public double[]    f2;
 271 
 272 
 273         @ExpectedString(
 274        "@ByteArray({(byte)0x0a, (byte)0x0b, (byte)0x0c})")
 275         @ByteArray({10, 11, 12})
 276         public byte[]      f3;
 277 
 278         @ExpectedString(
 279        "@ShortArray({0, 4, 5})")
 280         @ShortArray({0, 4, 5})
 281         public short[]     f4;
 282 
 283         @ExpectedString(
 284        "@CharArray({'a', 'b', 'c', '\\''})")
 285         @CharArray({'a', 'b', 'c', '\''})
 286         public char[]      f5;
 287 
 288         @ExpectedString(
 289        "@IntArray({1})")
 290         @IntArray({1})
 291         public int[]       f6;
 292 
 293         @ExpectedString(
 294        "@LongArray({-9223372036854775808L, -2147483649L, -2147483648L," +
 295                 " -2147483647L, 2147483648L, 9223372036854775807L})")
 296         @LongArray({Long.MIN_VALUE, Integer.MIN_VALUE-1L, Integer.MIN_VALUE,
 297                 -Integer.MAX_VALUE, Integer.MAX_VALUE+1L, Long.MAX_VALUE})
 298         public long[]      f7;
 299 
 300         @ExpectedString(
 301        "@StringArray({\"A\", \"B\", \"C\", \"\\\"Quote\\\"\"})")
 302         @StringArray({"A", "B", "C", "\"Quote\""})
 303         public String[]    f8;
 304 
 305         @ExpectedString(
 306        "@ClassArray({int.class, Obj[].class})")
 307         @ClassArray({int.class, Obj[].class})
 308         public Class<?>[]  f9;
 309 
 310         @ExpectedString(
 311        "@EnumArray({SOURCE})")
 312         @EnumArray({RetentionPolicy.SOURCE})
 313         public RetentionPolicy[]  f10;
 314     }
 315 }
 316 
 317 // ------------ Supporting types ------------
 318 
 319 class Obj {}
 320 
 321 @Retention(RetentionPolicy.RUNTIME)
 322 @interface ExpectedString {
 323     String value();
 324 }
 325 
 326 @Retention(RetentionPolicy.RUNTIME)
 327 @interface Classy {
 328     Class<?> value();
 329 }
 330 
 331 @Retention(RetentionPolicy.RUNTIME)
 332 @interface BooleanArray {
 333     boolean[] value();
 334 }
 335 
 336 @Retention(RetentionPolicy.RUNTIME)
 337 @interface FloatArray {
 338     float[] value();
 339 }
 340 
 341 @Retention(RetentionPolicy.RUNTIME)
 342 @interface DoubleArray {
 343     double[] value();
 344 }
 345 
 346 @Retention(RetentionPolicy.RUNTIME)
 347 @interface ByteArray {
 348     byte[] value();
 349 }
 350 
 351 @Retention(RetentionPolicy.RUNTIME)
 352 @interface ShortArray {
 353     short[] value();
 354 }
 355 
 356 @Retention(RetentionPolicy.RUNTIME)
 357 @interface CharArray {
 358     char[] value();
 359 }
 360 
 361 @Retention(RetentionPolicy.RUNTIME)
 362 @interface IntArray {
 363     int[] value();
 364 }
 365 
 366 @Retention(RetentionPolicy.RUNTIME)
 367 @interface LongArray {
 368     long[] value();
 369 }
 370 
 371 @Retention(RetentionPolicy.RUNTIME)
 372 @interface ClassArray {
 373     Class<?>[] value() default {int.class, Obj[].class};
 374 }
 375 
 376 @Retention(RetentionPolicy.RUNTIME)
 377 @interface StringArray {
 378     String[] value();
 379 }
 380 
 381 @Retention(RetentionPolicy.RUNTIME)
 382 @interface EnumArray {
 383     RetentionPolicy[] value();
 384 }
 385 
 386 @Retention(RetentionPolicy.RUNTIME)
 387 @interface MostlyPrimitive {
 388     char   c0();
 389     char   c1();
 390     byte   b0();
 391     int    i0();
 392     int    i1();
 393     float  f0();
 394     float  f1();
 395     double d0();
 396     double d1();
 397     long   l0();
 398     long   l1();
 399     long   l2();
 400     long   l3();
 401     String s0();
 402     String s1();
 403     Class<?> class0();
 404     Class<?>[] classArray();
 405 }