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             TestConstructorParameterTypeAnnotations.java
  29  * @run main            TestConstructorParameterTypeAnnotations
  30  * @compile -parameters TestConstructorParameterTypeAnnotations.java
  31  * @run main            TestConstructorParameterTypeAnnotations
  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 TestConstructorParameterTypeAnnotations {
  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 
  69         for (Class<?> clazz : classes) {
  70             for (Constructor<?> ctor : clazz.getConstructors()) {
  71                 System.out.println(ctor);
  72                 errors += checkGetParameterAnnotations(clazz, ctor);
  73                 errors += checkGetAnnotatedParametersGetAnnotation(clazz, ctor);
  74             }
  75         }
  76 
  77         if (errors > 0)
  78             throw new RuntimeException(errors + " errors.");
  79         return;
  80     }
  81 
  82     private static int checkGetParameterAnnotations(Class<?> clazz,
  83                                                     Constructor<?> ctor) {
  84         String annotationString =
  85             Arrays.deepToString(ctor.getParameterAnnotations());
  86         String expectedString =
  87             clazz.getAnnotation(ExpectedGetParameterAnnotations.class).value();
  88         
  89         if (!Objects.equals(annotationString, expectedString)) {
  90             System.err.println("Annotation mismatch on " + ctor +
  91                                "\n\tExpected:" + expectedString +
  92                                "\n\tActual:  " + annotationString);
  93             return 1;
  94         }
  95         return 0;
  96     }
  97 
  98     private static int checkGetAnnotatedParametersGetAnnotation(Class<?> clazz,
  99                                                        Constructor<?> ctor) {
 100         int errors = 0;
 101         int i = 0;
 102         ExpectedParameterTypeAnnotations epa =
 103             clazz.getAnnotation(ExpectedParameterTypeAnnotations.class);
 104 
 105         for (AnnotatedType param : ctor.getAnnotatedParameterTypes() ) {
 106             String annotationString =
 107                 Objects.toString(param.getAnnotation(MarkerTypeAnnotation.class));
 108             String expectedString = epa.value()[i];
 109 
 110             if (!Objects.equals(annotationString, expectedString)) {
 111                 System.err.println("Annotation mismatch on " + ctor +
 112                                    " on param " + param +
 113                                    "\n\tExpected:" + expectedString +
 114                                    "\n\tActual:  " + annotationString);
 115                 errors++;
 116             }
 117             i++;
 118         }
 119         return errors;
 120     }
 121 
 122     @ExpectedGetParameterAnnotations("[[]]")
 123     @ExpectedParameterTypeAnnotations({"null"})
 124     public class NestedClass0 {
 125         public NestedClass0() {}
 126     }
 127 
 128     @ExpectedGetParameterAnnotations("[[], []]")
 129     @ExpectedParameterTypeAnnotations({
 130         "null",
 131         "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=1)"})
 132     public class NestedClass1 {
 133         public NestedClass1(@MarkerTypeAnnotation(1) int parameter) {}
 134     }
 135 
 136     @ExpectedGetParameterAnnotations("[[], [], []]")
 137     @ExpectedParameterTypeAnnotations({
 138         "null",
 139         "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=2)",
 140         "null"})
 141     public class NestedClass2 {
 142         public NestedClass2(@MarkerTypeAnnotation(2) int parameter1,
 143                             int parameter2) {}
 144     }
 145 
 146     @ExpectedGetParameterAnnotations("[[], [], []]")
 147     @ExpectedParameterTypeAnnotations({
 148         "null",
 149         "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=3)",
 150         "null"})
 151     public class NestedClass3 {
 152         public <P> NestedClass3(@MarkerTypeAnnotation(3) P parameter1,
 153                                 int parameter2) {}
 154     }
 155 
 156     @ExpectedGetParameterAnnotations("[[], [], []]")
 157     @ExpectedParameterTypeAnnotations({
 158         "null",
 159         "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=4)",
 160         "null"})
 161     public class NestedClass4 {
 162         public <P, Q> NestedClass4(@MarkerTypeAnnotation(4) P parameter1,
 163                                    Q parameter2) {}
 164     }
 165 
 166     @ExpectedGetParameterAnnotations("[]")
 167     @ExpectedParameterTypeAnnotations({"null"})
 168     public static class StaticNestedClass0 {
 169         public StaticNestedClass0() {}
 170     }
 171 
 172     @ExpectedGetParameterAnnotations("[[]]")
 173     @ExpectedParameterTypeAnnotations({
 174         "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=1)"})
 175     public static class StaticNestedClass1 {
 176         public StaticNestedClass1(@MarkerTypeAnnotation(1) int parameter) {}
 177     }
 178 
 179     @ExpectedGetParameterAnnotations("[[], []]")
 180     @ExpectedParameterTypeAnnotations({
 181         "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=2)",
 182         "null"})
 183     public static class StaticNestedClass2 {
 184         public StaticNestedClass2(@MarkerTypeAnnotation(2) int parameter1,
 185                                   int parameter2) {}
 186     }
 187 
 188     @ExpectedGetParameterAnnotations("[[], []]")
 189     @ExpectedParameterTypeAnnotations({
 190         "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=3)",
 191         "null"})
 192     public static class StaticNestedClass3 {
 193          public <P> StaticNestedClass3(@MarkerTypeAnnotation(3) P parameter1,
 194                                       int parameter2) {}
 195     }
 196 
 197     @ExpectedGetParameterAnnotations("[[], []]")
 198     @ExpectedParameterTypeAnnotations({
 199         "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=4)",
 200         "null"})
 201     public static class StaticNestedClass4 {
 202         public <P, Q> StaticNestedClass4(@MarkerTypeAnnotation(4) P parameter1,
 203                                          Q parameter2) {}
 204     }
 205 
 206     @Target(ElementType.TYPE_USE)
 207     @Retention(RetentionPolicy.RUNTIME)
 208     @interface MarkerTypeAnnotation {
 209         int value();
 210     }
 211 
 212     /**
 213      * String form of expected value of calling
 214      * getParameterAnnotations on a constructor.
 215      */
 216     @Target(ElementType.TYPE)
 217     @Retention(RetentionPolicy.RUNTIME)
 218     @interface ExpectedGetParameterAnnotations {
 219         String value();
 220     }
 221 
 222     /**
 223      * String form of expected value of calling
 224      * getAnnotation(MarkerTypeAnnotation.class) on each element of the
 225      * result of getParameters() on a constructor.
 226      */
 227     @Target(ElementType.TYPE)
 228     @Retention(RetentionPolicy.RUNTIME)
 229     @interface ExpectedParameterTypeAnnotations {
 230         String[] value();
 231     }
 232 }