| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import java.lang.annotation.Annotation; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Parameter; |
| |
| public class Main { |
| // A simple parameter annotation |
| @Retention(RetentionPolicy.RUNTIME) |
| public @interface AnnotationA {} |
| |
| // A parameter annotation with additional state |
| @Retention(RetentionPolicy.RUNTIME) |
| public @interface AnnotationB { |
| String value() default "default-value"; |
| } |
| |
| // An inner class whose constructors with have an implicit |
| // argument for the enclosing instance. |
| public class Inner { |
| private final int number; |
| private final String text; |
| boolean flag; |
| |
| Inner(@AnnotationA int number, String text) { |
| this.number = number; |
| this.text = text; |
| this.flag = false; |
| } |
| |
| Inner(@AnnotationA int number, String text, @AnnotationB("x") boolean flag) { |
| this.number = number; |
| this.text = text; |
| this.flag = flag; |
| } |
| } |
| |
| // An inner class whose constructors with have no implicit |
| // arguments for the enclosing instance. |
| public static class StaticInner { |
| private final int number; |
| private final String text; |
| boolean flag; |
| |
| StaticInner(@AnnotationA int number, String text) { |
| this.number = number; |
| this.text = text; |
| this.flag = false; |
| } |
| |
| StaticInner(@AnnotationB("foo") int number, String text, @AnnotationA boolean flag) { |
| this.number = number; |
| this.text = text; |
| this.flag = flag; |
| } |
| } |
| |
| public enum ImportantNumber { |
| ONE(1.0), |
| TWO(2.0), |
| MANY(3.0, true); |
| |
| private double doubleValue; |
| private boolean isLarge; |
| |
| ImportantNumber(@AnnotationA double doubleValue) { |
| this.doubleValue = doubleValue; |
| this.isLarge = false; |
| } |
| |
| ImportantNumber(@AnnotationB("x") double doubleValue, @AnnotationB("y") boolean isLarge) { |
| this.doubleValue = doubleValue; |
| this.isLarge = isLarge; |
| } |
| } |
| |
| public enum BinaryNumber { |
| ZERO, |
| ONE; |
| } |
| |
| private abstract static class AnonymousBase { |
| public AnonymousBase(@AnnotationA String s) {} |
| } |
| |
| private static String annotationToNormalizedString(Annotation annotation) { |
| // String.replace() to accomodate different representation across VMs. |
| return annotation.toString().replace("\"", ""); |
| } |
| |
| private static void DumpConstructorParameterAnnotations(Class<?> cls) throws Throwable { |
| System.out.println(cls.getName()); |
| for (Constructor c : cls.getDeclaredConstructors()) { |
| System.out.println(" " + c); |
| Annotation[][] annotations = c.getParameterAnnotations(); |
| Parameter[] parameters = c.getParameters(); |
| for (int i = 0; i < annotations.length; ++i) { |
| // Exercise java.lang.reflect.Executable.getParameterAnnotationsNative() |
| // which retrieves all annotations for the parameters. |
| System.out.print(" Parameter [" + i + "]:"); |
| for (Annotation annotation : parameters[i].getAnnotations()) { |
| System.out.println(" Indexed : " + annotationToNormalizedString(annotation)); |
| } |
| for (Annotation annotation : annotations[i]) { |
| System.out.println(" Array : " + annotationToNormalizedString(annotation)); |
| } |
| |
| // Exercise Parameter.getAnnotationNative() with |
| // retrieves a single parameter annotation according to type. |
| Object[] opaqueClasses = new Object[] {AnnotationA.class, AnnotationB.class}; |
| for (Object opaqueClass : opaqueClasses) { |
| @SuppressWarnings("unchecked") |
| Class<? extends Annotation> annotationClass = |
| (Class<? extends Annotation>) opaqueClass; |
| Annotation annotation = parameters[i].getDeclaredAnnotation(annotationClass); |
| String hasAnnotation = (annotation != null ? "Yes" : "No"); |
| System.out.println(" " + annotationClass.getName() + " " + hasAnnotation); |
| |
| Annotation[] parameterAnnotations = parameters[i].getDeclaredAnnotationsByType(annotationClass); |
| for (Annotation parameterAnnotation : parameterAnnotations) { |
| System.out.println(" " + annotationToNormalizedString(parameterAnnotation)); |
| } |
| } |
| } |
| } |
| } |
| |
| private Class<?> getLocalClassWithEnclosingInstanceCapture() { |
| class LocalClass { |
| private final int integerValue; |
| |
| LocalClass(@AnnotationA int integerValue) { |
| this.integerValue = integerValue; |
| } |
| } |
| return LocalClass.class; |
| } |
| |
| private Class<?> getLocalClassWithEnclosingInstanceAndLocalCapture() { |
| final long CAPTURED_VALUE = System.currentTimeMillis(); |
| class LocalClassWithCapture { |
| private final String value; |
| private final long capturedValue; |
| |
| LocalClassWithCapture(@AnnotationA String p1) { |
| this.value = p1; |
| this.capturedValue = CAPTURED_VALUE; |
| } |
| } |
| return LocalClassWithCapture.class; |
| } |
| |
| public static void main(String[] args) throws Throwable { |
| // A local class declared in a static context (0 implicit parameters). |
| class LocalClassStaticContext { |
| private final int value; |
| |
| LocalClassStaticContext(@AnnotationA int p0) { |
| this.value = p0; |
| } |
| } |
| |
| final long CAPTURED_VALUE = System.currentTimeMillis(); |
| // A local class declared in a static context with a capture (1 implicit parameters). |
| class LocalClassStaticContextWithCapture { |
| private final long capturedValue; |
| private final String argumentValue; |
| |
| LocalClassStaticContextWithCapture(@AnnotationA String p1) { |
| this.capturedValue = CAPTURED_VALUE; |
| this.argumentValue = p1; |
| } |
| } |
| |
| // Another local class declared in a static context with a capture (1 implicit parameters). |
| class LocalClassStaticContextWithCaptureAlternateOrdering { |
| private final String argumentValue; |
| private final long capturedValue; |
| |
| LocalClassStaticContextWithCaptureAlternateOrdering(@AnnotationA String p1) { |
| this.argumentValue = p1; |
| this.capturedValue = CAPTURED_VALUE; |
| } |
| } |
| |
| DumpConstructorParameterAnnotations(Main.class); |
| DumpConstructorParameterAnnotations(LocalClassStaticContext.class); |
| DumpConstructorParameterAnnotations(LocalClassStaticContextWithCapture.class); |
| DumpConstructorParameterAnnotations(LocalClassStaticContextWithCaptureAlternateOrdering.class); |
| Main m = new Main(); |
| DumpConstructorParameterAnnotations(m.getLocalClassWithEnclosingInstanceCapture()); |
| DumpConstructorParameterAnnotations(m.getLocalClassWithEnclosingInstanceAndLocalCapture()); |
| DumpConstructorParameterAnnotations(Inner.class); |
| DumpConstructorParameterAnnotations(StaticInner.class); |
| DumpConstructorParameterAnnotations(ImportantNumber.class); |
| DumpConstructorParameterAnnotations(BinaryNumber.class); |
| DumpConstructorParameterAnnotations(new AnonymousBase("") {}.getClass()); |
| } |
| } |