001package serp.bytecode;
002
003import java.io.*;
004import java.lang.reflect.*;
005import java.util.*;
006
007import serp.bytecode.lowlevel.*;
008import serp.bytecode.visitor.*;
009import serp.util.*;
010
011/**
012 * A declared annotation.
013 *
014 * @author Abe White
015 */
016public 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(), true);
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}