1 /*
   2  * Copyright (c) 2017, 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     8074977
  27  * @summary Test consistency of annotations on constructor parameters
  28  * @compile             TestConstructorParameterAnnotations.java
  29  * @run main            TestConstructorParameterAnnotations
  30  * @compile -parameters TestConstructorParameterAnnotations.java
  31  * @run main            TestConstructorParameterAnnotations
  32  */
  33 
  34 import java.lang.annotation.*;
  35 import java.lang.reflect.*;
  36 import java.util.*;
  37 
  38 /*
  39  * Some constructor parameters are <em>mandated</em>; that is, they
  40  * are not explicitly present in the source code, but required to be
  41  * present by the Java Language Specification. In other cases, some
  42  * constructor parameters are not present in the source, but are
  43  * synthesized by the compiler as an implementation artifact. There is
  44  * not a reliable mechanism to consistently determine whether or not
  45  * a parameter is implicit or not.
  46  *
  47  * (Using the "-parameters" option to javac does emit the information
  48  * needed to make a reliably determination, but the information is not
  49  * present by default.)
  50  *
  51  * The lack of such a mechanism causes complications reading parameter
  52  * annotations in some cases since annotations for parameters are
  53  * written out for the parameters in the source code, but when reading
  54  * annotations at runtime all the parameters, including implicit ones,
  55  * are present.
  56  */
  57 public class TestConstructorParameterAnnotations {
  58     public static void main(String... args) {
  59         int errors = 0;
  60         Class<?>[] classes = {NestedClass0.class,
  61                               NestedClass1.class,
  62                               NestedClass2.class,
  63                               NestedClass3.class,
  64                               NestedClass4.class,
  65                               StaticNestedClass0.class,
  66                               StaticNestedClass1.class,
  67                               StaticNestedClass2.class, 
  68                               StaticNestedClass3.class,
  69                               StaticNestedClass4.class,
  70                               EnumClass.class};
  71 
  72         for (Class<?> clazz : classes) {
  73             for (Constructor<?> ctor : clazz.getConstructors()) {
  74                 System.out.println(ctor);
  75                 errors += checkGetParameterAnnotations(clazz, ctor);
  76                 errors += checkGetParametersGetAnnotation(clazz, ctor);
  77             }
  78         }
  79 
  80         if (errors > 0)
  81             throw new RuntimeException(errors + " errors.");
  82         return;
  83     }
  84 
  85     private static int checkGetParameterAnnotations(Class<?> clazz,
  86                                                     Constructor<?> ctor) {
  87         String annotationString =
  88             Arrays.deepToString(ctor.getParameterAnnotations());
  89         String expectedString =
  90             clazz.getAnnotation(ExpectedGetParameterAnnotations.class).value();
  91         
  92         if (!Objects.equals(annotationString, expectedString)) {
  93             System.err.println("Annotation mismatch on " + ctor +
  94                                "\n\tExpected:" + expectedString +
  95                                "\n\tActual:  " + annotationString);
  96             return 1;
  97         }
  98         return 0;
  99     }
 100 
 101     private static int checkGetParametersGetAnnotation(Class<?> clazz,
 102                                                        Constructor<?> ctor) {
 103         int errors = 0;
 104         int i = 0;
 105         ExpectedParameterAnnotations epa =
 106             clazz.getAnnotation(ExpectedParameterAnnotations.class);
 107 
 108         for (Parameter param : ctor.getParameters() ) {
 109             String annotationString =
 110                 Objects.toString(param.getAnnotation(MarkerAnnotation.class));
 111             String expectedString = epa.value()[i];
 112 
 113             if (!Objects.equals(annotationString, expectedString)) {
 114                 System.err.println("Annotation mismatch on " + ctor +
 115                                    " on param " + param +
 116                                    "\n\tExpected:" + expectedString +
 117                                    "\n\tActual:  " + annotationString);
 118                 errors++;
 119             }
 120             i++;
 121         }
 122         return errors;
 123     }
 124 
 125     @ExpectedGetParameterAnnotations("[[]]")
 126     @ExpectedParameterAnnotations({"null"})
 127     public class NestedClass0 {
 128         public NestedClass0() {}
 129     }
 130 
 131     @ExpectedGetParameterAnnotations(
 132         "[[], " +
 133         "[@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)]]")
 134     @ExpectedParameterAnnotations({
 135         "null",
 136         "@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)"})
 137     public class NestedClass1 {
 138         public NestedClass1(@MarkerAnnotation(1) int parameter) {}
 139     }
 140 
 141     @ExpectedGetParameterAnnotations(
 142         "[[], " +
 143         "[@TestConstructorParameterAnnotations$MarkerAnnotation(value=2)], " +
 144         "[]]")
 145     @ExpectedParameterAnnotations({
 146         "null",
 147         "@TestConstructorParameterAnnotations$MarkerAnnotation(value=2)",
 148         "null"})
 149     public class NestedClass2 {
 150         public NestedClass2(@MarkerAnnotation(2) int parameter1,
 151                             int parameter2) {}
 152     }
 153 
 154     @ExpectedGetParameterAnnotations(
 155         "[[], " +
 156         "[@TestConstructorParameterAnnotations$MarkerAnnotation(value=3)], " +
 157         "[]]")
 158     @ExpectedParameterAnnotations({
 159         "null",
 160         "@TestConstructorParameterAnnotations$MarkerAnnotation(value=3)",
 161             "null"})
 162     public class NestedClass3 {
 163         public <P> NestedClass3(@MarkerAnnotation(3) P parameter1,
 164                                 int parameter2) {}
 165     }
 166 
 167     @ExpectedGetParameterAnnotations(
 168         "[[], " +
 169         "[@TestConstructorParameterAnnotations$MarkerAnnotation(value=4)], " +
 170         "[]]")
 171     @ExpectedParameterAnnotations({
 172         "null",
 173         "@TestConstructorParameterAnnotations$MarkerAnnotation(value=4)",
 174         "null"})
 175     public class NestedClass4 {
 176         public <P, Q> NestedClass4(@MarkerAnnotation(4) P parameter1,
 177                                    Q parameter2) {}
 178     }
 179 
 180     @ExpectedGetParameterAnnotations("[]")
 181     @ExpectedParameterAnnotations({"null"})
 182     public static class StaticNestedClass0 {
 183         public StaticNestedClass0() {}
 184     }
 185 
 186     @ExpectedGetParameterAnnotations(
 187         "[[@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)]]")
 188     @ExpectedParameterAnnotations({
 189         "@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)"})
 190     public static class StaticNestedClass1 {
 191         public StaticNestedClass1(@MarkerAnnotation(1) int parameter) {}
 192     }
 193 
 194     @ExpectedGetParameterAnnotations(
 195         "[[@TestConstructorParameterAnnotations$MarkerAnnotation(value=2)], " +
 196         "[]]")
 197     @ExpectedParameterAnnotations({
 198         "@TestConstructorParameterAnnotations$MarkerAnnotation(value=2)",
 199         "null"})
 200     public static class StaticNestedClass2 {
 201         public StaticNestedClass2(@MarkerAnnotation(2) int parameter1,
 202                             int parameter2) {}
 203     }
 204 
 205     @ExpectedGetParameterAnnotations(
 206         "[[@TestConstructorParameterAnnotations$MarkerAnnotation(value=3)], " +
 207         "[]]")
 208     @ExpectedParameterAnnotations({
 209         "@TestConstructorParameterAnnotations$MarkerAnnotation(value=3)",
 210         "null"})
 211     public static class StaticNestedClass3 {
 212         public <P> StaticNestedClass3(@MarkerAnnotation(3) P parameter1,
 213                                       int parameter2) {}
 214     }
 215 
 216     @ExpectedGetParameterAnnotations(
 217         "[[@TestConstructorParameterAnnotations$MarkerAnnotation(value=4)], " +
 218         "[]]")
 219     @ExpectedParameterAnnotations({
 220         "@TestConstructorParameterAnnotations$MarkerAnnotation(value=4)",
 221         "null"})
 222     public static class StaticNestedClass4 {
 223         public <P, Q> StaticNestedClass4(@MarkerAnnotation(4) P parameter1,
 224                                          Q parameter2) {}
 225     }
 226 
 227     @ExpectedGetParameterAnnotations(
 228         "[[], [], " +
 229         "[@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)]]")
 230     @ExpectedParameterAnnotations({
 231         "null",
 232         "null",
 233         "@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)"})
 234     public enum EnumClass {
 235         CONSTANT1;
 236         EnumClass(@MarkerAnnotation(1) int parameter1) {}
 237     }
 238 
 239     @Target(ElementType.PARAMETER)
 240     @Retention(RetentionPolicy.RUNTIME)
 241     @interface MarkerAnnotation {
 242         int value();
 243     }
 244 
 245     /**
 246      * String form of expected value of calling
 247      * getParameterAnnotations on a constructor.
 248      */
 249     @Target(ElementType.TYPE)
 250     @Retention(RetentionPolicy.RUNTIME)
 251     @interface ExpectedGetParameterAnnotations {
 252         String value();
 253     }
 254 
 255     /**
 256      * String form of expected value of calling
 257      * getAnnotation(MarkerAnnotation.class) on each element of the
 258      * result of getParameters() on a constructor.
 259      */
 260     @Target(ElementType.TYPE)
 261     @Retention(RetentionPolicy.RUNTIME)
 262     @interface ExpectedParameterAnnotations {
 263         String[] value();
 264     }
 265 }