1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package libcore.java.lang.reflect.annotations;
18
19import junit.framework.TestCase;
20
21import java.lang.annotation.Annotation;
22import java.lang.reflect.Constructor;
23import java.lang.reflect.Executable;
24import java.lang.reflect.Method;
25import java.lang.reflect.Parameter;
26import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
27import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC;
28import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationD;
29import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
30import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
31
32import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
33import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation;
34import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent;
35import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods;
36
37/**
38 * Tests for the {@link java.lang.reflect.AnnotatedElement} methods from the {@link Parameter}
39 * objects obtained from both {@link Constructor} and {@link Method}.
40 */
41public class AnnotatedElementParameterTest extends TestCase {
42
43    private static class MethodClass {
44        public void methodWithoutAnnotatedParameters(String parameter1, String parameter2) {}
45
46        public void methodWithAnnotatedParameters(@AnnotationB @AnnotationD String parameter1,
47                @AnnotationC @AnnotationD String parameter2) {}
48    }
49
50    public void testMethodParameterAnnotations() throws Exception {
51        Class<?> c = MethodClass.class;
52        {
53            Parameter[] parameters = c.getDeclaredMethod(
54                    "methodWithoutAnnotatedParameters", String.class, String.class).getParameters();
55            Parameter parameter0 = parameters[0];
56            checkAnnotatedElementPresentMethods(parameter0);
57
58            Parameter parameter1 = parameters[1];
59            checkAnnotatedElementPresentMethods(parameter1);
60        }
61        {
62            Parameter[] parameters = c.getDeclaredMethod(
63                    "methodWithAnnotatedParameters", String.class, String.class).getParameters();
64
65            Parameter parameter0 = parameters[0];
66            checkAnnotatedElementPresentMethods(parameter0, AnnotationB.class, AnnotationD.class);
67
68            Parameter parameter1 = parameters[1];
69            checkAnnotatedElementPresentMethods(parameter1, AnnotationC.class, AnnotationD.class);
70        }
71    }
72
73    private static class ConstructorClass {
74        // No annotations.
75        public ConstructorClass(Integer parameter1, Integer parameter2) {}
76
77        // Annotations.
78        public ConstructorClass(@AnnotationB @AnnotationD String parameter1,
79                @AnnotationC @AnnotationD String parameter2) {}
80    }
81
82    public void testConstructorParameterAnnotations() throws Exception {
83        Class<?> c = ConstructorClass.class;
84        {
85            Parameter[] parameters =
86                    c.getDeclaredConstructor(Integer.class, Integer.class).getParameters();
87            Parameter parameter0 = parameters[0];
88            checkAnnotatedElementPresentMethods(parameter0);
89
90            Parameter parameter1 = parameters[1];
91            checkAnnotatedElementPresentMethods(parameter1);
92        }
93        {
94            Parameter[] parameters =
95                    c.getDeclaredConstructor(String.class, String.class).getParameters();
96
97            Parameter parameter0 = parameters[0];
98            checkAnnotatedElementPresentMethods(parameter0, AnnotationB.class, AnnotationD.class);
99
100            Parameter parameter1 = parameters[1];
101            checkAnnotatedElementPresentMethods(parameter1, AnnotationC.class, AnnotationD.class);
102        }
103    }
104
105    private static class AnnotatedMethodClass {
106        void noAnnotation(String p0) {}
107
108        void multipleAnnotationOddity(
109                @Repeated(1) @Container({@Repeated(2), @Repeated(3)}) String p0) {}
110
111        void multipleAnnotationExplicitSingle(@Container({@Repeated(1)}) String p0) {}
112
113        void multipleAnnotation(@Repeated(1) @Repeated(2) String p0) {}
114
115        void singleAnnotation(@Repeated(1) String p0) {}
116
117        static void staticSingleAnnotation(@Repeated(1) String p0) {}
118
119        static Method getMethodWithoutAnnotations() throws Exception {
120            return AnnotatedMethodClass.class.getDeclaredMethod("noAnnotation", String.class);
121        }
122
123        static Method getMethodMultipleAnnotationOddity() throws Exception {
124            return AnnotatedMethodClass.class.getDeclaredMethod(
125                    "multipleAnnotationOddity", String.class);
126        }
127
128        static Method getMethodMultipleAnnotationExplicitSingle() throws Exception {
129            return AnnotatedMethodClass.class.getDeclaredMethod(
130                    "multipleAnnotationExplicitSingle", String.class);
131        }
132
133        static Method getMethodMultipleAnnotation() throws Exception {
134            return AnnotatedMethodClass.class.getDeclaredMethod("multipleAnnotation", String.class);
135        }
136
137        static Method getMethodSingleAnnotation() throws Exception {
138            return AnnotatedMethodClass.class.getDeclaredMethod("singleAnnotation", String.class);
139        }
140
141        static Method getMethodStaticSingleAnnotation() throws Exception {
142            return AnnotatedMethodClass.class.getDeclaredMethod("staticSingleAnnotation",
143                    String.class);
144        }
145    }
146
147    private static abstract class AnnotatedMethodAbstractClass {
148        abstract void abstractSingleAnnotation(@Repeated(1) String p0);
149
150        static Method getMethodAbstractSingleAnnotation() throws Exception {
151            return AnnotatedMethodAbstractClass.class.getDeclaredMethod(
152                    "abstractSingleAnnotation", String.class);
153        }
154    }
155
156    // Tests for isAnnotationPresent and getDeclaredAnnotation.
157    public void testMethodDeclaredAnnotation() throws Exception {
158        Class<? extends Annotation> repeated = Repeated.class;
159        checkParameter0DeclaredAnnotation(
160                AnnotatedMethodClass.getMethodWithoutAnnotations(),
161                repeated, null);
162        checkParameter0DeclaredAnnotation(
163                AnnotatedMethodClass.getMethodMultipleAnnotationOddity(),
164                repeated, "@Repeated(1)");
165        checkParameter0DeclaredAnnotation(
166                AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(),
167                repeated, null);
168        checkParameter0DeclaredAnnotation(
169                AnnotatedMethodClass.getMethodMultipleAnnotation(),
170                repeated, null);
171        checkParameter0DeclaredAnnotation(
172                AnnotatedMethodClass.getMethodSingleAnnotation(),
173                repeated, "@Repeated(1)");
174        checkParameter0DeclaredAnnotation(
175                AnnotatedMethodClass.getMethodStaticSingleAnnotation(),
176                repeated, "@Repeated(1)");
177        checkParameter0DeclaredAnnotation(
178                AnnotatedMethodAbstractClass.getMethodAbstractSingleAnnotation(),
179                repeated, "@Repeated(1)");
180
181        Class<? extends Annotation> container = Container.class;
182        checkParameter0DeclaredAnnotation(
183                AnnotatedMethodClass.getMethodWithoutAnnotations(),
184                container, null);
185        checkParameter0DeclaredAnnotation(
186                AnnotatedMethodClass.getMethodMultipleAnnotationOddity(),
187                container, "@Container({@Repeated(2), @Repeated(3)})");
188        checkParameter0DeclaredAnnotation(
189                AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(),
190                container, "@Container({@Repeated(1)})");
191        checkParameter0DeclaredAnnotation(
192                AnnotatedMethodClass.getMethodMultipleAnnotation(),
193                container, "@Container({@Repeated(1), @Repeated(2)})");
194        checkParameter0DeclaredAnnotation(
195                AnnotatedMethodClass.getMethodSingleAnnotation(),
196                container, null);
197    }
198
199    private static class AnnotatedConstructorClass {
200        public AnnotatedConstructorClass(Boolean p0) {}
201
202        public AnnotatedConstructorClass(
203                @Repeated(1) @Container({@Repeated(2), @Repeated(3)}) Long p0) {}
204
205        public AnnotatedConstructorClass(@Container({@Repeated(1)}) Double p0) {}
206
207        public AnnotatedConstructorClass(@Repeated(1) @Repeated(2) Integer p0) {}
208
209        public AnnotatedConstructorClass(@Repeated(1) String p0) {}
210
211        static Constructor<?> getConstructorWithoutAnnotations() throws Exception {
212            return AnnotatedConstructorClass.class.getDeclaredConstructor(Boolean.class);
213        }
214
215        static Constructor<?> getConstructorMultipleAnnotationOddity() throws Exception {
216            return AnnotatedConstructorClass.class.getDeclaredConstructor(Long.class);
217        }
218
219        static Constructor<?> getConstructorMultipleAnnotationExplicitSingle()
220                throws Exception {
221            return AnnotatedConstructorClass.class.getDeclaredConstructor(Double.class);
222        }
223
224        static Constructor<?> getConstructorSingleAnnotation() throws Exception {
225            return AnnotatedConstructorClass.class.getDeclaredConstructor(String.class);
226        }
227
228        static Constructor<?> getConstructorMultipleAnnotation() throws Exception {
229            return AnnotatedConstructorClass.class.getDeclaredConstructor(Integer.class);
230        }
231    }
232
233    // Tests for isAnnotationPresent and getDeclaredAnnotation.
234    public void testConstructorDeclaredAnnotation() throws Exception {
235        Class<? extends Annotation> repeated = Repeated.class;
236        checkParameter0DeclaredAnnotation(
237                AnnotatedConstructorClass.getConstructorWithoutAnnotations(),
238                repeated, null);
239        checkParameter0DeclaredAnnotation(
240                AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(),
241                repeated, "@Repeated(1)");
242        checkParameter0DeclaredAnnotation(
243                AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(),
244                repeated, null);
245        checkParameter0DeclaredAnnotation(
246                AnnotatedConstructorClass.getConstructorMultipleAnnotation(),
247                repeated, null);
248        checkParameter0DeclaredAnnotation(
249                AnnotatedConstructorClass.getConstructorSingleAnnotation(),
250                repeated, "@Repeated(1)");
251
252        Class<? extends Annotation> container = Container.class;
253        checkParameter0DeclaredAnnotation(
254                AnnotatedConstructorClass.getConstructorWithoutAnnotations(),
255                container, null);
256        checkParameter0DeclaredAnnotation(
257                AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(),
258                container, "@Container({@Repeated(2), @Repeated(3)})");
259        checkParameter0DeclaredAnnotation(
260                AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(),
261                container, "@Container({@Repeated(1)})");
262        checkParameter0DeclaredAnnotation(
263                AnnotatedConstructorClass.getConstructorMultipleAnnotation(),
264                container, "@Container({@Repeated(1), @Repeated(2)})");
265        checkParameter0DeclaredAnnotation(
266                AnnotatedConstructorClass.getConstructorSingleAnnotation(),
267                container, null);
268    }
269
270    private static void checkParameter0DeclaredAnnotation(
271            Executable executable, Class<? extends Annotation> annotationType,
272            String expectedAnnotationString) throws Exception {
273        Parameter parameter = executable.getParameters()[0];
274
275        // isAnnotationPresent
276        assertIsAnnotationPresent(parameter, annotationType, expectedAnnotationString != null);
277
278        // getDeclaredAnnotation
279        assertGetDeclaredAnnotation(parameter, annotationType, expectedAnnotationString);
280    }
281
282    public void testMethodGetDeclaredAnnotationsByType() throws Exception {
283        Class<? extends Annotation> repeated = Repeated.class;
284        checkParameter0GetDeclaredAnnotationsByType(
285                AnnotatedMethodClass.getMethodWithoutAnnotations(),
286                repeated, EXPECT_EMPTY);
287        checkParameter0GetDeclaredAnnotationsByType(
288                AnnotatedMethodClass.getMethodMultipleAnnotationOddity(),
289                repeated, "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
290        checkParameter0GetDeclaredAnnotationsByType(
291                AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(),
292                repeated, "@Repeated(1)");
293        checkParameter0GetDeclaredAnnotationsByType(
294                AnnotatedMethodClass.getMethodMultipleAnnotation(),
295                repeated, "@Repeated(1)", "@Repeated(2)");
296        checkParameter0GetDeclaredAnnotationsByType(
297                AnnotatedMethodClass.getMethodSingleAnnotation(),
298                repeated, "@Repeated(1)");
299
300        Class<? extends Annotation> container = Container.class;
301        checkParameter0GetDeclaredAnnotationsByType(
302                AnnotatedMethodClass.getMethodWithoutAnnotations(),
303                container, EXPECT_EMPTY);
304        checkParameter0GetDeclaredAnnotationsByType(
305                AnnotatedMethodClass.getMethodMultipleAnnotationOddity(),
306                container, "@Container({@Repeated(2), @Repeated(3)})");
307        checkParameter0GetDeclaredAnnotationsByType(
308                AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(),
309                container, "@Container({@Repeated(1)})");
310        checkParameter0GetDeclaredAnnotationsByType(
311                AnnotatedMethodClass.getMethodMultipleAnnotation(),
312                container, "@Container({@Repeated(1), @Repeated(2)})");
313        checkParameter0GetDeclaredAnnotationsByType(
314                AnnotatedMethodClass.getMethodSingleAnnotation(),
315                container, EXPECT_EMPTY);
316    }
317
318    public void testConstructorGetDeclaredAnnotationsByType() throws Exception {
319        Class<? extends Annotation> repeated = Repeated.class;
320        checkParameter0GetDeclaredAnnotationsByType(
321                AnnotatedConstructorClass.getConstructorWithoutAnnotations(),
322                repeated, EXPECT_EMPTY);
323        checkParameter0GetDeclaredAnnotationsByType(
324                AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(),
325                repeated, "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
326        checkParameter0GetDeclaredAnnotationsByType(
327                AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(),
328                repeated, "@Repeated(1)");
329        checkParameter0GetDeclaredAnnotationsByType(
330                AnnotatedConstructorClass.getConstructorMultipleAnnotation(),
331                repeated, "@Repeated(1)", "@Repeated(2)");
332        checkParameter0GetDeclaredAnnotationsByType(
333                AnnotatedConstructorClass.getConstructorSingleAnnotation(),
334                repeated, "@Repeated(1)");
335
336        Class<? extends Annotation> container = Container.class;
337        checkParameter0GetDeclaredAnnotationsByType(
338                AnnotatedConstructorClass.getConstructorWithoutAnnotations(),
339                container, EXPECT_EMPTY);
340        checkParameter0GetDeclaredAnnotationsByType(
341                AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(),
342                container, "@Container({@Repeated(2), @Repeated(3)})");
343        checkParameter0GetDeclaredAnnotationsByType(
344                AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(),
345                container, "@Container({@Repeated(1)})");
346        checkParameter0GetDeclaredAnnotationsByType(
347                AnnotatedConstructorClass.getConstructorMultipleAnnotation(),
348                container, "@Container({@Repeated(1), @Repeated(2)})");
349        checkParameter0GetDeclaredAnnotationsByType(
350                AnnotatedConstructorClass.getConstructorSingleAnnotation(),
351                container, EXPECT_EMPTY);
352    }
353
354    private static void checkParameter0GetDeclaredAnnotationsByType(
355            Executable executable, Class<? extends Annotation> annotationType,
356            String... expectedAnnotationStrings) throws Exception {
357        Parameter parameter = executable.getParameters()[0];
358        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
359                parameter, annotationType, expectedAnnotationStrings);
360    }
361
362    public void testMethodGetAnnotationsByType() throws Exception {
363        Class<? extends Annotation> repeated = Repeated.class;
364        checkParameter0GetAnnotationsByType(
365                AnnotatedMethodClass.getMethodWithoutAnnotations(),
366                repeated, EXPECT_EMPTY);
367        checkParameter0GetAnnotationsByType(
368                AnnotatedMethodClass.getMethodMultipleAnnotationOddity(),
369                repeated, "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
370        checkParameter0GetAnnotationsByType(
371                AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(),
372                repeated, "@Repeated(1)");
373        checkParameter0GetAnnotationsByType(
374                AnnotatedMethodClass.getMethodMultipleAnnotation(),
375                repeated, "@Repeated(1)", "@Repeated(2)");
376        checkParameter0GetAnnotationsByType(
377                AnnotatedMethodClass.getMethodSingleAnnotation(),
378                repeated, "@Repeated(1)");
379
380        Class<? extends Annotation> container = Container.class;
381        checkParameter0GetAnnotationsByType(
382                AnnotatedMethodClass.getMethodWithoutAnnotations(),
383                container, EXPECT_EMPTY);
384        checkParameter0GetAnnotationsByType(
385                AnnotatedMethodClass.getMethodMultipleAnnotationOddity(),
386                container, "@Container({@Repeated(2), @Repeated(3)})");
387        checkParameter0GetAnnotationsByType(
388                AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(),
389                container, "@Container({@Repeated(1)})");
390        checkParameter0GetAnnotationsByType(
391                AnnotatedMethodClass.getMethodMultipleAnnotation(),
392                container, "@Container({@Repeated(1), @Repeated(2)})");
393        checkParameter0GetAnnotationsByType(
394                AnnotatedMethodClass.getMethodSingleAnnotation(),
395                container, EXPECT_EMPTY);
396    }
397
398    public void testConstructorGetAnnotationsByType() throws Exception {
399        Class<? extends Annotation> repeated = Repeated.class;
400        checkParameter0GetAnnotationsByType(
401                AnnotatedConstructorClass.getConstructorWithoutAnnotations(),
402                repeated, EXPECT_EMPTY);
403        checkParameter0GetAnnotationsByType(
404                AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(),
405                repeated, "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
406        checkParameter0GetAnnotationsByType(
407                AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(),
408                repeated, "@Repeated(1)");
409        checkParameter0GetAnnotationsByType(
410                AnnotatedConstructorClass.getConstructorMultipleAnnotation(),
411                repeated, "@Repeated(1)", "@Repeated(2)");
412        checkParameter0GetAnnotationsByType(
413                AnnotatedConstructorClass.getConstructorSingleAnnotation(),
414                repeated, "@Repeated(1)");
415
416        Class<? extends Annotation> container = Container.class;
417        checkParameter0GetAnnotationsByType(
418                AnnotatedConstructorClass.getConstructorWithoutAnnotations(),
419                container, EXPECT_EMPTY);
420        checkParameter0GetAnnotationsByType(
421                AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(),
422                container, "@Container({@Repeated(2), @Repeated(3)})");
423        checkParameter0GetAnnotationsByType(
424                AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(),
425                container, "@Container({@Repeated(1)})");
426        checkParameter0GetAnnotationsByType(
427                AnnotatedConstructorClass.getConstructorMultipleAnnotation(),
428                container, "@Container({@Repeated(1), @Repeated(2)})");
429        checkParameter0GetAnnotationsByType(
430                AnnotatedConstructorClass.getConstructorSingleAnnotation(),
431                container, EXPECT_EMPTY);
432    }
433
434    private static void checkParameter0GetAnnotationsByType(
435            Executable executable, Class<? extends Annotation> annotationType,
436            String... expectedAnnotationStrings) throws Exception {
437        Parameter parameter = executable.getParameters()[0];
438        AnnotatedElementTestSupport.assertGetAnnotationsByType(
439                parameter, annotationType, expectedAnnotationStrings);
440    }
441
442    /**
443     * As an inner class the constructor will actually have two parameters: the first, referencing
444     * the enclosing object, is inserted by the compiler.
445     */
446    class InnerClass {
447        InnerClass(@Repeated(1) String p1) {}
448    }
449
450    /** Special case testing for a compiler-generated constructor parameter. */
451    public void testImplicitConstructorParameters_singleAnnotation() throws Exception {
452        Constructor<InnerClass> constructor =
453                InnerClass.class.getDeclaredConstructor(
454                        AnnotatedElementParameterTest.class, String.class);
455        Parameter[] parameters = constructor.getParameters();
456
457        // The compiler-generated constructor should have no annotations.
458        Parameter parameter0 = parameters[0];
459        AnnotatedElementTestSupport.assertGetAnnotationsByType(
460                parameter0, Repeated.class, new String[0]);
461        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
462                parameter0, Repeated.class, new String[0]);
463        AnnotatedElementTestSupport.assertGetDeclaredAnnotation(
464                parameter0, Repeated.class, null);
465        AnnotatedElementTestSupport.assertIsAnnotationPresent(parameter0, Repeated.class, false);
466
467        // The annotation should remain on the correct parameter.
468        Parameter parameter1 = parameters[1];
469        AnnotatedElementTestSupport.assertGetAnnotationsByType(
470                parameter1, Repeated.class, new String[] {"@Repeated(1)"});
471        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
472                parameter1, Repeated.class, new String[] {"@Repeated(1)"});
473        AnnotatedElementTestSupport.assertGetDeclaredAnnotation(
474                parameter1, Repeated.class, "@Repeated(1)");
475        AnnotatedElementTestSupport.assertIsAnnotationPresent(
476                parameter1, Repeated.class, true);
477    }
478}
479