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 return Annotation[] so I don't have to do this in C.
50 * @author Mathias Panzenböck
52 * @throws AnnotationFormatError if an annotation is found to be
55 public static Annotation[] parseAnnotationsIntoArray(
56 byte[] rawAnnotations,
57 ConstantPool constPool,
59 Map<Class, Annotation> annotations = parseAnnotations(rawAnnotations, constPool, container);
60 return annotations.values().toArray(EMPTY_ANNOTATIONS_ARRAY);
64 * Parses parameter annotations.
66 * @author Mathias Panzenböck
68 * @throws AnnotationFormatError if an annotation is found to be
71 public static Annotation[][] parseParameterAnnotations(
72 byte[] parameterAnnotations,
73 ConstantPool constPool,
76 if (parameterAnnotations == null)
77 return new Annotation[numParameters][0];
79 Annotation[][] result = parseParameterAnnotations(
80 parameterAnnotations, constPool, container);
82 if (result.length != numParameters)
83 throw new AnnotationFormatError(
84 "Parameter annotations don't match number of parameters (count was " +
85 result.length + " but should be " + numParameters + ").");
90 * Parses the annotation default value of the supplied method.
91 * This method is basically copied from OpenJDKs
92 * java.lang.reflect.Method.getAnnotationDefault()
94 * @author Mathias Panzenböck
96 * @throws AnnotationFormatError if an annotation is found to be
99 public static Object parseAnnotationDefault(Method method,
100 byte[] annotationDefault,
101 ConstantPool constPool) {
102 if (annotationDefault == null)
105 Class memberType = AnnotationType.invocationHandlerReturnType(
106 method.getReturnType());
108 Object result = AnnotationParser.parseMemberValue(
109 memberType, ByteBuffer.wrap(annotationDefault),
110 constPool, method.getDeclaringClass());
112 if (result instanceof sun.reflect.annotation.ExceptionProxy)
113 throw new AnnotationFormatError("Invalid default: " + method);
119 * Parses the annotations described by the specified byte array.
120 * resolving constant references in the specified constant pool.
121 * The array must contain an array of annotations as described
122 * in the RuntimeVisibleAnnotations_attribute:
124 * u2 num_annotations;
125 * annotation annotations[num_annotations];
127 * @throws AnnotationFormatError if an annotation is found to be
130 public static Map<Class, Annotation> parseAnnotations(
131 byte[] rawAnnotations,
132 ConstantPool constPool,
134 if (rawAnnotations == null)
135 return Collections.emptyMap();
138 return parseAnnotations2(rawAnnotations, constPool, container);
139 } catch(BufferUnderflowException e) {
140 throw new AnnotationFormatError("Unexpected end of annotations.");
141 } catch(IllegalArgumentException e) {
142 // Type mismatch in constant pool
143 throw new AnnotationFormatError(e);
147 private static Map<Class, Annotation> parseAnnotations2(
148 byte[] rawAnnotations,
149 ConstantPool constPool,
151 Map<Class, Annotation> result = new LinkedHashMap<Class, Annotation>();
152 ByteBuffer buf = ByteBuffer.wrap(rawAnnotations);
153 int numAnnotations = buf.getShort() & 0xFFFF;
154 for (int i = 0; i < numAnnotations; i++) {
155 Annotation a = parseAnnotation(buf, constPool, container, false);
157 Class klass = a.annotationType();
158 AnnotationType type = AnnotationType.getInstance(klass);
159 if (type.retention() == RetentionPolicy.RUNTIME)
160 if (result.put(klass, a) != null)
161 throw new AnnotationFormatError(
162 "Duplicate annotation for class: "+klass+": " + a);
169 * Parses the parameter annotations described by the specified byte array.
170 * resolving constant references in the specified constant pool.
171 * The array must contain an array of annotations as described
172 * in the RuntimeVisibleParameterAnnotations_attribute:
176 * u2 num_annotations;
177 * annotation annotations[num_annotations];
178 * } parameter_annotations[num_parameters];
180 * Unlike parseAnnotations, rawAnnotations must not be null!
181 * A null value must be handled by the caller. This is so because
182 * we cannot determine the number of parameters if rawAnnotations
183 * is null. Also, the caller should check that the number
184 * of parameters indicated by the return value of this method
185 * matches the actual number of method parameters. A mismatch
186 * indicates that an AnnotationFormatError should be thrown.
188 * @throws AnnotationFormatError if an annotation is found to be
191 public static Annotation[][] parseParameterAnnotations(
192 byte[] rawAnnotations,
193 ConstantPool constPool,
196 return parseParameterAnnotations2(rawAnnotations, constPool, container);
197 } catch(BufferUnderflowException e) {
198 throw new AnnotationFormatError(
199 "Unexpected end of parameter annotations.");
200 } catch(IllegalArgumentException e) {
201 // Type mismatch in constant pool
202 throw new AnnotationFormatError(e);
206 private static Annotation[][] parseParameterAnnotations2(
207 byte[] rawAnnotations,
208 ConstantPool constPool,
210 ByteBuffer buf = ByteBuffer.wrap(rawAnnotations);
211 int numParameters = buf.get() & 0xFF;
212 Annotation[][] result = new Annotation[numParameters][];
214 for (int i = 0; i < numParameters; i++) {
215 int numAnnotations = buf.getShort() & 0xFFFF;
216 List<Annotation> annotations =
217 new ArrayList<Annotation>(numAnnotations);
218 for (int j = 0; j < numAnnotations; j++) {
219 Annotation a = parseAnnotation(buf, constPool, container, false);
221 AnnotationType type = AnnotationType.getInstance(
223 if (type.retention() == RetentionPolicy.RUNTIME)
227 result[i] = annotations.toArray(EMPTY_ANNOTATIONS_ARRAY);
232 private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY =
236 * Parses the annotation at the current position in the specified
237 * byte buffer, resolving constant references in the specified constant
238 * pool. The cursor of the byte buffer must point to an "annotation
239 * structure" as described in the RuntimeVisibleAnnotations_attribute:
243 * u2 num_member_value_pairs;
244 * { u2 member_name_index;
245 * member_value value;
246 * } member_value_pairs[num_member_value_pairs];
250 * Returns the annotation, or null if the annotation's type cannot
251 * be found by the VM, or is not a valid annotation type.
253 * @param exceptionOnMissingAnnotationClass if true, throw
254 * TypeNotPresentException if a referenced annotation type is not
255 * available at runtime
257 private static Annotation parseAnnotation(ByteBuffer buf,
258 ConstantPool constPool,
260 boolean exceptionOnMissingAnnotationClass) {
261 int typeIndex = buf.getShort() & 0xFFFF;
262 Class annotationClass = null;
263 String sig = "[unknown]";
266 sig = constPool.getUTF8At(typeIndex);
267 annotationClass = parseSig(sig, container);
268 } catch (IllegalArgumentException ex) {
269 // support obsolete early jsr175 format class files
270 annotationClass = constPool.getClassAt(typeIndex);
272 } catch (NoClassDefFoundError e) {
273 if (exceptionOnMissingAnnotationClass)
274 // note: at this point sig is "[unknown]" or VM-style
275 // name instead of a binary name
276 throw new TypeNotPresentException(sig, e);
277 skipAnnotation(buf, false);
280 catch (TypeNotPresentException e) {
281 if (exceptionOnMissingAnnotationClass)
283 skipAnnotation(buf, false);
286 AnnotationType type = null;
288 type = AnnotationType.getInstance(annotationClass);
289 } catch (IllegalArgumentException e) {
290 skipAnnotation(buf, false);
294 Map<String, Class> memberTypes = type.memberTypes();
295 Map<String, Object> memberValues =
296 new LinkedHashMap<String, Object>(type.memberDefaults());
298 int numMembers = buf.getShort() & 0xFFFF;
299 for (int i = 0; i < numMembers; i++) {
300 int memberNameIndex = buf.getShort() & 0xFFFF;
301 String memberName = constPool.getUTF8At(memberNameIndex);
302 Class memberType = memberTypes.get(memberName);
304 if (memberType == null) {
305 // Member is no longer present in annotation type; ignore it
306 skipMemberValue(buf);
308 Object value = parseMemberValue(memberType, buf, constPool, container);
309 if (value instanceof AnnotationTypeMismatchExceptionProxy)
310 ((AnnotationTypeMismatchExceptionProxy) value).
311 setMember(type.members().get(memberName));
312 memberValues.put(memberName, value);
315 return annotationForMap(annotationClass, memberValues);
319 * Returns an annotation of the given type backed by the given
320 * member -> value map.
322 public static Annotation annotationForMap(
323 Class type, Map<String, Object> memberValues)
325 return (Annotation) Proxy.newProxyInstance(
326 type.getClassLoader(), new Class[] { type },
327 new AnnotationInvocationHandler(type, memberValues));
331 * Parses the annotation member value at the current position in the
332 * specified byte buffer, resolving constant references in the specified
333 * constant pool. The cursor of the byte buffer must point to a
334 * "member_value structure" as described in the
335 * RuntimeVisibleAnnotations_attribute:
340 * u2 const_value_index;
342 * u2 type_name_index;
343 * u2 const_name_index;
344 * } enum_const_value;
345 * u2 class_info_index;
346 * annotation annotation_value;
349 * member_value values[num_values];
354 * The member must be of the indicated type. If it is not, this
355 * method returns an AnnotationTypeMismatchExceptionProxy.
357 public static Object parseMemberValue(Class memberType, ByteBuffer buf,
358 ConstantPool constPool,
360 Object result = null;
364 return parseEnumValue(memberType, buf, constPool, container);
366 result = parseClassValue(buf, constPool, container);
369 result = parseAnnotation(buf, constPool, container, true);
372 return parseArray(memberType, buf, constPool, container);
374 result = parseConst(tag, buf, constPool);
377 if (!(result instanceof ExceptionProxy) &&
378 !memberType.isInstance(result))
379 result = new AnnotationTypeMismatchExceptionProxy(
380 result.getClass() + "[" + result + "]");
385 * Parses the primitive or String annotation member value indicated by
386 * the specified tag byte at the current position in the specified byte
387 * buffer, resolving constant reference in the specified constant pool.
388 * The cursor of the byte buffer must point to an annotation member value
389 * of the type indicated by the specified tag, as described in the
390 * RuntimeVisibleAnnotations_attribute:
392 * u2 const_value_index;
394 private static Object parseConst(int tag,
395 ByteBuffer buf, ConstantPool constPool) {
396 int constIndex = buf.getShort() & 0xFFFF;
399 return Byte.valueOf((byte) constPool.getIntAt(constIndex));
401 return Character.valueOf((char) constPool.getIntAt(constIndex));
403 return Double.valueOf(constPool.getDoubleAt(constIndex));
405 return Float.valueOf(constPool.getFloatAt(constIndex));
407 return Integer.valueOf(constPool.getIntAt(constIndex));
409 return Long.valueOf(constPool.getLongAt(constIndex));
411 return Short.valueOf((short) constPool.getIntAt(constIndex));
413 return Boolean.valueOf(constPool.getIntAt(constIndex) != 0);
415 return constPool.getUTF8At(constIndex);
417 throw new AnnotationFormatError(
418 "Invalid member-value tag in annotation: " + tag);
423 * Parses the Class member value at the current position in the
424 * specified byte buffer, resolving constant references in the specified
425 * constant pool. The cursor of the byte buffer must point to a "class
426 * info index" as described in the RuntimeVisibleAnnotations_attribute:
428 * u2 class_info_index;
430 private static Object parseClassValue(ByteBuffer buf,
431 ConstantPool constPool,
433 int classIndex = buf.getShort() & 0xFFFF;
436 String sig = constPool.getUTF8At(classIndex);
437 return parseSig(sig, container);
438 } catch (IllegalArgumentException ex) {
439 // support obsolete early jsr175 format class files
440 return constPool.getClassAt(classIndex);
442 } catch (NoClassDefFoundError e) {
443 return new TypeNotPresentExceptionProxy("[unknown]", e);
445 catch (TypeNotPresentException e) {
446 return new TypeNotPresentExceptionProxy(e.typeName(), e.getCause());
451 * Parses a return type signature and returns the according Class object.
453 private static Class<?> parseSig(String sig, Class container) {
454 if (sig.equals("V")) {
458 return toClass(new FieldSignatureParser(container, sig).getFieldType());
462 static Class<?> toClass(Type o) {
463 if (o instanceof GenericArrayType)
464 return Array.newInstance(toClass(((GenericArrayType)o).getGenericComponentType()),
471 * Parses the enum constant member value at the current position in the
472 * specified byte buffer, resolving constant references in the specified
473 * constant pool. The cursor of the byte buffer must point to a
474 * "enum_const_value structure" as described in the
475 * RuntimeVisibleAnnotations_attribute:
478 * u2 type_name_index;
479 * u2 const_name_index;
480 * } enum_const_value;
482 private static Object parseEnumValue(Class enumType, ByteBuffer buf,
483 ConstantPool constPool,
485 int typeNameIndex = buf.getShort() & 0xFFFF;
486 String typeName = constPool.getUTF8At(typeNameIndex);
487 int constNameIndex = buf.getShort() & 0xFFFF;
488 String constName = constPool.getUTF8At(constNameIndex);
490 if (!typeName.endsWith(";")) {
491 // support now-obsolete early jsr175-format class files.
492 if (!enumType.getName().equals(typeName))
493 return new AnnotationTypeMismatchExceptionProxy(
494 typeName + "." + constName);
495 } else if (enumType != parseSig(typeName, container)) {
496 return new AnnotationTypeMismatchExceptionProxy(
497 typeName + "." + constName);
501 return Enum.valueOf(enumType, constName);
502 } catch(IllegalArgumentException e) {
503 return new EnumConstantNotPresentExceptionProxy(
504 (Class<? extends Enum>)enumType, constName);
509 * Parses the array value at the current position in the specified byte
510 * buffer, resolving constant references in the specified constant pool.
511 * The cursor of the byte buffer must point to an array value struct
512 * as specified in the RuntimeVisibleAnnotations_attribute:
516 * member_value values[num_values];
519 * If the array values do not match arrayType, an
520 * AnnotationTypeMismatchExceptionProxy will be returned.
522 private static Object parseArray(Class arrayType,
524 ConstantPool constPool,
526 int length = buf.getShort() & 0xFFFF; // Number of array components
527 Class componentType = arrayType.getComponentType();
529 if (componentType == byte.class) {
530 return parseByteArray(length, buf, constPool);
531 } else if (componentType == char.class) {
532 return parseCharArray(length, buf, constPool);
533 } else if (componentType == double.class) {
534 return parseDoubleArray(length, buf, constPool);
535 } else if (componentType == float.class) {
536 return parseFloatArray(length, buf, constPool);
537 } else if (componentType == int.class) {
538 return parseIntArray(length, buf, constPool);
539 } else if (componentType == long.class) {
540 return parseLongArray(length, buf, constPool);
541 } else if (componentType == short.class) {
542 return parseShortArray(length, buf, constPool);
543 } else if (componentType == boolean.class) {
544 return parseBooleanArray(length, buf, constPool);
545 } else if (componentType == String.class) {
546 return parseStringArray(length, buf, constPool);
547 } else if (componentType == Class.class) {
548 return parseClassArray(length, buf, constPool, container);
549 } else if (componentType.isEnum()) {
550 return parseEnumArray(length, componentType, buf,
551 constPool, container);
553 assert componentType.isAnnotation();
554 return parseAnnotationArray(length, componentType, buf,
555 constPool, container);
559 private static Object parseByteArray(int length,
560 ByteBuffer buf, ConstantPool constPool) {
561 byte[] result = new byte[length];
562 boolean typeMismatch = false;
565 for (int i = 0; i < length; i++) {
568 int index = buf.getShort() & 0xFFFF;
569 result[i] = (byte) constPool.getIntAt(index);
571 skipMemberValue(tag, buf);
575 return typeMismatch ? exceptionProxy(tag) : result;
578 private static Object parseCharArray(int length,
579 ByteBuffer buf, ConstantPool constPool) {
580 char[] result = new char[length];
581 boolean typeMismatch = false;
584 for (int i = 0; i < length; i++) {
587 int index = buf.getShort() & 0xFFFF;
588 result[i] = (char) constPool.getIntAt(index);
590 skipMemberValue(tag, buf);
594 return typeMismatch ? exceptionProxy(tag) : result;
597 private static Object parseDoubleArray(int length,
598 ByteBuffer buf, ConstantPool constPool) {
599 double[] result = new double[length];
600 boolean typeMismatch = false;
603 for (int i = 0; i < length; i++) {
606 int index = buf.getShort() & 0xFFFF;
607 result[i] = constPool.getDoubleAt(index);
609 skipMemberValue(tag, buf);
613 return typeMismatch ? exceptionProxy(tag) : result;
616 private static Object parseFloatArray(int length,
617 ByteBuffer buf, ConstantPool constPool) {
618 float[] result = new float[length];
619 boolean typeMismatch = false;
622 for (int i = 0; i < length; i++) {
625 int index = buf.getShort() & 0xFFFF;
626 result[i] = constPool.getFloatAt(index);
628 skipMemberValue(tag, buf);
632 return typeMismatch ? exceptionProxy(tag) : result;
635 private static Object parseIntArray(int length,
636 ByteBuffer buf, ConstantPool constPool) {
637 int[] result = new int[length];
638 boolean typeMismatch = false;
641 for (int i = 0; i < length; i++) {
644 int index = buf.getShort() & 0xFFFF;
645 result[i] = constPool.getIntAt(index);
647 skipMemberValue(tag, buf);
651 return typeMismatch ? exceptionProxy(tag) : result;
654 private static Object parseLongArray(int length,
655 ByteBuffer buf, ConstantPool constPool) {
656 long[] result = new long[length];
657 boolean typeMismatch = false;
660 for (int i = 0; i < length; i++) {
663 int index = buf.getShort() & 0xFFFF;
664 result[i] = constPool.getLongAt(index);
666 skipMemberValue(tag, buf);
670 return typeMismatch ? exceptionProxy(tag) : result;
673 private static Object parseShortArray(int length,
674 ByteBuffer buf, ConstantPool constPool) {
675 short[] result = new short[length];
676 boolean typeMismatch = false;
679 for (int i = 0; i < length; i++) {
682 int index = buf.getShort() & 0xFFFF;
683 result[i] = (short) constPool.getIntAt(index);
685 skipMemberValue(tag, buf);
689 return typeMismatch ? exceptionProxy(tag) : result;
692 private static Object parseBooleanArray(int length,
693 ByteBuffer buf, ConstantPool constPool) {
694 boolean[] result = new boolean[length];
695 boolean typeMismatch = false;
698 for (int i = 0; i < length; i++) {
701 int index = buf.getShort() & 0xFFFF;
702 result[i] = (constPool.getIntAt(index) != 0);
704 skipMemberValue(tag, buf);
708 return typeMismatch ? exceptionProxy(tag) : result;
711 private static Object parseStringArray(int length,
712 ByteBuffer buf, ConstantPool constPool) {
713 String[] result = new String[length];
714 boolean typeMismatch = false;
717 for (int i = 0; i < length; i++) {
720 int index = buf.getShort() & 0xFFFF;
721 result[i] = constPool.getUTF8At(index);
723 skipMemberValue(tag, buf);
727 return typeMismatch ? exceptionProxy(tag) : result;
730 private static Object parseClassArray(int length,
732 ConstantPool constPool,
734 Object[] result = new Class[length];
735 boolean typeMismatch = false;
738 for (int i = 0; i < length; i++) {
741 result[i] = parseClassValue(buf, constPool, container);
743 skipMemberValue(tag, buf);
747 return typeMismatch ? exceptionProxy(tag) : result;
750 private static Object parseEnumArray(int length, Class enumType,
752 ConstantPool constPool,
754 Object[] result = (Object[]) Array.newInstance(enumType, length);
755 boolean typeMismatch = false;
758 for (int i = 0; i < length; i++) {
761 result[i] = parseEnumValue(enumType, buf, constPool, container);
763 skipMemberValue(tag, buf);
767 return typeMismatch ? exceptionProxy(tag) : result;
770 private static Object parseAnnotationArray(int length,
771 Class annotationType,
773 ConstantPool constPool,
775 Object[] result = (Object[]) Array.newInstance(annotationType, length);
776 boolean typeMismatch = false;
779 for (int i = 0; i < length; i++) {
782 result[i] = parseAnnotation(buf, constPool, container, true);
784 skipMemberValue(tag, buf);
788 return typeMismatch ? exceptionProxy(tag) : result;
792 * Return an appropriate exception proxy for a mismatching array
793 * annotation where the erroneous array has the specified tag.
795 private static ExceptionProxy exceptionProxy(int tag) {
796 return new AnnotationTypeMismatchExceptionProxy(
797 "Array with component tag: " + tag);
801 * Skips the annotation at the current position in the specified
802 * byte buffer. The cursor of the byte buffer must point to
803 * an "annotation structure" OR two bytes into an annotation
804 * structure (i.e., after the type index).
806 * @parameter complete true if the byte buffer points to the beginning
807 * of an annotation structure (rather than two bytes in).
809 private static void skipAnnotation(ByteBuffer buf, boolean complete) {
811 buf.getShort(); // Skip type index
812 int numMembers = buf.getShort() & 0xFFFF;
813 for (int i = 0; i < numMembers; i++) {
814 buf.getShort(); // Skip memberNameIndex
815 skipMemberValue(buf);
820 * Skips the annotation member value at the current position in the
821 * specified byte buffer. The cursor of the byte buffer must point to a
822 * "member_value structure."
824 private static void skipMemberValue(ByteBuffer buf) {
826 skipMemberValue(tag, buf);
830 * Skips the annotation member value at the current position in the
831 * specified byte buffer. The cursor of the byte buffer must point
832 * immediately after the tag in a "member_value structure."
834 private static void skipMemberValue(int tag, ByteBuffer buf) {
836 case 'e': // Enum value
837 buf.getInt(); // (Two shorts, actually.)
840 skipAnnotation(buf, true);
846 // Class, primitive, or String
852 * Skips the array value at the current position in the specified byte
853 * buffer. The cursor of the byte buffer must point to an array value
856 private static void skipArray(ByteBuffer buf) {
857 int length = buf.getShort() & 0xFFFF;
858 for (int i = 0; i < length; i++)
859 skipMemberValue(buf);