2 * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
26 package sun.reflect.annotation;
28 import java.lang.annotation.*;
30 import java.nio.ByteBuffer;
31 import java.nio.BufferUnderflowException;
32 import java.lang.reflect.*;
33 import sun.reflect.ConstantPool;
35 import gnu.java.lang.reflect.FieldSignatureParser;
39 * Parser for Java programming language annotations. Translates
40 * annotation byte streams emitted by compiler into annotation objects.
45 public class AnnotationParser {
47 * Parses the annotations described by the passed byte array,
48 * but returns Annotation[] so I don't have to do this in C.
50 * @author Mathias Panzenböck
52 * @param rawAnnotations are the unparsed annotations
53 * @param constPool is the constant pool of the declaring class
54 * @param container is the containing class
55 * @return the parsed annotations in an array
56 * @throws AnnotationFormatError if an annotation is found to be
59 public static Annotation[] parseAnnotationsIntoArray(
60 byte[] rawAnnotations,
61 ConstantPool constPool,
63 Map<Class, Annotation> annotations = parseAnnotations(rawAnnotations, constPool, container);
64 return annotations.values().toArray(EMPTY_ANNOTATIONS_ARRAY);
68 * Parses parameter annotations.
70 * @author Mathias Panzenböck
72 * @param parameterAnnotations are the unparsed parameter annotations
73 * @param constPool is the constant pool of the declaring class
74 * @param container is the containing class
75 * @return the parsed parameter annotations in an array 2 dimensional array
76 * @throws AnnotationFormatError if an annotation is found to be
79 public static Annotation[][] parseParameterAnnotations(
80 byte[] parameterAnnotations,
81 ConstantPool constPool,
84 if (parameterAnnotations == null)
85 return new Annotation[numParameters][0];
87 Annotation[][] result = parseParameterAnnotations(
88 parameterAnnotations, constPool, container);
90 if (result.length != numParameters)
91 throw new AnnotationFormatError(
92 "Parameter annotations don't match number of parameters (count was " +
93 result.length + " but should be " + numParameters + ").");
98 * Parses the annotation default value of the supplied method.
99 * This method is basically copied from OpenJDKs
100 * java.lang.reflect.Method.getAnnotationDefault()
102 * @author Mathias Panzenböck
104 * @param method represents the method for which the annotation default value has to be parsed
105 * @param annotationDefault is the unparsed annotation default value
106 * @param constPool is the constant pool of the declaring class
107 * @return the parsed annotation default value (boxed, if it's a primitive type)
108 * @throws AnnotationFormatError if an annotation is found to be
111 public static Object parseAnnotationDefault(Method method,
112 byte[] annotationDefault,
113 ConstantPool constPool) {
114 if (annotationDefault == null)
117 Class memberType = AnnotationType.invocationHandlerReturnType(
118 method.getReturnType());
120 Object result = AnnotationParser.parseMemberValue(
121 memberType, ByteBuffer.wrap(annotationDefault),
122 constPool, method.getDeclaringClass());
124 if (result instanceof sun.reflect.annotation.ExceptionProxy)
125 throw new AnnotationFormatError("Invalid default: " + method);
131 * Parses the annotations described by the specified byte array.
132 * resolving constant references in the specified constant pool.
133 * The array must contain an array of annotations as described
134 * in the RuntimeVisibleAnnotations_attribute:
136 * u2 num_annotations;
137 * annotation annotations[num_annotations];
139 * @throws AnnotationFormatError if an annotation is found to be
142 public static Map<Class, Annotation> parseAnnotations(
143 byte[] rawAnnotations,
144 ConstantPool constPool,
146 if (rawAnnotations == null)
147 return Collections.emptyMap();
150 return parseAnnotations2(rawAnnotations, constPool, container);
151 } catch(BufferUnderflowException e) {
152 throw new AnnotationFormatError("Unexpected end of annotations.");
153 } catch(IllegalArgumentException e) {
154 // Type mismatch in constant pool
155 throw new AnnotationFormatError(e);
159 private static Map<Class, Annotation> parseAnnotations2(
160 byte[] rawAnnotations,
161 ConstantPool constPool,
163 Map<Class, Annotation> result = new LinkedHashMap<Class, Annotation>();
164 ByteBuffer buf = ByteBuffer.wrap(rawAnnotations);
165 int numAnnotations = buf.getShort() & 0xFFFF;
166 for (int i = 0; i < numAnnotations; i++) {
167 Annotation a = parseAnnotation(buf, constPool, container, false);
169 Class klass = a.annotationType();
170 AnnotationType type = AnnotationType.getInstance(klass);
171 if (type.retention() == RetentionPolicy.RUNTIME)
172 if (result.put(klass, a) != null)
173 throw new AnnotationFormatError(
174 "Duplicate annotation for class: "+klass+": " + a);
181 * Parses the parameter annotations described by the specified byte array.
182 * resolving constant references in the specified constant pool.
183 * The array must contain an array of annotations as described
184 * in the RuntimeVisibleParameterAnnotations_attribute:
188 * u2 num_annotations;
189 * annotation annotations[num_annotations];
190 * } parameter_annotations[num_parameters];
192 * Unlike parseAnnotations, rawAnnotations must not be null!
193 * A null value must be handled by the caller. This is so because
194 * we cannot determine the number of parameters if rawAnnotations
195 * is null. Also, the caller should check that the number
196 * of parameters indicated by the return value of this method
197 * matches the actual number of method parameters. A mismatch
198 * indicates that an AnnotationFormatError should be thrown.
200 * @throws AnnotationFormatError if an annotation is found to be
203 public static Annotation[][] parseParameterAnnotations(
204 byte[] rawAnnotations,
205 ConstantPool constPool,
208 return parseParameterAnnotations2(rawAnnotations, constPool, container);
209 } catch(BufferUnderflowException e) {
210 throw new AnnotationFormatError(
211 "Unexpected end of parameter annotations.");
212 } catch(IllegalArgumentException e) {
213 // Type mismatch in constant pool
214 throw new AnnotationFormatError(e);
218 private static Annotation[][] parseParameterAnnotations2(
219 byte[] rawAnnotations,
220 ConstantPool constPool,
222 ByteBuffer buf = ByteBuffer.wrap(rawAnnotations);
223 int numParameters = buf.get() & 0xFF;
224 Annotation[][] result = new Annotation[numParameters][];
226 for (int i = 0; i < numParameters; i++) {
227 int numAnnotations = buf.getShort() & 0xFFFF;
228 List<Annotation> annotations =
229 new ArrayList<Annotation>(numAnnotations);
230 for (int j = 0; j < numAnnotations; j++) {
231 Annotation a = parseAnnotation(buf, constPool, container, false);
233 AnnotationType type = AnnotationType.getInstance(
235 if (type.retention() == RetentionPolicy.RUNTIME)
239 result[i] = annotations.toArray(EMPTY_ANNOTATIONS_ARRAY);
244 private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY =
248 * Parses the annotation at the current position in the specified
249 * byte buffer, resolving constant references in the specified constant
250 * pool. The cursor of the byte buffer must point to an "annotation
251 * structure" as described in the RuntimeVisibleAnnotations_attribute:
255 * u2 num_member_value_pairs;
256 * { u2 member_name_index;
257 * member_value value;
258 * } member_value_pairs[num_member_value_pairs];
262 * Returns the annotation, or null if the annotation's type cannot
263 * be found by the VM, or is not a valid annotation type.
265 * @param exceptionOnMissingAnnotationClass if true, throw
266 * TypeNotPresentException if a referenced annotation type is not
267 * available at runtime
269 private static Annotation parseAnnotation(ByteBuffer buf,
270 ConstantPool constPool,
272 boolean exceptionOnMissingAnnotationClass) {
273 int typeIndex = buf.getShort() & 0xFFFF;
274 Class annotationClass = null;
275 String sig = "[unknown]";
278 sig = constPool.getUTF8At(typeIndex);
279 annotationClass = parseSig(sig, container);
280 } catch (IllegalArgumentException ex) {
281 // support obsolete early jsr175 format class files
282 annotationClass = constPool.getClassAt(typeIndex);
284 } catch (NoClassDefFoundError e) {
285 if (exceptionOnMissingAnnotationClass)
286 // note: at this point sig is "[unknown]" or VM-style
287 // name instead of a binary name
288 throw new TypeNotPresentException(sig, e);
289 skipAnnotation(buf, false);
292 catch (TypeNotPresentException e) {
293 if (exceptionOnMissingAnnotationClass)
295 skipAnnotation(buf, false);
298 AnnotationType type = null;
300 type = AnnotationType.getInstance(annotationClass);
301 } catch (IllegalArgumentException e) {
302 skipAnnotation(buf, false);
306 Map<String, Class> memberTypes = type.memberTypes();
307 Map<String, Object> memberValues =
308 new LinkedHashMap<String, Object>(type.memberDefaults());
310 int numMembers = buf.getShort() & 0xFFFF;
311 for (int i = 0; i < numMembers; i++) {
312 int memberNameIndex = buf.getShort() & 0xFFFF;
313 String memberName = constPool.getUTF8At(memberNameIndex);
314 Class memberType = memberTypes.get(memberName);
316 if (memberType == null) {
317 // Member is no longer present in annotation type; ignore it
318 skipMemberValue(buf);
320 Object value = parseMemberValue(memberType, buf, constPool, container);
321 if (value instanceof AnnotationTypeMismatchExceptionProxy)
322 ((AnnotationTypeMismatchExceptionProxy) value).
323 setMember(type.members().get(memberName));
324 memberValues.put(memberName, value);
327 return annotationForMap(annotationClass, memberValues);
331 * Returns an annotation of the given type backed by the given
332 * member -> value map.
334 public static Annotation annotationForMap(
335 Class type, Map<String, Object> memberValues)
337 return (Annotation) Proxy.newProxyInstance(
338 type.getClassLoader(), new Class[] { type },
339 new AnnotationInvocationHandler(type, memberValues));
343 * Parses the annotation member value at the current position in the
344 * specified byte buffer, resolving constant references in the specified
345 * constant pool. The cursor of the byte buffer must point to a
346 * "member_value structure" as described in the
347 * RuntimeVisibleAnnotations_attribute:
352 * u2 const_value_index;
354 * u2 type_name_index;
355 * u2 const_name_index;
356 * } enum_const_value;
357 * u2 class_info_index;
358 * annotation annotation_value;
361 * member_value values[num_values];
366 * The member must be of the indicated type. If it is not, this
367 * method returns an AnnotationTypeMismatchExceptionProxy.
369 public static Object parseMemberValue(Class memberType, ByteBuffer buf,
370 ConstantPool constPool,
372 Object result = null;
376 return parseEnumValue(memberType, buf, constPool, container);
378 result = parseClassValue(buf, constPool, container);
381 result = parseAnnotation(buf, constPool, container, true);
384 return parseArray(memberType, buf, constPool, container);
386 result = parseConst(tag, buf, constPool);
389 if (!(result instanceof ExceptionProxy) &&
390 !memberType.isInstance(result))
391 result = new AnnotationTypeMismatchExceptionProxy(
392 result.getClass() + "[" + result + "]");
397 * Parses the primitive or String annotation member value indicated by
398 * the specified tag byte at the current position in the specified byte
399 * buffer, resolving constant reference in the specified constant pool.
400 * The cursor of the byte buffer must point to an annotation member value
401 * of the type indicated by the specified tag, as described in the
402 * RuntimeVisibleAnnotations_attribute:
404 * u2 const_value_index;
406 private static Object parseConst(int tag,
407 ByteBuffer buf, ConstantPool constPool) {
408 int constIndex = buf.getShort() & 0xFFFF;
411 return Byte.valueOf((byte) constPool.getIntAt(constIndex));
413 return Character.valueOf((char) constPool.getIntAt(constIndex));
415 return Double.valueOf(constPool.getDoubleAt(constIndex));
417 return Float.valueOf(constPool.getFloatAt(constIndex));
419 return Integer.valueOf(constPool.getIntAt(constIndex));
421 return Long.valueOf(constPool.getLongAt(constIndex));
423 return Short.valueOf((short) constPool.getIntAt(constIndex));
425 return Boolean.valueOf(constPool.getIntAt(constIndex) != 0);
427 return constPool.getUTF8At(constIndex);
429 throw new AnnotationFormatError(
430 "Invalid member-value tag in annotation: " + tag);
435 * Parses the Class member value at the current position in the
436 * specified byte buffer, resolving constant references in the specified
437 * constant pool. The cursor of the byte buffer must point to a "class
438 * info index" as described in the RuntimeVisibleAnnotations_attribute:
440 * u2 class_info_index;
442 private static Object parseClassValue(ByteBuffer buf,
443 ConstantPool constPool,
445 int classIndex = buf.getShort() & 0xFFFF;
448 String sig = constPool.getUTF8At(classIndex);
449 return parseSig(sig, container);
450 } catch (IllegalArgumentException ex) {
451 // support obsolete early jsr175 format class files
452 return constPool.getClassAt(classIndex);
454 } catch (NoClassDefFoundError e) {
455 return new TypeNotPresentExceptionProxy("[unknown]", e);
457 catch (TypeNotPresentException e) {
458 return new TypeNotPresentExceptionProxy(e.typeName(), e.getCause());
463 * Parses a return type signature and returns the according Class object.
465 private static Class<?> parseSig(String sig, Class container) {
466 if (sig.equals("V")) {
470 return toClass(new FieldSignatureParser(container, sig).getFieldType());
474 static Class<?> toClass(Type o) {
475 if (o instanceof GenericArrayType)
476 return Array.newInstance(toClass(((GenericArrayType)o).getGenericComponentType()),
483 * Parses the enum constant member value at the current position in the
484 * specified byte buffer, resolving constant references in the specified
485 * constant pool. The cursor of the byte buffer must point to a
486 * "enum_const_value structure" as described in the
487 * RuntimeVisibleAnnotations_attribute:
490 * u2 type_name_index;
491 * u2 const_name_index;
492 * } enum_const_value;
494 private static Object parseEnumValue(Class enumType, ByteBuffer buf,
495 ConstantPool constPool,
497 int typeNameIndex = buf.getShort() & 0xFFFF;
498 String typeName = constPool.getUTF8At(typeNameIndex);
499 int constNameIndex = buf.getShort() & 0xFFFF;
500 String constName = constPool.getUTF8At(constNameIndex);
502 if (!typeName.endsWith(";")) {
503 // support now-obsolete early jsr175-format class files.
504 if (!enumType.getName().equals(typeName))
505 return new AnnotationTypeMismatchExceptionProxy(
506 typeName + "." + constName);
507 } else if (enumType != parseSig(typeName, container)) {
508 return new AnnotationTypeMismatchExceptionProxy(
509 typeName + "." + constName);
513 return Enum.valueOf(enumType, constName);
514 } catch(IllegalArgumentException e) {
515 return new EnumConstantNotPresentExceptionProxy(
516 (Class<? extends Enum>)enumType, constName);
521 * Parses the array value at the current position in the specified byte
522 * buffer, resolving constant references in the specified constant pool.
523 * The cursor of the byte buffer must point to an array value struct
524 * as specified in the RuntimeVisibleAnnotations_attribute:
528 * member_value values[num_values];
531 * If the array values do not match arrayType, an
532 * AnnotationTypeMismatchExceptionProxy will be returned.
534 private static Object parseArray(Class arrayType,
536 ConstantPool constPool,
538 int length = buf.getShort() & 0xFFFF; // Number of array components
539 Class componentType = arrayType.getComponentType();
541 if (componentType == byte.class) {
542 return parseByteArray(length, buf, constPool);
543 } else if (componentType == char.class) {
544 return parseCharArray(length, buf, constPool);
545 } else if (componentType == double.class) {
546 return parseDoubleArray(length, buf, constPool);
547 } else if (componentType == float.class) {
548 return parseFloatArray(length, buf, constPool);
549 } else if (componentType == int.class) {
550 return parseIntArray(length, buf, constPool);
551 } else if (componentType == long.class) {
552 return parseLongArray(length, buf, constPool);
553 } else if (componentType == short.class) {
554 return parseShortArray(length, buf, constPool);
555 } else if (componentType == boolean.class) {
556 return parseBooleanArray(length, buf, constPool);
557 } else if (componentType == String.class) {
558 return parseStringArray(length, buf, constPool);
559 } else if (componentType == Class.class) {
560 return parseClassArray(length, buf, constPool, container);
561 } else if (componentType.isEnum()) {
562 return parseEnumArray(length, componentType, buf,
563 constPool, container);
565 assert componentType.isAnnotation();
566 return parseAnnotationArray(length, componentType, buf,
567 constPool, container);
571 private static Object parseByteArray(int length,
572 ByteBuffer buf, ConstantPool constPool) {
573 byte[] result = new byte[length];
574 boolean typeMismatch = false;
577 for (int i = 0; i < length; i++) {
580 int index = buf.getShort() & 0xFFFF;
581 result[i] = (byte) constPool.getIntAt(index);
583 skipMemberValue(tag, buf);
587 return typeMismatch ? exceptionProxy(tag) : result;
590 private static Object parseCharArray(int length,
591 ByteBuffer buf, ConstantPool constPool) {
592 char[] result = new char[length];
593 boolean typeMismatch = false;
596 for (int i = 0; i < length; i++) {
599 int index = buf.getShort() & 0xFFFF;
600 result[i] = (char) constPool.getIntAt(index);
602 skipMemberValue(tag, buf);
606 return typeMismatch ? exceptionProxy(tag) : result;
609 private static Object parseDoubleArray(int length,
610 ByteBuffer buf, ConstantPool constPool) {
611 double[] result = new double[length];
612 boolean typeMismatch = false;
615 for (int i = 0; i < length; i++) {
618 int index = buf.getShort() & 0xFFFF;
619 result[i] = constPool.getDoubleAt(index);
621 skipMemberValue(tag, buf);
625 return typeMismatch ? exceptionProxy(tag) : result;
628 private static Object parseFloatArray(int length,
629 ByteBuffer buf, ConstantPool constPool) {
630 float[] result = new float[length];
631 boolean typeMismatch = false;
634 for (int i = 0; i < length; i++) {
637 int index = buf.getShort() & 0xFFFF;
638 result[i] = constPool.getFloatAt(index);
640 skipMemberValue(tag, buf);
644 return typeMismatch ? exceptionProxy(tag) : result;
647 private static Object parseIntArray(int length,
648 ByteBuffer buf, ConstantPool constPool) {
649 int[] result = new int[length];
650 boolean typeMismatch = false;
653 for (int i = 0; i < length; i++) {
656 int index = buf.getShort() & 0xFFFF;
657 result[i] = constPool.getIntAt(index);
659 skipMemberValue(tag, buf);
663 return typeMismatch ? exceptionProxy(tag) : result;
666 private static Object parseLongArray(int length,
667 ByteBuffer buf, ConstantPool constPool) {
668 long[] result = new long[length];
669 boolean typeMismatch = false;
672 for (int i = 0; i < length; i++) {
675 int index = buf.getShort() & 0xFFFF;
676 result[i] = constPool.getLongAt(index);
678 skipMemberValue(tag, buf);
682 return typeMismatch ? exceptionProxy(tag) : result;
685 private static Object parseShortArray(int length,
686 ByteBuffer buf, ConstantPool constPool) {
687 short[] result = new short[length];
688 boolean typeMismatch = false;
691 for (int i = 0; i < length; i++) {
694 int index = buf.getShort() & 0xFFFF;
695 result[i] = (short) constPool.getIntAt(index);
697 skipMemberValue(tag, buf);
701 return typeMismatch ? exceptionProxy(tag) : result;
704 private static Object parseBooleanArray(int length,
705 ByteBuffer buf, ConstantPool constPool) {
706 boolean[] result = new boolean[length];
707 boolean typeMismatch = false;
710 for (int i = 0; i < length; i++) {
713 int index = buf.getShort() & 0xFFFF;
714 result[i] = (constPool.getIntAt(index) != 0);
716 skipMemberValue(tag, buf);
720 return typeMismatch ? exceptionProxy(tag) : result;
723 private static Object parseStringArray(int length,
724 ByteBuffer buf, ConstantPool constPool) {
725 String[] result = new String[length];
726 boolean typeMismatch = false;
729 for (int i = 0; i < length; i++) {
732 int index = buf.getShort() & 0xFFFF;
733 result[i] = constPool.getUTF8At(index);
735 skipMemberValue(tag, buf);
739 return typeMismatch ? exceptionProxy(tag) : result;
742 private static Object parseClassArray(int length,
744 ConstantPool constPool,
746 Object[] result = new Class[length];
747 boolean typeMismatch = false;
750 for (int i = 0; i < length; i++) {
753 result[i] = parseClassValue(buf, constPool, container);
755 skipMemberValue(tag, buf);
759 return typeMismatch ? exceptionProxy(tag) : result;
762 private static Object parseEnumArray(int length, Class enumType,
764 ConstantPool constPool,
766 Object[] result = (Object[]) Array.newInstance(enumType, length);
767 boolean typeMismatch = false;
770 for (int i = 0; i < length; i++) {
773 result[i] = parseEnumValue(enumType, buf, constPool, container);
775 skipMemberValue(tag, buf);
779 return typeMismatch ? exceptionProxy(tag) : result;
782 private static Object parseAnnotationArray(int length,
783 Class annotationType,
785 ConstantPool constPool,
787 Object[] result = (Object[]) Array.newInstance(annotationType, length);
788 boolean typeMismatch = false;
791 for (int i = 0; i < length; i++) {
794 result[i] = parseAnnotation(buf, constPool, container, true);
796 skipMemberValue(tag, buf);
800 return typeMismatch ? exceptionProxy(tag) : result;
804 * Return an appropriate exception proxy for a mismatching array
805 * annotation where the erroneous array has the specified tag.
807 private static ExceptionProxy exceptionProxy(int tag) {
808 return new AnnotationTypeMismatchExceptionProxy(
809 "Array with component tag: " + tag);
813 * Skips the annotation at the current position in the specified
814 * byte buffer. The cursor of the byte buffer must point to
815 * an "annotation structure" OR two bytes into an annotation
816 * structure (i.e., after the type index).
818 * @parameter complete true if the byte buffer points to the beginning
819 * of an annotation structure (rather than two bytes in).
821 private static void skipAnnotation(ByteBuffer buf, boolean complete) {
823 buf.getShort(); // Skip type index
824 int numMembers = buf.getShort() & 0xFFFF;
825 for (int i = 0; i < numMembers; i++) {
826 buf.getShort(); // Skip memberNameIndex
827 skipMemberValue(buf);
832 * Skips the annotation member value at the current position in the
833 * specified byte buffer. The cursor of the byte buffer must point to a
834 * "member_value structure."
836 private static void skipMemberValue(ByteBuffer buf) {
838 skipMemberValue(tag, buf);
842 * Skips the annotation member value at the current position in the
843 * specified byte buffer. The cursor of the byte buffer must point
844 * immediately after the tag in a "member_value structure."
846 private static void skipMemberValue(int tag, ByteBuffer buf) {
848 case 'e': // Enum value
849 buf.getInt(); // (Two shorts, actually.)
852 skipAnnotation(buf, true);
858 // Class, primitive, or String
864 * Skips the array value at the current position in the specified byte
865 * buffer. The cursor of the byte buffer must point to an array value
868 private static void skipArray(ByteBuffer buf) {
869 int length = buf.getShort() & 0xFFFF;
870 for (int i = 0; i < length; i++)
871 skipMemberValue(buf);