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