1 /*
2  * Copyright (C) 2018 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 
17 import java.lang.annotation.Annotation;
18 import java.lang.annotation.Retention;
19 import java.lang.annotation.RetentionPolicy;
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.Parameter;
22 
23 public class Main {
24     // A simple parameter annotation
25     @Retention(RetentionPolicy.RUNTIME)
26     public @interface AnnotationA {}
27 
28     // A parameter annotation with additional state
29     @Retention(RetentionPolicy.RUNTIME)
30     public @interface AnnotationB {
value()31         String value() default "default-value";
32     }
33 
34     // An inner class whose constructors with have an implicit
35     // argument for the enclosing instance.
36     public class Inner {
37         private final int number;
38         private final String text;
39         boolean flag;
40 
Inner(@nnotationA int number, String text)41         Inner(@AnnotationA int number, String text) {
42             this.number = number;
43             this.text = text;
44             this.flag = false;
45         }
46 
Inner(@nnotationA int number, String text, @AnnotationB("x") boolean flag)47         Inner(@AnnotationA int number, String text, @AnnotationB("x") boolean flag) {
48             this.number = number;
49             this.text = text;
50             this.flag = flag;
51         }
52     }
53 
54     // An inner class whose constructors with have no implicit
55     // arguments for the enclosing instance.
56     public static class StaticInner {
57         private final int number;
58         private final String text;
59         boolean flag;
60 
StaticInner(@nnotationA int number, String text)61         StaticInner(@AnnotationA int number, String text) {
62             this.number = number;
63             this.text = text;
64             this.flag = false;
65         }
66 
StaticInner(@nnotationB"foo") int number, String text, @AnnotationA boolean flag)67         StaticInner(@AnnotationB("foo") int number, String text, @AnnotationA boolean flag) {
68             this.number = number;
69             this.text = text;
70             this.flag = flag;
71         }
72     }
73 
74     public enum ImportantNumber {
75         ONE(1.0),
76         TWO(2.0),
77         MANY(3.0, true);
78 
79         private double doubleValue;
80         private boolean isLarge;
81 
ImportantNumber(@nnotationA double doubleValue)82         ImportantNumber(@AnnotationA double doubleValue) {
83             this.doubleValue = doubleValue;
84             this.isLarge = false;
85         }
86 
ImportantNumber(@nnotationB"x") double doubleValue, @AnnotationB("y") boolean isLarge)87         ImportantNumber(@AnnotationB("x") double doubleValue, @AnnotationB("y") boolean isLarge) {
88             this.doubleValue = doubleValue;
89             this.isLarge = isLarge;
90         }
91     }
92 
93     public enum BinaryNumber {
94         ZERO,
95         ONE;
96     }
97 
98     private abstract static class AnonymousBase {
AnonymousBase(@nnotationA String s)99         public AnonymousBase(@AnnotationA String s) {}
100     }
101 
annotationToNormalizedString(Annotation annotation)102     private static String annotationToNormalizedString(Annotation annotation) {
103         // String.replace() to accomodate different representation across VMs.
104         return annotation.toString().replace("\"", "");
105     }
106 
DumpConstructorParameterAnnotations(Class<?> cls)107     private static void DumpConstructorParameterAnnotations(Class<?> cls) throws Throwable {
108         System.out.println(cls.getName());
109         for (Constructor c : cls.getDeclaredConstructors()) {
110             System.out.println(" " + c);
111             Annotation[][] annotations = c.getParameterAnnotations();
112             Parameter[] parameters = c.getParameters();
113             for (int i = 0; i < annotations.length; ++i) {
114                 // Exercise java.lang.reflect.Executable.getParameterAnnotationsNative()
115                 // which retrieves all annotations for the parameters.
116                 System.out.print("  Parameter [" + i + "]:");
117                 for (Annotation annotation : parameters[i].getAnnotations()) {
118                     System.out.println("    Indexed : " + annotationToNormalizedString(annotation));
119                 }
120                 for (Annotation annotation : annotations[i]) {
121                     System.out.println("    Array : " + annotationToNormalizedString(annotation));
122                 }
123 
124                 // Exercise Parameter.getAnnotationNative() with
125                 // retrieves a single parameter annotation according to type.
126                 Object[] opaqueClasses = new Object[] {AnnotationA.class, AnnotationB.class};
127                 for (Object opaqueClass : opaqueClasses) {
128                     @SuppressWarnings("unchecked")
129                     Class<? extends Annotation> annotationClass =
130                             (Class<? extends Annotation>) opaqueClass;
131                     Annotation annotation = parameters[i].getDeclaredAnnotation(annotationClass);
132                     String hasAnnotation = (annotation != null ? "Yes" : "No");
133                     System.out.println("    " + annotationClass.getName() + " " + hasAnnotation);
134 
135                     Annotation[] parameterAnnotations = parameters[i].getDeclaredAnnotationsByType(annotationClass);
136                     for (Annotation parameterAnnotation : parameterAnnotations) {
137                         System.out.println("    " + annotationToNormalizedString(parameterAnnotation));
138                     }
139                 }
140             }
141         }
142     }
143 
getLocalClassWithEnclosingInstanceCapture()144     private Class<?> getLocalClassWithEnclosingInstanceCapture() {
145         class LocalClass {
146             private final int integerValue;
147 
148             LocalClass(@AnnotationA int integerValue) {
149                 this.integerValue = integerValue;
150             }
151         }
152         return LocalClass.class;
153     }
154 
getLocalClassWithEnclosingInstanceAndLocalCapture()155     private Class<?> getLocalClassWithEnclosingInstanceAndLocalCapture() {
156         final long CAPTURED_VALUE = System.currentTimeMillis();
157         class LocalClassWithCapture {
158             private final String value;
159             private final long capturedValue;
160 
161             LocalClassWithCapture(@AnnotationA String p1) {
162                 this.value = p1;
163                 this.capturedValue = CAPTURED_VALUE;
164             }
165         }
166         return LocalClassWithCapture.class;
167     }
168 
main(String[] args)169     public static void main(String[] args) throws Throwable {
170         // A local class declared in a static context (0 implicit parameters).
171         class LocalClassStaticContext {
172             private final int value;
173 
174             LocalClassStaticContext(@AnnotationA int p0) {
175                 this.value = p0;
176             }
177         }
178 
179         final long CAPTURED_VALUE = System.currentTimeMillis();
180         // A local class declared in a static context with a capture (1 implicit parameters).
181         class LocalClassStaticContextWithCapture {
182             private final long capturedValue;
183             private final String argumentValue;
184 
185             LocalClassStaticContextWithCapture(@AnnotationA String p1) {
186                 this.capturedValue = CAPTURED_VALUE;
187                 this.argumentValue = p1;
188             }
189         }
190 
191         // Another local class declared in a static context with a capture (1 implicit parameters).
192         class LocalClassStaticContextWithCaptureAlternateOrdering {
193             private final String argumentValue;
194             private final long capturedValue;
195 
196             LocalClassStaticContextWithCaptureAlternateOrdering(@AnnotationA String p1) {
197                 this.argumentValue = p1;
198                 this.capturedValue = CAPTURED_VALUE;
199             }
200         }
201 
202         DumpConstructorParameterAnnotations(Main.class);
203         DumpConstructorParameterAnnotations(LocalClassStaticContext.class);
204         DumpConstructorParameterAnnotations(LocalClassStaticContextWithCapture.class);
205         DumpConstructorParameterAnnotations(LocalClassStaticContextWithCaptureAlternateOrdering.class);
206         Main m = new Main();
207         DumpConstructorParameterAnnotations(m.getLocalClassWithEnclosingInstanceCapture());
208         DumpConstructorParameterAnnotations(m.getLocalClassWithEnclosingInstanceAndLocalCapture());
209         DumpConstructorParameterAnnotations(Inner.class);
210         DumpConstructorParameterAnnotations(StaticInner.class);
211         DumpConstructorParameterAnnotations(ImportantNumber.class);
212         DumpConstructorParameterAnnotations(BinaryNumber.class);
213         DumpConstructorParameterAnnotations(new AnonymousBase("") {}.getClass());
214     }
215 }
216