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                               StaticNestedClass0.class,
  64                               StaticNestedClass1.class,
  65                               StaticNestedClass2.class };
  66 
  67         for (Class<?> clazz : classes) {
  68             for (Constructor<?> ctor : clazz.getConstructors()) {
  69                 System.out.println(ctor);
  70                 errors += checkGetParameterAnnotations(clazz, ctor);
  71                 errors += checkGetAnnotatedParametersGetAnnotation(clazz, ctor);
  72             }
  73         }
  74 
  75         if (errors > 0)
  76             throw new RuntimeException(errors + " errors.");
  77         return;
  78     }
  79 
  80     private static int checkGetParameterAnnotations(Class<?> clazz,
  81                                                     Constructor<?> ctor) {
  82         String annotationString =
  83             Arrays.deepToString(ctor.getParameterAnnotations());
  84         String expectedString =
  85             clazz.getAnnotation(ExpectedGetParameterAnnotations.class).value();
  86         
  87         if (!Objects.equals(annotationString, expectedString)) {
  88             System.err.println("Annotation mismatch on " + ctor +
  89                                "\n\tExpected:" + expectedString +
  90                                "\n\tActual:  " + annotationString);
  91             return 1;
  92         }
  93         return 0;
  94     }
  95 
  96     private static int checkGetAnnotatedParametersGetAnnotation(Class<?> clazz,
  97                                                        Constructor<?> ctor) {
  98         int errors = 0;
  99         int i = 0;
 100         ExpectedParameterTypeAnnotations epa =
 101             clazz.getAnnotation(ExpectedParameterTypeAnnotations.class);
 102 
 103         for (AnnotatedType param : ctor.getAnnotatedParameterTypes() ) {
 104             String annotationString =
 105                 Objects.toString(param.getAnnotation(MarkerTypeAnnotation.class));
 106             String expectedString = epa.value()[i];
 107 
 108             if (!Objects.equals(annotationString, expectedString)) {
 109                 System.err.println("Annotation mismatch on " + ctor +
 110                                    " on param " + param +
 111                                    "\n\tExpected:" + expectedString +
 112                                    "\n\tActual:  " + annotationString);
 113                 errors++;
 114             }
 115             i++;
 116         }
 117         return errors;
 118     }
 119 
 120     @ExpectedGetParameterAnnotations("[[]]")
 121     @ExpectedParameterTypeAnnotations({"null"})
 122     public class NestedClass0 {
 123         public NestedClass0() {}
 124     }
 125 
 126     @ExpectedGetParameterAnnotations(
 127         "[[], " +
 128         "[]]")
 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         "[[], " +
 138         "[], " +
 139         "[]]")
 140     @ExpectedParameterTypeAnnotations({
 141         "null",
 142         "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=2)",
 143         "null"})
 144     public class NestedClass2 {
 145         public NestedClass2(@MarkerTypeAnnotation(2) int parameter1,
 146                             int parameter2) {}
 147     }
 148 
 149     @ExpectedGetParameterAnnotations("[]")
 150     @ExpectedParameterTypeAnnotations({"null"})
 151     public static class StaticNestedClass0 {
 152         public StaticNestedClass0() {}
 153     }
 154 
 155     @ExpectedGetParameterAnnotations(
 156         "[[]]")
 157     @ExpectedParameterTypeAnnotations({
 158         "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=1)"})
 159     public static class StaticNestedClass1 {
 160         public StaticNestedClass1(@MarkerTypeAnnotation(1) int parameter) {}
 161     }
 162 
 163     @ExpectedGetParameterAnnotations(
 164         "[[], " +
 165         "[]]")
 166     @ExpectedParameterTypeAnnotations({
 167         "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=2)",
 168         "null"})
 169     public static class StaticNestedClass2 {
 170         public StaticNestedClass2(@MarkerTypeAnnotation(2) int parameter1,
 171                             int parameter2) {}
 172     }
 173 
 174     @Target(ElementType.TYPE_USE)
 175     @Retention(RetentionPolicy.RUNTIME)
 176     @interface MarkerTypeAnnotation {
 177         int value();
 178     }
 179 
 180     /**
 181      * String form of expected value of calling
 182      * getParameterAnnotations on a constructor.
 183      */
 184     @Target(ElementType.TYPE)
 185     @Retention(RetentionPolicy.RUNTIME)
 186     @interface ExpectedGetParameterAnnotations {
 187         String value();
 188     }
 189 
 190     /**
 191      * String form of expected value of calling
 192      * getAnnotation(MarkerTypeAnnotation.class) on each element of the
 193      * result of getParameters() on a constructor.
 194      */
 195     @Target(ElementType.TYPE)
 196     @Retention(RetentionPolicy.RUNTIME)
 197     @interface ExpectedParameterTypeAnnotations {
 198         String[] value();
 199     }
 200 }