c745ad3a3bde20ca5f707a1f7f8b53d8674d5e36
[cacao.git] / src / lib / gnu / sun / reflect / annotation / AnnotationParser.java
1 /*
2  * Copyright 2003-2005 Sun Microsystems, Inc.  All Rights Reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
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.
10  *
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).
16  *
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.
20  *
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
23  * have any questions.
24  */
25
26 package sun.reflect.annotation;
27
28 import java.lang.annotation.*;
29 import java.util.*;
30 import java.nio.ByteBuffer;
31 import java.nio.BufferUnderflowException;
32 import java.lang.reflect.*;
33 import sun.reflect.ConstantPool;
34
35 import gnu.java.lang.reflect.FieldSignatureParser;
36
37
38 /**
39  * Parser for Java programming language annotations.  Translates
40  * annotation byte streams emitted by compiler into annotation objects.
41  *
42  * @author  Josh Bloch
43  * @since   1.5
44  */
45 public class AnnotationParser {
46     /**
47      * Parses the annotations described by the passed byte array.
48      * But return Annotation[] so I don't have to do this in C.
49      *
50      * @author Mathias Panzenböck
51      * 
52      * @throws AnnotationFormatError if an annotation is found to be
53      *         malformed.
54      */
55     public static Annotation[] parseAnnotationsIntoArray(
56                 byte[] rawAnnotations,
57                 ConstantPool constPool,
58                 Class container) {
59         Map<Class, Annotation> annotations = parseAnnotations(rawAnnotations, constPool, container);
60         return annotations.values().toArray(EMPTY_ANNOTATIONS_ARRAY);
61     }
62
63     /**
64      * Parses parameter annotations.
65      * 
66      * @author Mathias Panzenböck
67      * 
68      * @throws AnnotationFormatError if an annotation is found to be
69      *         malformed.
70      */
71     public static Annotation[][] parseParameterAnnotations(
72                     byte[] parameterAnnotations,
73                     ConstantPool constPool,
74                     Class container,
75                     int numParameters) {
76         if (parameterAnnotations == null)
77             return new Annotation[numParameters][0];
78
79         Annotation[][] result = parseParameterAnnotations(
80             parameterAnnotations, constPool, container); 
81
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 + ").");
86         return result;
87     }
88
89     /**
90      * Parses the annotation default value of the supplied method.
91      * This method is basically copied from OpenJDKs
92      * java.lang.reflect.Method.getAnnotationDefault()
93      * 
94      * @author Mathias Panzenböck
95      *
96      * @throws AnnotationFormatError if an annotation is found to be
97      *         malformed.
98      */
99     public static Object parseAnnotationDefault(Method method,
100                                                 byte[] annotationDefault,
101                                                 ConstantPool constPool) {
102         if  (annotationDefault == null)
103             return null;
104         
105         Class memberType = AnnotationType.invocationHandlerReturnType(
106             method.getReturnType());
107         
108         Object result = AnnotationParser.parseMemberValue(
109             memberType, ByteBuffer.wrap(annotationDefault),
110             constPool, method.getDeclaringClass());
111
112         if (result instanceof sun.reflect.annotation.ExceptionProxy)
113             throw new AnnotationFormatError("Invalid default: " + method);
114         
115         return result;
116     }
117
118     /**
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:
123      *
124      *   u2 num_annotations;
125      *   annotation annotations[num_annotations];
126      *
127      * @throws AnnotationFormatError if an annotation is found to be
128      *         malformed.
129      */
130     public static Map<Class, Annotation> parseAnnotations(
131                 byte[] rawAnnotations,
132                 ConstantPool constPool,
133                 Class container) {
134         if (rawAnnotations == null)
135             return Collections.emptyMap();
136
137         try {
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);
144         }
145     }
146
147     private static Map<Class, Annotation> parseAnnotations2(
148                 byte[] rawAnnotations,
149                 ConstantPool constPool,
150                 Class container) {
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);
156             if (a != null) {
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);
163             }
164         }
165         return result;
166     }
167
168     /**
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:
173      *
174      *    u1 num_parameters;
175      *    {
176      *        u2 num_annotations;
177      *        annotation annotations[num_annotations];
178      *    } parameter_annotations[num_parameters];
179      *
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.
187      *
188      * @throws AnnotationFormatError if an annotation is found to be
189      *         malformed.
190      */
191     public static Annotation[][] parseParameterAnnotations(
192                     byte[] rawAnnotations,
193                     ConstantPool constPool,
194                     Class container) {
195         try {
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);
203         }
204     }
205
206     private static Annotation[][] parseParameterAnnotations2(
207                     byte[] rawAnnotations,
208                     ConstantPool constPool,
209                     Class container) {
210         ByteBuffer buf = ByteBuffer.wrap(rawAnnotations);
211         int numParameters = buf.get() & 0xFF;
212         Annotation[][] result = new Annotation[numParameters][];
213
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);
220                 if (a != null) {
221                     AnnotationType type = AnnotationType.getInstance(
222                                               a.annotationType());
223                     if (type.retention() == RetentionPolicy.RUNTIME)
224                         annotations.add(a);
225                 }
226             }
227             result[i] = annotations.toArray(EMPTY_ANNOTATIONS_ARRAY);
228         }
229         return result;
230     }
231
232     private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY =
233                     new Annotation[0];
234
235     /**
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:
240      *
241      * annotation {
242      *    u2    type_index;
243      *       u2    num_member_value_pairs;
244      *       {    u2    member_name_index;
245      *             member_value value;
246      *       }    member_value_pairs[num_member_value_pairs];
247      *    }
248      * }
249      *
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.
252      *
253      * @param exceptionOnMissingAnnotationClass if true, throw
254      * TypeNotPresentException if a referenced annotation type is not
255      * available at runtime
256      */
257     private static Annotation parseAnnotation(ByteBuffer buf,
258                                               ConstantPool constPool,
259                                               Class container,
260                                               boolean exceptionOnMissingAnnotationClass) {
261         int typeIndex = buf.getShort() & 0xFFFF;
262         Class annotationClass = null;
263         String sig = "[unknown]";
264         try {
265             try {
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);
271             }
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);
278             return null;
279         }
280         catch (TypeNotPresentException e) {
281             if (exceptionOnMissingAnnotationClass)
282                 throw e;
283             skipAnnotation(buf, false);
284             return null;
285         }
286         AnnotationType type = null;
287         try {
288             type = AnnotationType.getInstance(annotationClass);
289         } catch (IllegalArgumentException e) {
290             skipAnnotation(buf, false);
291             return null;
292         }
293
294         Map<String, Class> memberTypes = type.memberTypes();
295         Map<String, Object> memberValues =
296             new LinkedHashMap<String, Object>(type.memberDefaults());
297
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);
303
304             if (memberType == null) {
305                 // Member is no longer present in annotation type; ignore it
306                 skipMemberValue(buf);
307             } else {
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);
313             }
314         }
315         return annotationForMap(annotationClass, memberValues);
316     }
317
318     /**
319      * Returns an annotation of the given type backed by the given
320      * member -> value map.
321      */
322     public static Annotation annotationForMap(
323         Class type, Map<String, Object> memberValues)
324     {
325         return (Annotation) Proxy.newProxyInstance(
326             type.getClassLoader(), new Class[] { type },
327             new AnnotationInvocationHandler(type, memberValues));
328     }
329
330     /**
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:
336      *
337      *  member_value {
338      *    u1 tag;
339      *    union {
340      *       u2   const_value_index;
341      *       {
342      *           u2   type_name_index;
343      *           u2   const_name_index;
344      *       } enum_const_value;
345      *       u2   class_info_index;
346      *       annotation annotation_value; 
347      *       {
348      *           u2    num_values;
349      *           member_value values[num_values];
350      *       } array_value;
351      *    } value;
352      * }
353      *
354      * The member must be of the indicated type. If it is not, this
355      * method returns an AnnotationTypeMismatchExceptionProxy.
356      */
357     public static Object parseMemberValue(Class memberType, ByteBuffer buf,
358                                           ConstantPool constPool,
359                                           Class container) {
360         Object result = null;
361         int tag = buf.get();
362         switch(tag) {
363           case 'e':
364               return parseEnumValue(memberType, buf, constPool, container);
365           case 'c':
366               result = parseClassValue(buf, constPool, container);
367               break;
368           case '@':
369               result = parseAnnotation(buf, constPool, container, true);
370               break;
371           case '[':
372               return parseArray(memberType, buf, constPool, container);
373           default:
374               result = parseConst(tag, buf, constPool);
375         }
376
377         if (!(result instanceof ExceptionProxy) &&
378             !memberType.isInstance(result))
379             result = new AnnotationTypeMismatchExceptionProxy(
380                 result.getClass() + "[" + result + "]");
381         return result;
382     }
383
384     /**
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:
391      *
392      *       u2   const_value_index;
393      */
394     private static Object parseConst(int tag,
395                                      ByteBuffer buf, ConstantPool constPool) {
396         int constIndex = buf.getShort() & 0xFFFF;
397         switch(tag) {
398           case 'B':
399             return Byte.valueOf((byte) constPool.getIntAt(constIndex));
400           case 'C':
401             return Character.valueOf((char) constPool.getIntAt(constIndex));
402           case 'D':
403             return Double.valueOf(constPool.getDoubleAt(constIndex));
404           case 'F':
405             return Float.valueOf(constPool.getFloatAt(constIndex));
406           case 'I':
407             return Integer.valueOf(constPool.getIntAt(constIndex));
408           case 'J':
409             return Long.valueOf(constPool.getLongAt(constIndex));
410           case 'S':
411             return Short.valueOf((short) constPool.getIntAt(constIndex));
412           case 'Z':
413             return Boolean.valueOf(constPool.getIntAt(constIndex) != 0);
414           case 's':
415             return constPool.getUTF8At(constIndex);
416           default:
417             throw new AnnotationFormatError(
418                 "Invalid member-value tag in annotation: " + tag);
419         }
420     }
421
422     /**
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:
427      *
428      *       u2   class_info_index;
429      */
430     private static Object parseClassValue(ByteBuffer buf,
431                                           ConstantPool constPool,
432                                           Class container) {
433         int classIndex = buf.getShort() & 0xFFFF;
434         try {
435             try {
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);
441             }
442         } catch (NoClassDefFoundError e) {
443             return new TypeNotPresentExceptionProxy("[unknown]", e);
444         }
445         catch (TypeNotPresentException e) {
446             return new TypeNotPresentExceptionProxy(e.typeName(), e.getCause());
447         }
448     }
449
450     /**
451      * Parses a return type signature and returns the according Class object.
452      */
453     private static Class<?> parseSig(String sig, Class container) {
454         if (sig.equals("V")) {
455             return void.class;
456         }
457         else {
458             return toClass(new FieldSignatureParser(container, sig).getFieldType());
459         }
460     }
461
462     static Class<?> toClass(Type o) {
463         if (o instanceof GenericArrayType)
464             return Array.newInstance(toClass(((GenericArrayType)o).getGenericComponentType()),
465                                      0)
466                 .getClass();
467         return (Class<?>)o;
468     }
469
470     /**
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:
476      *
477      *       {
478      *           u2   type_name_index;
479      *           u2   const_name_index;
480      *       } enum_const_value;
481      */
482     private static Object parseEnumValue(Class enumType, ByteBuffer buf,
483                                          ConstantPool constPool,
484                                          Class container) {
485         int typeNameIndex = buf.getShort() & 0xFFFF;
486         String typeName  = constPool.getUTF8At(typeNameIndex);
487         int constNameIndex = buf.getShort() & 0xFFFF;
488         String constName = constPool.getUTF8At(constNameIndex);
489
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);
498         }
499
500         try {
501             return  Enum.valueOf(enumType, constName);
502         } catch(IllegalArgumentException e) {
503             return new EnumConstantNotPresentExceptionProxy(
504                 (Class<? extends Enum>)enumType, constName);
505         }
506     }
507
508     /**
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:
513      *
514      *       {
515      *           u2    num_values;
516      *           member_value values[num_values];
517      *       } array_value;
518      *
519      * If the array values do not match arrayType, an
520      * AnnotationTypeMismatchExceptionProxy will be returned.
521      */
522     private static Object parseArray(Class arrayType,
523                                      ByteBuffer buf,
524                                      ConstantPool constPool,
525                                      Class container) {
526         int length = buf.getShort() & 0xFFFF;  // Number of array components
527         Class componentType = arrayType.getComponentType();
528
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);
552         } else {
553             assert componentType.isAnnotation();
554             return parseAnnotationArray(length, componentType, buf,
555                                         constPool, container);
556         }
557     }
558
559     private static Object parseByteArray(int length,
560                                   ByteBuffer buf, ConstantPool constPool) {
561         byte[] result = new byte[length];
562         boolean typeMismatch = false;
563         int tag = 0;
564
565         for (int i = 0; i < length; i++) {
566             tag = buf.get();
567             if (tag == 'B') {
568                 int index = buf.getShort() & 0xFFFF;
569                 result[i] = (byte) constPool.getIntAt(index);
570             } else {
571                 skipMemberValue(tag, buf);
572                 typeMismatch = true;
573             }
574         }
575         return typeMismatch ? exceptionProxy(tag) : result;
576     }
577
578     private static Object parseCharArray(int length,
579                                   ByteBuffer buf, ConstantPool constPool) {
580         char[] result = new char[length];
581         boolean typeMismatch = false;
582         byte tag = 0;
583
584         for (int i = 0; i < length; i++) {
585             tag = buf.get();
586             if (tag == 'C') {
587                 int index = buf.getShort() & 0xFFFF;
588                 result[i] = (char) constPool.getIntAt(index);
589             } else {
590                 skipMemberValue(tag, buf);
591                 typeMismatch = true;
592             }
593         }
594         return typeMismatch ? exceptionProxy(tag) : result;
595     }
596
597     private static Object parseDoubleArray(int length,
598                                     ByteBuffer buf, ConstantPool constPool) {
599         double[] result = new  double[length];
600         boolean typeMismatch = false;
601         int tag = 0;
602
603         for (int i = 0; i < length; i++) {
604             tag = buf.get();
605             if (tag == 'D') {
606                 int index = buf.getShort() & 0xFFFF;
607                 result[i] = constPool.getDoubleAt(index);
608             } else {
609                 skipMemberValue(tag, buf);
610                 typeMismatch = true;
611             }
612         }
613         return typeMismatch ? exceptionProxy(tag) : result;
614     }
615
616     private static Object parseFloatArray(int length,
617                                    ByteBuffer buf, ConstantPool constPool) {
618         float[] result = new float[length];
619         boolean typeMismatch = false;
620         int tag = 0;
621
622         for (int i = 0; i < length; i++) {
623             tag = buf.get();
624             if (tag == 'F') {
625                 int index = buf.getShort() & 0xFFFF;
626                 result[i] = constPool.getFloatAt(index);
627             } else {
628                 skipMemberValue(tag, buf);
629                 typeMismatch = true;
630             }
631         }
632         return typeMismatch ? exceptionProxy(tag) : result;
633     }
634
635     private static Object parseIntArray(int length,
636                                  ByteBuffer buf, ConstantPool constPool) {
637         int[] result = new  int[length];
638         boolean typeMismatch = false;
639         int tag = 0;
640
641         for (int i = 0; i < length; i++) {
642             tag = buf.get();
643             if (tag == 'I') {
644                 int index = buf.getShort() & 0xFFFF;
645                 result[i] = constPool.getIntAt(index);
646             } else {
647                 skipMemberValue(tag, buf);
648                 typeMismatch = true;
649             }
650         }
651         return typeMismatch ? exceptionProxy(tag) : result;
652     }
653     
654     private static Object parseLongArray(int length,
655                                   ByteBuffer buf, ConstantPool constPool) {
656         long[] result = new long[length];
657         boolean typeMismatch = false;
658         int tag = 0;
659
660         for (int i = 0; i < length; i++) {
661             tag = buf.get();
662             if (tag == 'J') {
663                 int index = buf.getShort() & 0xFFFF;
664                 result[i] = constPool.getLongAt(index);
665             } else {
666                 skipMemberValue(tag, buf);
667                 typeMismatch = true;
668             }
669         }
670         return typeMismatch ? exceptionProxy(tag) : result;
671     }
672
673     private static Object parseShortArray(int length,
674                                    ByteBuffer buf, ConstantPool constPool) {
675         short[] result = new short[length];
676         boolean typeMismatch = false;
677         int tag = 0;
678
679         for (int i = 0; i < length; i++) {
680             tag = buf.get();
681             if (tag == 'S') {
682                 int index = buf.getShort() & 0xFFFF;
683                 result[i] = (short) constPool.getIntAt(index);
684             } else {
685                 skipMemberValue(tag, buf);
686                 typeMismatch = true;
687             }
688         }
689         return typeMismatch ? exceptionProxy(tag) : result;
690     }
691
692     private static Object parseBooleanArray(int length,
693                                      ByteBuffer buf, ConstantPool constPool) {
694         boolean[] result = new boolean[length];
695         boolean typeMismatch = false;
696         int tag = 0;
697
698         for (int i = 0; i < length; i++) {
699             tag = buf.get();
700             if (tag == 'Z') {
701                 int index = buf.getShort() & 0xFFFF;
702                 result[i] = (constPool.getIntAt(index) != 0);
703             } else {
704                 skipMemberValue(tag, buf);
705                 typeMismatch = true;
706             }
707         }
708         return typeMismatch ? exceptionProxy(tag) : result;
709     }
710
711     private static Object parseStringArray(int length,
712                                     ByteBuffer buf,  ConstantPool constPool) {
713         String[] result = new String[length];
714         boolean typeMismatch = false;
715         int tag = 0;
716
717         for (int i = 0; i < length; i++) {
718             tag = buf.get();
719             if (tag == 's') {
720                 int index = buf.getShort() & 0xFFFF;
721                 result[i] = constPool.getUTF8At(index);
722             } else {
723                 skipMemberValue(tag, buf);
724                 typeMismatch = true;
725             }
726         }
727         return typeMismatch ? exceptionProxy(tag) : result;
728     }
729
730     private static Object parseClassArray(int length,
731                                           ByteBuffer buf,
732                                           ConstantPool constPool,
733                                           Class container) {
734         Object[] result = new Class[length];
735         boolean typeMismatch = false;
736         int tag = 0;
737
738         for (int i = 0; i < length; i++) {
739             tag = buf.get();
740             if (tag == 'c') {
741                 result[i] = parseClassValue(buf, constPool, container);
742             } else {
743                 skipMemberValue(tag, buf);
744                 typeMismatch = true;
745             }
746         }
747         return typeMismatch ? exceptionProxy(tag) : result;
748     }
749
750     private static Object parseEnumArray(int length, Class enumType,
751                                          ByteBuffer buf,
752                                          ConstantPool constPool,
753                                          Class container) {
754         Object[] result = (Object[]) Array.newInstance(enumType, length);
755         boolean typeMismatch = false;
756         int tag = 0;
757
758         for (int i = 0; i < length; i++) {
759             tag = buf.get();
760             if (tag == 'e') {
761                 result[i] = parseEnumValue(enumType, buf, constPool, container);
762             } else {
763                 skipMemberValue(tag, buf);
764                 typeMismatch = true;
765             }
766         }
767         return typeMismatch ? exceptionProxy(tag) : result;
768     }
769
770     private static Object parseAnnotationArray(int length,
771                                                Class annotationType,
772                                                ByteBuffer buf,
773                                                ConstantPool constPool,
774                                                Class container) {
775         Object[] result = (Object[]) Array.newInstance(annotationType, length);
776         boolean typeMismatch = false;
777         int tag = 0;
778
779         for (int i = 0; i < length; i++) {
780             tag = buf.get();
781             if (tag == '@') {
782                 result[i] = parseAnnotation(buf, constPool, container, true);
783             } else {
784                 skipMemberValue(tag, buf);
785                 typeMismatch = true;
786             }
787         }
788         return typeMismatch ? exceptionProxy(tag) : result;
789     }
790
791     /**
792      * Return an appropriate exception proxy for a mismatching array
793      * annotation where the erroneous array has the specified tag.
794      */
795     private static ExceptionProxy exceptionProxy(int tag) {
796         return new AnnotationTypeMismatchExceptionProxy(
797             "Array with component tag: " + tag);
798     }
799
800     /**
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).
805      * 
806      * @parameter complete true if the byte buffer points to the beginning
807      *     of an annotation structure (rather than two bytes in).
808      */
809     private static void skipAnnotation(ByteBuffer buf, boolean complete) {
810         if (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);
816         }
817     }
818
819     /**
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."
823      */
824     private static void skipMemberValue(ByteBuffer buf) {
825         int tag = buf.get();
826         skipMemberValue(tag, buf);
827     }
828
829     /**
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."
833      */
834     private static void skipMemberValue(int tag, ByteBuffer buf) {
835         switch(tag) {
836           case 'e': // Enum value
837             buf.getInt();  // (Two shorts, actually.)
838             break;
839           case '@':
840             skipAnnotation(buf, true);
841             break;
842           case '[':
843             skipArray(buf);
844             break;
845           default:
846             // Class, primitive, or String
847             buf.getShort();
848         }
849     }
850
851     /**
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
854      * struct.
855      */
856     private static void skipArray(ByteBuffer buf) {
857         int length = buf.getShort() & 0xFFFF;
858         for (int i = 0; i < length; i++)
859             skipMemberValue(buf);
860     }
861 }