001    package serp.bytecode;
002    
003    import java.io.*;
004    import java.lang.reflect.*;
005    import java.util.*;
006    
007    import serp.bytecode.lowlevel.*;
008    import serp.bytecode.visitor.*;
009    import serp.util.*;
010    
011    /**
012     * A declared annotation.
013     *
014     * @author Abe White
015     */
016    public class Annotation implements BCEntity, VisitAcceptor {
017        private static Method ENUM_VALUEOF = null;
018        private static Method ENUM_NAME = null;
019        static {
020            try {
021                Class c = Class.forName("java.lang.Enum");
022                ENUM_VALUEOF = c.getMethod("valueOf", new Class[] {
023                    Class.class, String.class });
024                ENUM_NAME = c.getMethod("name", (Class[]) null);
025            } catch (Throwable t) {
026                // pre-1.5 JDK
027            }
028        }
029     
030        private BCEntity _owner = null;
031        private int _typeIndex = 0;
032        private List _properties = null;
033    
034        Annotation(BCEntity owner) {
035            _owner = owner;
036        }
037    
038        /**
039         * Annotations are stored in an {@link Annotations} table or as
040         * part of an {@link Annotation} property value.
041         */
042        public BCEntity getOwner() {
043            return _owner;
044        }
045    
046        void invalidate() {
047            _owner = null;
048        }
049    
050        /**
051         * The index in the class {@link ConstantPool} of the
052         * {@link UTF8Entry} holding the type of this annotation.
053         */
054        public int getTypeIndex() {
055            return _typeIndex;
056        }
057    
058        /**
059         * The index in the class {@link ConstantPool} of the
060         * {@link UTF8Entry} holding the type of this annotation.
061         */
062        public void setTypeIndex(int index) {
063            _typeIndex = index;
064        }
065    
066        /**
067         * The name of this annotation's type.
068         */
069        public String getTypeName() {
070            String desc = ((UTF8Entry) getPool().getEntry(_typeIndex)).getValue();
071            return getProject().getNameCache().getExternalForm(desc, false);
072        }
073    
074        /**
075         * The {@link Class} object for this annotation's type.
076         */
077        public Class getType() {
078            return Strings.toClass(getTypeName(), getClassLoader());
079        }
080    
081        /**
082         * The bytecode for the type of this annotation.
083         */
084        public BCClass getTypeBC() {
085            return getProject().loadClass(getTypeName(), getClassLoader());
086        }
087    
088        /**
089         * This annotation's type.
090         */
091        public void setType(String type) {
092            type = getProject().getNameCache().getInternalForm(type, true);
093            _typeIndex = getPool().findUTF8Entry(type, true);
094        }
095    
096        /**
097         * This annotation's type.
098         */
099        public void setType(Class type) {
100            setType(type.getName());
101        }
102    
103        /**
104         * This annotation's type.
105         */
106        public void setType(BCClass type) {
107            setType(type.getName());
108        }
109    
110        /**
111         * All declared properties.
112         */
113        public Property[] getProperties() {
114            if (_properties == null)
115                return new Property[0];
116            return (Property[]) _properties.toArray
117                (new Property[_properties.size()]);
118        }
119    
120        /**
121         * Set the annotation properties.  This method is useful when
122         * importing properties from another instance.
123         */
124        public void setProperties(Property[] props) {
125            clearProperties();
126            if (props != null)
127                for (int i = 0; i < props.length; i++)
128                    addProperty(props[i]);
129        }
130    
131        /**
132         * Return the property with the given name, or null if none.
133         */
134        public Property getProperty(String name) {
135            if (_properties == null)
136                return null;
137            Property prop;
138            for (int i = 0; i < _properties.size(); i++) {
139                prop = (Property) _properties.get(i);
140                if (prop.getName().equals(name))
141                    return prop;
142            }
143            return null;
144        }
145    
146        /**
147         * Import a property from another instance.
148         *
149         * @return the newly added property
150         */
151        public Property addProperty(Property p) {
152            Property prop = addProperty(p.getName());
153            prop.setValue(p.getValue());
154            return prop;
155        }
156    
157        /**
158         * Add a new property.
159         */
160        public Property addProperty(String name) {
161            Property prop = new Property(this);
162            prop.setName(name);
163            if (_properties == null)
164                _properties = new ArrayList();
165            _properties.add(prop);
166            return prop;
167        }
168    
169        /**
170         * Clear all annotation properties.
171         */
172        public void clearProperties() {
173            if (_properties == null)
174                return;
175            for (int i = 0; i < _properties.size(); i++)
176                ((Property) _properties.get(i)).invalidate();
177            _properties.clear();
178        }
179    
180        /**
181         * Remove the given property.
182         *
183         * @return true if an property was removed, false otherwise
184         */
185        public boolean removeProperty(Property prop) {
186            return prop != null && removeProperty(prop.getName());
187        }
188    
189        /**
190         * Remove the property with the given name.
191         *
192         * @return true if a property was removed, false otherwise
193         */
194        public boolean removeProperty(String name) {
195            if (name == null || _properties == null)
196                return false;
197            Property prop;
198            for (int i = 0; i < _properties.size(); i++) {
199                prop = (Property) _properties.get(i);
200                if (prop.getName().equals(name)) {
201                    prop.invalidate();
202                    _properties.remove(i);
203                    return true;
204                }
205            }
206            return false;
207        }
208    
209        public Project getProject() {
210            return _owner.getProject();
211        }
212    
213        public ConstantPool getPool() {
214            return _owner.getPool();
215        }
216    
217        public ClassLoader getClassLoader() {
218            return _owner.getClassLoader();
219        }
220    
221        public boolean isValid() {
222            return _owner != null;
223        }
224    
225        public void acceptVisit(BCVisitor visit) {
226            visit.enterAnnotation(this);
227            if (_properties != null)
228                for (int i = 0; i < _properties.size(); i++)
229                    ((Property) _properties.get(i)).acceptVisit(visit);
230            visit.exitAnnotation(this);
231        }
232    
233        int getLength() {
234            int len = 4;
235            if (_properties != null)
236                for (int i = 0; i < _properties.size(); i++)
237                    len += ((Property) _properties.get(i)).getLength();
238            return len;
239        }
240    
241        void read(DataInput in) throws IOException {
242            _typeIndex = in.readUnsignedShort();
243            clearProperties();
244            int props = in.readUnsignedShort();
245            if (props > 0) {
246                if (_properties == null)
247                    _properties = new ArrayList(props);
248                Property prop;
249                for (int i = 0; i < props; i++) {
250                    prop = new Property(this);
251                    prop.read(in);
252                    _properties.add(prop); 
253                }
254            }
255        }
256    
257        void write(DataOutput out) throws IOException {
258            out.writeShort(_typeIndex); 
259            out.writeShort((_properties == null) ? 0 : _properties.size());
260            if (_properties != null) {
261                for (int i = 0; i < _properties.size(); i++)
262                    ((Property) _properties.get(i)).write(out);
263            }
264        }
265    
266        /**
267         * An annotation property.
268         */
269        public static class Property implements BCEntity, VisitAcceptor {
270            private Annotation _owner = null;
271            private int _nameIndex = 0;
272            private final Value _value = new Value();
273            private Value[] _values = null;
274    
275            Property(Annotation owner) {
276                _owner = owner;
277            }
278    
279            /**
280             * The owning annotation.
281             */
282            public Annotation getAnnotation() {
283                return _owner;
284            }
285    
286            void invalidate() {
287                _owner = null;
288            }
289    
290            /**
291             * Return the index in the class {@link ConstantPool} of the
292             * {@link UTF8Entry} holding the name of this property.
293             */
294            public int getNameIndex() {
295                return _nameIndex;
296            }
297    
298            /**
299             * Set the index in the class {@link ConstantPool} of the
300             * {@link UTF8Entry} holding the name of this property.
301             */
302            public void setNameIndex(int index) {
303                _nameIndex = index;
304            }
305    
306            /**
307             * Return the name of this property.
308             */
309            public String getName() {
310                return ((UTF8Entry) getPool().getEntry(_nameIndex)).getValue();
311            }
312    
313            /**
314             * Set the name of this property.
315             */
316            public void setName(String name) {
317                _nameIndex = getPool().findUTF8Entry(name, true);
318            }
319    
320            /**
321             * Return the value of the property as its wrapper type.
322             * Returns class values as the class name.
323             */
324            public Object getValue() {
325                if (_values == null)
326                    return getValue(_value);
327                Object[] vals = new Object[_values.length];
328                for (int i = 0; i < vals.length; i++)
329                    vals[i] = getValue(_values[i]);
330                return vals;
331            }
332    
333            /**
334             * Extract the Java value.
335             */
336            private Object getValue(Value val) {
337                if (val.index == -1)
338                    return val.value;
339    
340                Object o = ((ConstantEntry) getPool().getEntry(val.index)).
341                    getConstant();
342                if (val.index2 != -1) {
343                    // enum value
344                    String e = getProject().getNameCache().
345                        getExternalForm((String) o, false);
346                    String name = ((UTF8Entry) getPool().getEntry(val.index2)).
347                        getValue();
348                    try {
349                        Class cls = Class.forName(e, true, getClassLoader());  
350                        return ENUM_VALUEOF.invoke(null, new Object[] {cls, name});
351                    } catch (Throwable t) {
352                        return e + "." + name;
353                    } 
354                }
355                if (val.type == null)
356                    return o;
357    
358                switch (val.type.getName().charAt(0)) {
359                case 'b': 
360                    if (val.type == boolean.class)
361                        return (((Number) o).intValue() != 0) ? Boolean.TRUE
362                            : Boolean.FALSE;
363                    return new Byte(((Number) o).byteValue());
364                case 'c':
365                    return new Character((char) ((Number) o).intValue());
366                case 'j': // java.lang.Class
367                    return getProject().getNameCache().getExternalForm((String) o, 
368                        false);
369                case 's':
370                    return new Short(((Number) o).shortValue());
371                default:
372                    return o;
373                }
374            }
375    
376            /**
377             * Set value of this property. The value should be an instance of any
378             * primitive wrapper type, String, Class, BCClass, an enum constant,
379             * an annotation, or an array of any of these types.
380             */
381            public void setValue(Object value) {
382                if (!value.getClass().isArray()) {
383                    _values = null;
384                    setValue(_value, value);
385                } else {
386                    _value.value = null;
387                    _values = new Value[Array.getLength(value)];
388                    for (int i = 0; i < _values.length; i++) {
389                        _values[i] = new Value();
390                        setValue(_values[i], Array.get(value, i));
391                    } 
392                }
393            }
394    
395            /**
396             * Set the given value.
397             */
398            private void setValue(Value val, Object o) {
399                if (o instanceof String) 
400                    setValue(val, (String) o);
401                else if (o instanceof Boolean)
402                    setValue(val, ((Boolean) o).booleanValue());
403                else if (o instanceof Byte)
404                    setValue(val, ((Byte) o).byteValue());
405                else if (o instanceof Character)
406                    setValue(val, ((Character) o).charValue());
407                else if (o instanceof Double)
408                    setValue(val, ((Double) o).doubleValue());
409                else if (o instanceof Float)
410                    setValue(val, ((Float) o).floatValue());
411                else if (o instanceof Integer)
412                    setValue(val, ((Integer) o).intValue());
413                else if (o instanceof Long)
414                    setValue(val, ((Long) o).longValue());
415                else if (o instanceof Short)
416                    setValue(val, ((Short) o).shortValue());
417                else if (o instanceof Class)
418                    setClassNameValue(val, ((Class) o).getName());
419                else if (o instanceof BCClass)
420                    setClassNameValue(val, ((BCClass) o).getName());
421                else if (o instanceof Annotation)
422                    setValue(val, (Annotation) o);
423                else {
424                    String name = getEnumName(o);
425                    if (name != null) {
426                        String type = getProject().getNameCache().
427                            getInternalForm(o.getClass().getName(), false);
428                        val.index = getPool().findUTF8Entry(type, true);
429                        val.index2 = getPool().findUTF8Entry(name, true);
430                        val.value = null;
431                        val.type = null;
432                    } else {
433                        val.index = -1;
434                        val.index2 = -1;
435                        val.value = o;
436                        val.type = o.getClass();
437                    }
438                }
439            }
440    
441            /**
442             * Return the name of this enum value, or null if not an enum.
443             */
444            private static String getEnumName(Object o) {
445                for (Class c = o.getClass(); true; c = c.getSuperclass()) {
446                    if (c == Object.class || c == null)
447                        return null;
448                    if ("java.lang.Enum".equals(c.getName()))
449                        break;
450                }
451                try {
452                    return (String) ENUM_NAME.invoke(o, (Object[]) null);
453                } catch (Throwable t) {
454                    return o.toString();
455                }
456            }
457    
458            /**
459             * Return the string value of this property, or null if not set.
460             */
461            public String getStringValue() {
462                return (String) getValue();
463            }
464    
465            /**
466             * Return the boolean value of this property, or false if not set.
467             */
468            public boolean getBooleanValue() {
469                Object value = getValue();
470                return (value == null) ? false : ((Boolean) value).booleanValue();
471            }
472    
473            /**
474             * Return the byte value of this property, or false if not set.
475             */
476            public byte getByteValue() {
477                Object value = getValue();
478                return (value == null) ? (byte) 0 : ((Number) value).byteValue();
479            }
480    
481            /**
482             * Return the int value of this property, or 0 if not set.
483             */
484            public int getIntValue() {
485                Object value = getValue();
486                return (value == null) ? 0 : ((Number) value).intValue();
487            }
488    
489            /**
490             * Return the long value of this property, or 0 if not set.
491             */
492            public long getLongValue() {
493                Object value = getValue();
494                return (value == null) ? 0L : ((Number) value).longValue();
495            }
496    
497            /**
498             * Return the float value of this property, or 0 if not set.
499             */
500            public float getFloatValue() {
501                Object value = getValue();
502                return (value == null) ? 0F : ((Number) value).floatValue();
503            }
504    
505            /**
506             * Return the double value of this property, or 0 if not set.
507             */
508            public double getDoubleValue() {
509                Object value = getValue();
510                return (value == null) ? 0D : ((Number) value).doubleValue();
511            }
512    
513            /**
514             * Return the short value of this property, or 0 if not set.
515             */
516            public short getShortValue() {
517                Object value = getValue();
518                return (value == null) ? (short) 0 : ((Number) value).shortValue();
519            }
520    
521            /**
522             * Return the class value of this property, or null if not set.
523             */
524            public String getClassNameValue() {
525                return (String) getValue();
526            }
527    
528            /**
529             * Return the annotation value of this property, or null if not set.
530             */
531            public Annotation getAnnotationValue() {
532                return (Annotation) getValue();
533            }
534    
535            /**
536             * Set the string value of this property.
537             */
538            public void setValue(String value) {
539                _values = null;
540                setValue(_value, value);
541            }
542    
543            /**
544             * Set the string value of this property.
545             */
546            private void setValue(Value val, String o) {
547                val.index = getPool().findUTF8Entry(o, true);
548                val.index2 = -1;
549                val.value = null;
550                val.type = null;
551            }
552    
553            /**
554             * Set the boolean value of this property.
555             */
556            public void setValue(boolean value) {
557                _values = null;
558                setValue(_value, value);
559            }
560    
561            /**
562             * Set the boolean value of this property.
563             */
564            private void setValue(Value val, boolean o) {
565                setValue(val, (o) ? 1 : 0);
566                val.type = boolean.class;
567            }
568    
569            /**
570             * Set the byte value of this property.
571             */
572            public void setValue(byte value) {
573                _values = null;
574                setValue(_value, value);
575            }
576    
577            /**
578             * Set the byte value of this property.
579             */
580            private void setValue(Value val, byte o) {
581                setValue(val, (int) o);
582                val.type = byte.class;
583            }
584    
585            /**
586             * Set the int value of this property.
587             */
588            public void setValue(int value) {
589                _values = null;
590                setValue(_value, value);
591            }
592    
593            /**
594             * Set the int value of this property.
595             */
596            private void setValue(Value val, int o) {
597                val.index = getPool().findIntEntry(o, true);
598                val.index2 = -1;
599                val.value = null;
600                val.type = null;
601            }
602    
603            /**
604             * Set the long value of this property.
605             */
606            public void setValue(long value) {
607                _values = null;
608                setValue(_value, value);
609            }
610    
611            /**
612             * Set the long value of this property.
613             */
614            private void setValue(Value val, long o) {
615                val.index = getPool().findLongEntry(o, true);
616                val.index2 = -1;
617                val.value = null;
618                val.type = null;
619            }
620    
621            /**
622             * Set the float value of this property.
623             */
624            public void setValue(float value) {
625                _values = null;
626                setValue(_value, value);
627            }
628    
629            /**
630             * Set the float value of this property.
631             */
632            private void setValue(Value val, float o) {
633                val.index = getPool().findFloatEntry(o, true);
634                val.index2 = -1;
635                val.value = null;
636                val.type = null;
637            }
638    
639            /**
640             * Set the double value of this property.
641             */
642            public void setValue(double value) {
643                _values = null;
644                setValue(_value, value);
645            }
646    
647            /**
648             * Set the double value of this property.
649             */
650            private void setValue(Value val, double o) {
651                val.index = getPool().findDoubleEntry(o, true);
652                val.index2 = -1;
653                val.value = null;
654                val.type = null;
655            }
656    
657            /**
658             * Set the short value of this property.
659             */
660            public void setValue(short value) {
661                _values = null;
662                setValue(_value, value);
663            }
664    
665            /**
666             * Set the short value of this property.
667             */
668            private void setValue(Value val, short o) {
669                setValue(val, (int) o);
670                val.type = short.class;
671            }
672    
673            /**
674             * Set the class value of this property.
675             */
676            public void setValue(Class value) {
677                setClassNameValue(value.getName());
678            }
679    
680            /**
681             * Set the class value of this property.
682             */
683            public void setValue(BCClass value) {
684                setClassNameValue(value.getName());
685            }
686    
687            /**
688             * Set the class value of this property.
689             */
690            public void setClassNameValue(String value) {
691                _values = null;
692                setClassNameValue(_value, value);
693            }
694    
695            /**
696             * Set the class value of this property.
697             */
698            private void setClassNameValue(Value val, String o) {
699                o = getProject().getNameCache().getInternalForm(o, true);
700                val.index = getPool().findUTF8Entry(o, true);
701                val.index2 = -1;
702                val.value = null;
703                val.type = Class.class;
704            }
705    
706            /**
707             * Set the annotation value of this property by importing the given
708             * annotation from another instance.
709             */
710            public Annotation setValue(Annotation value) {
711                _values = null;
712                return setValue(_value, value);
713            }
714    
715            /**
716             * Set the annotation value of this property by importing the given
717             * annotation from another instance.
718             */
719            private Annotation setValue(Value val, Annotation o) {
720                Annotation anno = new Annotation(this);
721                anno.setType(o.getTypeName());
722                anno.setProperties(o.getProperties());
723                val.index = -1;
724                val.index2 = -1;
725                val.value = anno;
726                val.type = null;
727                return anno;
728            }
729    
730            /**
731             * Set the annotation value of this property by importing the given
732             * annotation from another instance.
733             */
734            public Annotation[] setValue(Annotation[] value) {
735                _value.value = null;
736                _values = new Value[value.length];
737                Annotation[] ret = new Annotation[value.length];
738                for (int i = 0; i < _values.length; i++) {
739                    _values[i] = new Value();
740                    ret[i] = setValue(_values[i], value[i]);
741                }
742                return ret;
743            }
744    
745            /**
746             * Set this property value to a new annotation of the given type, 
747             * returning the annotation for manipulation.
748             */
749            public Annotation newAnnotationValue(Class type) {
750                return newAnnotationValue(type.getName());
751            }
752    
753            /**
754             * Set this property value to a new annotation of the given type, 
755             * returning the annotation for manipulation.
756             */
757            public Annotation newAnnotationValue(BCClass type) {
758                return newAnnotationValue(type.getName());
759            }
760    
761            /**
762             * Set this property value to a new annotation of the given type, 
763             * returning the annotation for manipulation.
764             */
765            public Annotation newAnnotationValue(String type) {
766                Annotation anno = new Annotation(this);
767                anno.setType(type);
768                _values = null;
769                _value.index = -1;
770                _value.index2 = -1;
771                _value.value = anno;
772                _value.type = null;
773                return anno;
774            }
775    
776            /**
777             * Set this property value to a new annotation array of the given type
778             * and length, returning the annotations for manipulation.
779             */
780            public Annotation[] newAnnotationArrayValue(Class type, int length) {
781                return newAnnotationArrayValue(type.getName(), length);
782            }
783    
784            /**
785             * Set this property value to a new annotation array of the given type
786             * and length, returning the annotations for manipulation.
787             */
788            public Annotation[] newAnnotationArrayValue(BCClass type, int length) {
789                return newAnnotationArrayValue(type.getName(), length);
790            }
791    
792            /**
793             * Set this property value to a new annotation array of the given type
794             * and length, returning the annotations for manipulation.
795             */
796            public Annotation[] newAnnotationArrayValue(String type, int length) {
797                _value.value = null;
798                _values = new Value[length]; 
799                Annotation[] ret = new Annotation[length];
800                for (int i = 0; i < length; i++) {
801                    ret[i] = new Annotation(this);
802                    ret[i].setType(type);
803                    _values[i] = new Value();
804                    _values[i].index = -1;
805                    _values[i].index2 = -1;
806                    _values[i].value = ret[i];
807                    _values[i].type = null;
808                }
809                return ret;
810            }
811    
812            public Project getProject() {
813                return _owner.getProject();
814            }
815    
816            public ConstantPool getPool() {
817                return _owner.getPool();
818            }
819    
820            public ClassLoader getClassLoader() {
821                return _owner.getClassLoader();
822            }
823    
824            public boolean isValid() {
825                return _owner != null && (_values != null || _value.index != -1
826                    || _value.value != null);
827            }
828    
829            public void acceptVisit(BCVisitor visit) {
830                visit.enterAnnotationProperty(this);
831                visit.exitAnnotationProperty(this);
832            }
833    
834            int getLength() {
835                if (!isValid())
836                    throw new IllegalStateException();
837    
838                int len = 2; // name
839                if (_values == null)
840                    len += getLength(_value);
841                else {
842                    len += 3; // arr length + tag
843                    for (int i = 0; i < _values.length; i++)
844                        len += getLength(_values[i]);
845                }
846                return len;
847            }
848    
849            /**
850             * Return the length of the given value.
851             */
852            private int getLength(Value val) {
853                if (val.index2 != -1)
854                    return 5; // tag + enum type + enum name
855                if (val.index != -1)
856                    return 3; // tag + constant or class
857                return 1 + ((Annotation) val.value).getLength(); // tag + anno
858            }
859    
860            void read(DataInput in) throws IOException {
861                _nameIndex = in.readUnsignedShort(); 
862                int tag = in.readByte();
863                if (tag == '[') {
864                    int len = in.readUnsignedShort();
865                    _values = new Value[len];
866                    for (int i = 0; i < len; i++) {
867                        _values[i] = new Value();
868                        read(_values[i], in.readByte(), in); 
869                    }
870                } else
871                    read(_value, tag, in);
872            }
873    
874            /**
875             * Read data into the given value.
876             */
877            private void read(Value val, int tag, DataInput in) throws IOException {
878                switch (tag) {
879                case 'B':
880                    val.index = in.readUnsignedShort();
881                    val.index2 = -1;
882                    val.value = null;
883                    val.type = byte.class;
884                    break;
885                case 'C':
886                    val.index = in.readUnsignedShort();
887                    val.index2 = -1;
888                    val.value = null;
889                    val.type = char.class;
890                    break;
891                case 'D':
892                case 'F':
893                case 'I':
894                case 'J':
895                case 'S':
896                case 's':
897                    val.index = in.readUnsignedShort();
898                    val.index2 = -1;
899                    val.value = null;
900                    val.type = null;
901                    break;
902                case 'Z':
903                    val.index = in.readUnsignedShort();
904                    val.index2 = -1;
905                    val.value = null;
906                    val.type = boolean.class;
907                    break;
908                case 'c':
909                    val.index = in.readUnsignedShort();
910                    val.index2 = -1;
911                    val.value = null;
912                    val.type = Class.class;
913                    break;
914                case 'e':
915                    val.index = in.readUnsignedShort();
916                    val.index2 = in.readUnsignedShort();
917                    val.value = null;
918                    val.type = null;
919                    break;
920                case '@':
921                    Annotation anno = new Annotation(this);
922                    anno.read(in);
923                    val.index = -1;
924                    val.index2 = -1;
925                    val.value = anno;
926                    val.type = null;
927                    break;
928                default:
929                    throw new IllegalStateException(String.valueOf(tag));
930                }
931            }
932    
933            void write(DataOutput out) throws IOException {
934                if (!isValid())
935                    throw new IllegalStateException();
936    
937                out.writeShort(_nameIndex);
938                if (_values == null)
939                    write(_value, out);
940                else {
941                    out.writeByte('[');
942                    out.writeShort(_values.length);
943                    for (int i = 0; i < _values.length; i++)
944                        write(_values[i], out);
945                }
946            }
947    
948            /**
949             * Write the data for the given value to the stream.
950             */
951            private void write(Value val, DataOutput out) throws IOException {
952                if (val.index2 != -1) {
953                    out.writeByte('e');
954                    out.writeShort(val.index);
955                    out.writeShort(val.index2);
956                } else if (val.index != -1) {
957                    if (val.type != null) {
958                        switch (val.type.getName().charAt(0)) {
959                        case 'b':
960                            if (val.type == byte.class)
961                                out.writeByte('B');
962                            else 
963                                out.writeByte('Z');
964                            break;
965                        case 'c':
966                            out.writeByte('C');
967                            break;
968                        case 'j': // java.lang.Class
969                            out.writeByte('c');
970                            break;
971                        case 's':
972                            out.writeByte('S');
973                            break;
974                        default:
975                            throw new IllegalStateException(val.type.getName());
976                        }
977                    } else {
978                        Entry entry = getPool().getEntry(val.index);
979                        if (entry instanceof DoubleEntry) 
980                            out.writeByte('D');
981                        else if (entry instanceof FloatEntry)
982                            out.writeByte('F');
983                        else if (entry instanceof IntEntry)
984                            out.writeByte('I');
985                        else if (entry instanceof LongEntry)
986                            out.writeByte('J');
987                        else if (entry instanceof UTF8Entry)
988                            out.writeByte('s');
989                        else
990                            throw new IllegalStateException(entry.getClass().
991                                getName());
992                    }
993                    out.writeShort(val.index);
994                } else {
995                    out.writeByte('@');
996                    ((Annotation) val.value).write(out);
997                }
998            }
999    
1000            /**
1001             * Property value struct.
1002             */ 
1003            private static class Value {
1004                public int index = -1;
1005                public int index2 = -1;
1006                public Class type = null;
1007                public Object value = null;
1008            }
1009        }
1010    }