View Javadoc

1   package serp.bytecode;
2   
3   import java.io.*;
4   import java.lang.reflect.*;
5   import java.util.*;
6   
7   import serp.bytecode.lowlevel.*;
8   import serp.bytecode.visitor.*;
9   import serp.util.*;
10  
11  /***
12   * A declared annotation.
13   *
14   * @author Abe White
15   */
16  public class Annotation implements BCEntity, VisitAcceptor {
17      private static Method ENUM_VALUEOF = null;
18      private static Method ENUM_NAME = null;
19      static {
20          try {
21              Class c = Class.forName("java.lang.Enum");
22              ENUM_VALUEOF = c.getMethod("valueOf", new Class[] {
23                  Class.class, String.class });
24              ENUM_NAME = c.getMethod("name", (Class[]) null);
25          } catch (Throwable t) {
26              // pre-1.5 JDK
27          }
28      }
29   
30      private BCEntity _owner = null;
31      private int _typeIndex = 0;
32      private List _properties = null;
33  
34      Annotation(BCEntity owner) {
35          _owner = owner;
36      }
37  
38      /***
39       * Annotations are stored in an {@link Annotations} table or as
40       * part of an {@link Annotation} property value.
41       */
42      public BCEntity getOwner() {
43          return _owner;
44      }
45  
46      void invalidate() {
47          _owner = null;
48      }
49  
50      /***
51       * The index in the class {@link ConstantPool} of the
52       * {@link UTF8Entry} holding the type of this annotation.
53       */
54      public int getTypeIndex() {
55          return _typeIndex;
56      }
57  
58      /***
59       * The index in the class {@link ConstantPool} of the
60       * {@link UTF8Entry} holding the type of this annotation.
61       */
62      public void setTypeIndex(int index) {
63          _typeIndex = index;
64      }
65  
66      /***
67       * The name of this annotation's type.
68       */
69      public String getTypeName() {
70          String desc = ((UTF8Entry) getPool().getEntry(_typeIndex)).getValue();
71          return getProject().getNameCache().getExternalForm(desc, false);
72      }
73  
74      /***
75       * The {@link Class} object for this annotation's type.
76       */
77      public Class getType() {
78          return Strings.toClass(getTypeName(), getClassLoader());
79      }
80  
81      /***
82       * The bytecode for the type of this annotation.
83       */
84      public BCClass getTypeBC() {
85          return getProject().loadClass(getTypeName(), getClassLoader());
86      }
87  
88      /***
89       * This annotation's type.
90       */
91      public void setType(String type) {
92          type = getProject().getNameCache().getInternalForm(type, true);
93          _typeIndex = getPool().findUTF8Entry(type, true);
94      }
95  
96      /***
97       * This annotation's type.
98       */
99      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 }