--- old/src/java.base/share/classes/java/lang/reflect/Constructor.java 2017-05-20 23:24:53.131588055 +0200 +++ new/src/java.base/share/classes/java/lang/reflect/Constructor.java 2017-05-20 23:24:53.052589430 +0200 @@ -30,6 +30,7 @@ import jdk.internal.reflect.ConstructorAccessor; import jdk.internal.reflect.Reflection; import jdk.internal.vm.annotation.ForceInline; +import sun.reflect.annotation.AnnotationSupport; import sun.reflect.annotation.TypeAnnotation; import sun.reflect.annotation.TypeAnnotationParser; import sun.reflect.generics.repository.ConstructorRepository; @@ -37,7 +38,7 @@ import sun.reflect.generics.factory.GenericsFactory; import sun.reflect.generics.scope.ConstructorScope; import java.lang.annotation.Annotation; -import java.lang.annotation.AnnotationFormatError; +import java.util.Arrays; import java.util.StringJoiner; /** @@ -584,26 +585,16 @@ */ @Override public Annotation[][] getParameterAnnotations() { - return sharedGetParameterAnnotations(parameterTypes, parameterAnnotations); - } - - @Override - void handleParameterNumberMismatch(int resultLength, int numParameters) { - Class declaringClass = getDeclaringClass(); - if (declaringClass.isEnum() || - declaringClass.isAnonymousClass() || - declaringClass.isLocalClass() ) - return ; // Can't do reliable parameter counting - else { - if (!declaringClass.isMemberClass() || // top-level - // Check for the enclosing instance parameter for - // non-static member classes - (declaringClass.isMemberClass() && - ((declaringClass.getModifiers() & Modifier.STATIC) == 0) && - resultLength + 1 != numParameters) ) { - throw new AnnotationFormatError( - "Parameter annotations don't match number of parameters"); + if (parameterAnnotations == null) { + return new Annotation[parameterTypes.length][0]; + } else { + Annotation[][] anns = parseParameterAnnotations(parameterAnnotations); + anns = AnnotationSupport.fixConstructorParameterAnnotations( + this, anns, new Annotation[0], Annotation[][]::new); + if (anns.length != parameterTypes.length) { + throwParameterAnnotationsDontMatchNumberOfParameters(); } + return anns; } } --- old/src/java.base/share/classes/java/lang/reflect/Executable.java 2017-05-20 23:24:53.435582766 +0200 +++ new/src/java.base/share/classes/java/lang/reflect/Executable.java 2017-05-20 23:24:53.358584106 +0200 @@ -543,21 +543,10 @@ */ public abstract Annotation[][] getParameterAnnotations(); - Annotation[][] sharedGetParameterAnnotations(Class[] parameterTypes, - byte[] parameterAnnotations) { - int numParameters = parameterTypes.length; - if (parameterAnnotations == null) - return new Annotation[numParameters][0]; - - Annotation[][] result = parseParameterAnnotations(parameterAnnotations); - - if (result.length != numParameters) - handleParameterNumberMismatch(result.length, numParameters); - return result; + void throwParameterAnnotationsDontMatchNumberOfParameters() { + throw new AnnotationFormatError("Parameter annotations don't match number of parameters"); } - abstract void handleParameterNumberMismatch(int resultLength, int numParameters); - /** * {@inheritDoc} * @throws NullPointerException {@inheritDoc} --- old/src/java.base/share/classes/java/lang/reflect/Method.java 2017-05-20 23:24:53.770576937 +0200 +++ new/src/java.base/share/classes/java/lang/reflect/Method.java 2017-05-20 23:24:53.686578399 +0200 @@ -706,7 +706,15 @@ */ @Override public Annotation[][] getParameterAnnotations() { - return sharedGetParameterAnnotations(parameterTypes, parameterAnnotations); + if (parameterAnnotations == null) { + return new Annotation[parameterTypes.length][0]; + } else { + Annotation[][] anns = parseParameterAnnotations(parameterAnnotations); + if (anns.length != parameterTypes.length) { + throwParameterAnnotationsDontMatchNumberOfParameters(); + } + return anns; + } } /** @@ -717,9 +725,4 @@ public AnnotatedType getAnnotatedReturnType() { return getAnnotatedReturnType0(getGenericReturnType()); } - - @Override - void handleParameterNumberMismatch(int resultLength, int numParameters) { - throw new AnnotationFormatError("Parameter annotations don't match number of parameters"); - } } --- old/src/java.base/share/classes/sun/reflect/annotation/AnnotationSupport.java 2017-05-20 23:24:54.104571125 +0200 +++ new/src/java.base/share/classes/sun/reflect/annotation/AnnotationSupport.java 2017-05-20 23:24:53.998572970 +0200 @@ -34,6 +34,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.IntFunction; import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.JavaLangAccess; @@ -178,6 +179,65 @@ } + /** + * There are cases where the number of parameters present for a constructor + * in source code and the number of parameters present for that constructor + * in the class file differ. Constructors of enum classes, anonymous classes, + * local classes and member classes may have additional parameters generated + * by compiler. This method fixes an array of "annotations collections" + * by prefixing and/or suffixing it with "empty collection" elements + * for parameters added by compiler. + * + * @param constructor the constructor + * @param annColls an array of annotation collections for parameters in source code + * @param emptyColl an empty collection replicated for compiler generated parameters + * @param arrayFactory a factory for arrays of collections + * @param the type of annotation collection + * @return new array of annotation collections for parameters in class file + */ + public static A[] fixConstructorParameterAnnotations( + Constructor constructor, A[] annColls, A emptyColl, IntFunction arrayFactory) + { + int prefixLength = 0; + boolean mayHaveSuffix = false; + Class declaringClass = constructor.getDeclaringClass(); + if (declaringClass.isEnum() || + (declaringClass.isAnonymousClass() && declaringClass.getSuperclass().isEnum())) { + // enum class or enum constant anonymous subclass + prefixLength = 2; + mayHaveSuffix = true; + } else if (declaringClass.isLocalClass() || declaringClass.isAnonymousClass()) { + // local or anonymous class + if (!Modifier.isStatic(declaringClass.getModifiers())) { + prefixLength = 1; + } + mayHaveSuffix = true; + } else if (declaringClass.isMemberClass()) { + // member class + if (!Modifier.isStatic(declaringClass.getModifiers())) { + prefixLength = 1; + } + } + // compute total length of array of arrays + int len = annColls.length + prefixLength; + if (len < constructor.getParameterCount() && mayHaveSuffix) { + len = constructor.getParameterCount(); + } + // prepend empty annotations prefix and/or append suffix if needed + if (len > annColls.length) { + A[] newAnnColls = arrayFactory.apply(len); + Arrays.fill(newAnnColls, 0, prefixLength, emptyColl); + System.arraycopy(annColls, 0, + newAnnColls, prefixLength, + annColls.length); + Arrays.fill(newAnnColls, + prefixLength + annColls.length, newAnnColls.length, + emptyColl); + annColls = newAnnColls; + } + return annColls; + } + /* Reflectively invoke the values-method of the given annotation * (container), cast it to an array of annotations and return the result. */ --- old/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotationParser.java 2017-05-20 23:24:54.380566323 +0200 +++ new/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotationParser.java 2017-05-20 23:24:54.293567837 +0200 @@ -123,6 +123,10 @@ tmp.add(t); } } + if (decl instanceof Constructor) { + l = AnnotationSupport.fixConstructorParameterAnnotations( + (Constructor) decl, l, null, ArrayList[]::new); + } for (int i = 0; i < size; i++) { @SuppressWarnings("unchecked") ArrayList list = l[i]; --- /dev/null 2017-05-20 18:20:32.042451134 +0200 +++ new/test/java/lang/annotation/TestConstructorParameterAnnotations.java 2017-05-20 23:24:54.616562217 +0200 @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8074977 + * @summary Test consistency of annotations on constructor parameters + * @compile TestConstructorParameterAnnotations.java + * @run main TestConstructorParameterAnnotations + * @compile -parameters TestConstructorParameterAnnotations.java + * @run main TestConstructorParameterAnnotations + */ + +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.*; + +/* + * Some constructor parameters are mandated; that is, they + * are not explicitly present in the source code, but required to be + * present by the Java Language Specification. In other cases, some + * constructor parameters are not present in the source, but are + * synthesized by the compiler as an implementation artifact. There is + * not a reliable mechanism to consistently determine whether or not + * a parameter is implicit or not. + * + * (Using the "-parameters" option to javac does emit the information + * needed to make a reliably determination, but the information is not + * present by default.) + * + * The lack of such a mechanism causes complications reading parameter + * annotations in some cases since annotations for parameters are + * written out for the parameters in the source code, but when reading + * annotations at runtime all the parameters, including implicit ones, + * are present. + */ +public class TestConstructorParameterAnnotations { + public static void main(String... args) { + int errors = 0; + Class[] classes = {NestedClass0.class, + NestedClass1.class, + NestedClass2.class, + NestedClass3.class, + NestedClass4.class, + StaticNestedClass0.class, + StaticNestedClass1.class, + StaticNestedClass2.class, + StaticNestedClass3.class, + StaticNestedClass4.class, + EnumClass.class}; + + for (Class clazz : classes) { + for (Constructor ctor : clazz.getConstructors()) { + System.out.println(ctor); + errors += checkGetParameterAnnotations(clazz, ctor); + errors += checkGetParametersGetAnnotation(clazz, ctor); + } + } + + if (errors > 0) + throw new RuntimeException(errors + " errors."); + return; + } + + private static int checkGetParameterAnnotations(Class clazz, + Constructor ctor) { + String annotationString = + Arrays.deepToString(ctor.getParameterAnnotations()); + String expectedString = + clazz.getAnnotation(ExpectedGetParameterAnnotations.class).value(); + + if (!Objects.equals(annotationString, expectedString)) { + System.err.println("Annotation mismatch on " + ctor + + "\n\tExpected:" + expectedString + + "\n\tActual: " + annotationString); + return 1; + } + return 0; + } + + private static int checkGetParametersGetAnnotation(Class clazz, + Constructor ctor) { + int errors = 0; + int i = 0; + ExpectedParameterAnnotations epa = + clazz.getAnnotation(ExpectedParameterAnnotations.class); + + for (Parameter param : ctor.getParameters() ) { + String annotationString = + Objects.toString(param.getAnnotation(MarkerAnnotation.class)); + String expectedString = epa.value()[i]; + + if (!Objects.equals(annotationString, expectedString)) { + System.err.println("Annotation mismatch on " + ctor + + " on param " + param + + "\n\tExpected:" + expectedString + + "\n\tActual: " + annotationString); + errors++; + } + i++; + } + return errors; + } + + @ExpectedGetParameterAnnotations("[[]]") + @ExpectedParameterAnnotations({"null"}) + public class NestedClass0 { + public NestedClass0() {} + } + + @ExpectedGetParameterAnnotations( + "[[], " + + "[@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)]]") + @ExpectedParameterAnnotations({ + "null", + "@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)"}) + public class NestedClass1 { + public NestedClass1(@MarkerAnnotation(1) int parameter) {} + } + + @ExpectedGetParameterAnnotations( + "[[], " + + "[@TestConstructorParameterAnnotations$MarkerAnnotation(value=2)], " + + "[]]") + @ExpectedParameterAnnotations({ + "null", + "@TestConstructorParameterAnnotations$MarkerAnnotation(value=2)", + "null"}) + public class NestedClass2 { + public NestedClass2(@MarkerAnnotation(2) int parameter1, + int parameter2) {} + } + + @ExpectedGetParameterAnnotations( + "[[], " + + "[@TestConstructorParameterAnnotations$MarkerAnnotation(value=3)], " + + "[]]") + @ExpectedParameterAnnotations({ + "null", + "@TestConstructorParameterAnnotations$MarkerAnnotation(value=3)", + "null"}) + public class NestedClass3 { + public

NestedClass3(@MarkerAnnotation(3) P parameter1, + int parameter2) {} + } + + @ExpectedGetParameterAnnotations( + "[[], " + + "[@TestConstructorParameterAnnotations$MarkerAnnotation(value=4)], " + + "[]]") + @ExpectedParameterAnnotations({ + "null", + "@TestConstructorParameterAnnotations$MarkerAnnotation(value=4)", + "null"}) + public class NestedClass4 { + public NestedClass4(@MarkerAnnotation(4) P parameter1, + Q parameter2) {} + } + + @ExpectedGetParameterAnnotations("[]") + @ExpectedParameterAnnotations({"null"}) + public static class StaticNestedClass0 { + public StaticNestedClass0() {} + } + + @ExpectedGetParameterAnnotations( + "[[@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)]]") + @ExpectedParameterAnnotations({ + "@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)"}) + public static class StaticNestedClass1 { + public StaticNestedClass1(@MarkerAnnotation(1) int parameter) {} + } + + @ExpectedGetParameterAnnotations( + "[[@TestConstructorParameterAnnotations$MarkerAnnotation(value=2)], " + + "[]]") + @ExpectedParameterAnnotations({ + "@TestConstructorParameterAnnotations$MarkerAnnotation(value=2)", + "null"}) + public static class StaticNestedClass2 { + public StaticNestedClass2(@MarkerAnnotation(2) int parameter1, + int parameter2) {} + } + + @ExpectedGetParameterAnnotations( + "[[@TestConstructorParameterAnnotations$MarkerAnnotation(value=3)], " + + "[]]") + @ExpectedParameterAnnotations({ + "@TestConstructorParameterAnnotations$MarkerAnnotation(value=3)", + "null"}) + public static class StaticNestedClass3 { + public

StaticNestedClass3(@MarkerAnnotation(3) P parameter1, + int parameter2) {} + } + + @ExpectedGetParameterAnnotations( + "[[@TestConstructorParameterAnnotations$MarkerAnnotation(value=4)], " + + "[]]") + @ExpectedParameterAnnotations({ + "@TestConstructorParameterAnnotations$MarkerAnnotation(value=4)", + "null"}) + public static class StaticNestedClass4 { + public StaticNestedClass4(@MarkerAnnotation(4) P parameter1, + Q parameter2) {} + } + + @ExpectedGetParameterAnnotations( + "[[], [], " + + "[@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)]]") + @ExpectedParameterAnnotations({ + "null", + "null", + "@TestConstructorParameterAnnotations$MarkerAnnotation(value=1)"}) + public enum EnumClass { + CONSTANT1; + EnumClass(@MarkerAnnotation(1) int parameter1) {} + } + + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.RUNTIME) + @interface MarkerAnnotation { + int value(); + } + + /** + * String form of expected value of calling + * getParameterAnnotations on a constructor. + */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @interface ExpectedGetParameterAnnotations { + String value(); + } + + /** + * String form of expected value of calling + * getAnnotation(MarkerAnnotation.class) on each element of the + * result of getParameters() on a constructor. + */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @interface ExpectedParameterAnnotations { + String[] value(); + } +} --- /dev/null 2017-05-20 18:20:32.042451134 +0200 +++ new/test/java/lang/annotation/typeAnnotations/TestConstructorParameterTypeAnnotations.java 2017-05-20 23:24:54.908557136 +0200 @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8074977 + * @summary Test consistency of annotations on constructor parameters + * @compile TestConstructorParameterTypeAnnotations.java + * @run main TestConstructorParameterTypeAnnotations + * @compile -parameters TestConstructorParameterTypeAnnotations.java + * @run main TestConstructorParameterTypeAnnotations + */ + +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.*; + +/* + * Some constructor parameters are mandated; that is, they + * are not explicitly present in the source code, but required to be + * present by the Java Language Specification. In other cases, some + * constructor parameters are not present in the source, but are + * synthesized by the compiler as an implementation artifact. There is + * not a reliable mechanism to consistently determine whether or not + * a parameter is implicit or not. + * + * (Using the "-parameters" option to javac does emit the information + * needed to make a reliably determination, but the information is not + * present by default.) + * + * The lack of such a mechanism causes complications reading parameter + * annotations in some cases since annotations for parameters are + * written out for the parameters in the source code, but when reading + * annotations at runtime all the parameters, including implicit ones, + * are present. + */ +public class TestConstructorParameterTypeAnnotations { + public static void main(String... args) { + int errors = 0; + Class[] classes = {NestedClass0.class, + NestedClass1.class, + NestedClass2.class, + NestedClass3.class, + NestedClass4.class, + StaticNestedClass0.class, + StaticNestedClass1.class, + StaticNestedClass2.class }; + + for (Class clazz : classes) { + for (Constructor ctor : clazz.getConstructors()) { + System.out.println(ctor); + errors += checkGetParameterAnnotations(clazz, ctor); + errors += checkGetAnnotatedParametersGetAnnotation(clazz, ctor); + } + } + + if (errors > 0) + throw new RuntimeException(errors + " errors."); + return; + } + + private static int checkGetParameterAnnotations(Class clazz, + Constructor ctor) { + String annotationString = + Arrays.deepToString(ctor.getParameterAnnotations()); + String expectedString = + clazz.getAnnotation(ExpectedGetParameterAnnotations.class).value(); + + if (!Objects.equals(annotationString, expectedString)) { + System.err.println("Annotation mismatch on " + ctor + + "\n\tExpected:" + expectedString + + "\n\tActual: " + annotationString); + return 1; + } + return 0; + } + + private static int checkGetAnnotatedParametersGetAnnotation(Class clazz, + Constructor ctor) { + int errors = 0; + int i = 0; + ExpectedParameterTypeAnnotations epa = + clazz.getAnnotation(ExpectedParameterTypeAnnotations.class); + + for (AnnotatedType param : ctor.getAnnotatedParameterTypes() ) { + String annotationString = + Objects.toString(param.getAnnotation(MarkerTypeAnnotation.class)); + String expectedString = epa.value()[i]; + + if (!Objects.equals(annotationString, expectedString)) { + System.err.println("Annotation mismatch on " + ctor + + " on param " + param + + "\n\tExpected:" + expectedString + + "\n\tActual: " + annotationString); + errors++; + } + i++; + } + return errors; + } + + @ExpectedGetParameterAnnotations("[[]]") + @ExpectedParameterTypeAnnotations({"null"}) + public class NestedClass0 { + public NestedClass0() {} + } + + @ExpectedGetParameterAnnotations("[[], []]") + @ExpectedParameterTypeAnnotations({ + "null", + "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=1)"}) + public class NestedClass1 { + public NestedClass1(@MarkerTypeAnnotation(1) int parameter) {} + } + + @ExpectedGetParameterAnnotations("[[], [], []]") + @ExpectedParameterTypeAnnotations({ + "null", + "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=2)", + "null"}) + public class NestedClass2 { + public NestedClass2(@MarkerTypeAnnotation(2) int parameter1, + int parameter2) {} + } + + @ExpectedGetParameterAnnotations("[[], [], []]") + @ExpectedParameterTypeAnnotations({ + "null", + "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=3)", + "null"}) + public class NestedClass3 { + public

NestedClass3(@MarkerTypeAnnotation(3) P parameter1, + int parameter2) {} + } + + @ExpectedGetParameterAnnotations("[[], [], []]") + @ExpectedParameterTypeAnnotations({ + "null", + "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=4)", + "null"}) + public class NestedClass4 { + public NestedClass4(@MarkerTypeAnnotation(4) P parameter1, + Q parameter2) {} + } + + @ExpectedGetParameterAnnotations("[]") + @ExpectedParameterTypeAnnotations({"null"}) + public static class StaticNestedClass0 { + public StaticNestedClass0() {} + } + + @ExpectedGetParameterAnnotations("[[]]") + @ExpectedParameterTypeAnnotations({ + "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=1)"}) + public static class StaticNestedClass1 { + public StaticNestedClass1(@MarkerTypeAnnotation(1) int parameter) {} + } + + @ExpectedGetParameterAnnotations("[[], []]") + @ExpectedParameterTypeAnnotations({ + "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=2)", + "null"}) + public static class StaticNestedClass2 { + public StaticNestedClass2(@MarkerTypeAnnotation(2) int parameter1, + int parameter2) {} + } + + @ExpectedGetParameterAnnotations("[[], []]") + @ExpectedParameterTypeAnnotations({ + "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=3)", + "null"}) + public static class StaticNestedClass3 { + public

StaticNestedClass3(@MarkerTypeAnnotation(3) P parameter1, + int parameter2) {} + } + + @ExpectedGetParameterAnnotations("[[], []]") + @ExpectedParameterTypeAnnotations({ + "@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(value=4)", + "null"}) + public static class StaticNestedClass4 { + public StaticNestedClass4(@MarkerTypeAnnotation(4) P parameter1, + Q parameter2) {} + } + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + @interface MarkerTypeAnnotation { + int value(); + } + + /** + * String form of expected value of calling + * getParameterAnnotations on a constructor. + */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @interface ExpectedGetParameterAnnotations { + String value(); + } + + /** + * String form of expected value of calling + * getAnnotation(MarkerTypeAnnotation.class) on each element of the + * result of getParameters() on a constructor. + */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @interface ExpectedParameterTypeAnnotations { + String[] value(); + } +}