/* * 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()); } }