• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  */
15 
16 package javassist.bytecode;
17 
18 import java.io.DataInputStream;
19 import java.io.DataOutputStream;
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.ListIterator;
24 import java.util.Map;
25 import javassist.CannotCompileException;
26 
27 /**
28  * <code>ClassFile</code> represents a Java <code>.class</code> file, which
29  * consists of a constant pool, methods, fields, and attributes.
30  *
31  * @see javassist.CtClass#getClassFile()
32  */
33 public final class ClassFile {
34     int major, minor; // version number
35     ConstPool constPool;
36     int thisClass;
37     int accessFlags;
38     int superClass;
39     int[] interfaces;
40     ArrayList fields;
41     ArrayList methods;
42     ArrayList attributes;
43     String thisclassname; // not JVM-internal name
44     String[] cachedInterfaces;
45     String cachedSuperclass;
46 
47     /**
48      * The major version number of class files
49      * for JDK 1.1.
50      */
51     public static final int JAVA_1 = 45;
52 
53     /**
54      * The major version number of class files
55      * for JDK 1.2.
56      */
57     public static final int JAVA_2 = 46;
58 
59     /**
60      * The major version number of class files
61      * for JDK 1.3.
62      */
63     public static final int JAVA_3 = 47;
64 
65     /**
66      * The major version number of class files
67      * for JDK 1.4.
68      */
69     public static final int JAVA_4 = 48;
70 
71     /**
72      * The major version number of class files
73      * for JDK 1.5.
74      */
75     public static final int JAVA_5 = 49;
76 
77     /**
78      * The major version number of class files
79      * for JDK 1.6.
80      */
81     public static final int JAVA_6 = 50;
82 
83     /**
84      * The major version number of class files
85      * for JDK 1.7.
86      */
87     public static final int JAVA_7 = 51;
88 
89     /**
90      * The major version number of class files created
91      * from scratch.  The default value is 47 (JDK 1.3)
92      * or 49 (JDK 1.5) if the JVM supports <code>java.lang.StringBuilder</code>.
93      */
94     public static int MAJOR_VERSION = JAVA_3;
95 
96     static {
97         try {
98             Class.forName("java.lang.StringBuilder");
99             MAJOR_VERSION = JAVA_5;
100         }
101         catch (Throwable t) {}
102     }
103 
104     /**
105      * Constructs a class file from a byte stream.
106      */
ClassFile(DataInputStream in)107     public ClassFile(DataInputStream in) throws IOException {
108         read(in);
109     }
110 
111     /**
112      * Constructs a class file including no members.
113      *
114      * @param isInterface
115      *            true if this is an interface. false if this is a class.
116      * @param classname
117      *            a fully-qualified class name
118      * @param superclass
119      *            a fully-qualified super class name
120      */
ClassFile(boolean isInterface, String classname, String superclass)121     public ClassFile(boolean isInterface, String classname, String superclass) {
122         major = MAJOR_VERSION;
123         minor = 0; // JDK 1.3 or later
124         constPool = new ConstPool(classname);
125         thisClass = constPool.getThisClassInfo();
126         if (isInterface)
127             accessFlags = AccessFlag.INTERFACE | AccessFlag.ABSTRACT;
128         else
129             accessFlags = AccessFlag.SUPER;
130 
131         initSuperclass(superclass);
132         interfaces = null;
133         fields = new ArrayList();
134         methods = new ArrayList();
135         thisclassname = classname;
136 
137         attributes = new ArrayList();
138         attributes.add(new SourceFileAttribute(constPool,
139                 getSourcefileName(thisclassname)));
140     }
141 
initSuperclass(String superclass)142     private void initSuperclass(String superclass) {
143         if (superclass != null) {
144             this.superClass = constPool.addClassInfo(superclass);
145             cachedSuperclass = superclass;
146         }
147         else {
148             this.superClass = constPool.addClassInfo("java.lang.Object");
149             cachedSuperclass = "java.lang.Object";
150         }
151     }
152 
getSourcefileName(String qname)153     private static String getSourcefileName(String qname) {
154         int index = qname.lastIndexOf('.');
155         if (index >= 0)
156             qname = qname.substring(index + 1);
157 
158         return qname + ".java";
159     }
160 
161     /**
162      * Eliminates dead constant pool items. If a method or a field is removed,
163      * the constant pool items used by that method/field become dead items. This
164      * method recreates a constant pool.
165      */
compact()166     public void compact() {
167         ConstPool cp = compact0();
168         ArrayList list = methods;
169         int n = list.size();
170         for (int i = 0; i < n; ++i) {
171             MethodInfo minfo = (MethodInfo)list.get(i);
172             minfo.compact(cp);
173         }
174 
175         list = fields;
176         n = list.size();
177         for (int i = 0; i < n; ++i) {
178             FieldInfo finfo = (FieldInfo)list.get(i);
179             finfo.compact(cp);
180         }
181 
182         attributes = AttributeInfo.copyAll(attributes, cp);
183         constPool = cp;
184     }
185 
compact0()186     private ConstPool compact0() {
187         ConstPool cp = new ConstPool(thisclassname);
188         thisClass = cp.getThisClassInfo();
189         String sc = getSuperclass();
190         if (sc != null)
191             superClass = cp.addClassInfo(getSuperclass());
192 
193         if (interfaces != null) {
194             int n = interfaces.length;
195             for (int i = 0; i < n; ++i)
196                 interfaces[i]
197                     = cp.addClassInfo(constPool.getClassInfo(interfaces[i]));
198         }
199 
200         return cp;
201     }
202 
203     /**
204      * Discards all attributes, associated with both the class file and the
205      * members such as a code attribute and exceptions attribute. The unused
206      * constant pool entries are also discarded (a new packed constant pool is
207      * constructed).
208      */
prune()209     public void prune() {
210         ConstPool cp = compact0();
211         ArrayList newAttributes = new ArrayList();
212         AttributeInfo invisibleAnnotations
213             = getAttribute(AnnotationsAttribute.invisibleTag);
214         if (invisibleAnnotations != null) {
215             invisibleAnnotations = invisibleAnnotations.copy(cp, null);
216             newAttributes.add(invisibleAnnotations);
217         }
218 
219         AttributeInfo visibleAnnotations
220             = getAttribute(AnnotationsAttribute.visibleTag);
221         if (visibleAnnotations != null) {
222             visibleAnnotations = visibleAnnotations.copy(cp, null);
223             newAttributes.add(visibleAnnotations);
224         }
225 
226         AttributeInfo signature
227             = getAttribute(SignatureAttribute.tag);
228         if (signature != null) {
229             signature = signature.copy(cp, null);
230             newAttributes.add(signature);
231         }
232 
233         ArrayList list = methods;
234         int n = list.size();
235         for (int i = 0; i < n; ++i) {
236             MethodInfo minfo = (MethodInfo)list.get(i);
237             minfo.prune(cp);
238         }
239 
240         list = fields;
241         n = list.size();
242         for (int i = 0; i < n; ++i) {
243             FieldInfo finfo = (FieldInfo)list.get(i);
244             finfo.prune(cp);
245         }
246 
247         attributes = newAttributes;
248         constPool = cp;
249     }
250 
251     /**
252      * Returns a constant pool table.
253      */
getConstPool()254     public ConstPool getConstPool() {
255         return constPool;
256     }
257 
258     /**
259      * Returns true if this is an interface.
260      */
isInterface()261     public boolean isInterface() {
262         return (accessFlags & AccessFlag.INTERFACE) != 0;
263     }
264 
265     /**
266      * Returns true if this is a final class or interface.
267      */
isFinal()268     public boolean isFinal() {
269         return (accessFlags & AccessFlag.FINAL) != 0;
270     }
271 
272     /**
273      * Returns true if this is an abstract class or an interface.
274      */
isAbstract()275     public boolean isAbstract() {
276         return (accessFlags & AccessFlag.ABSTRACT) != 0;
277     }
278 
279     /**
280      * Returns access flags.
281      *
282      * @see javassist.bytecode.AccessFlag
283      */
getAccessFlags()284     public int getAccessFlags() {
285         return accessFlags;
286     }
287 
288     /**
289      * Changes access flags.
290      *
291      * @see javassist.bytecode.AccessFlag
292      */
setAccessFlags(int acc)293     public void setAccessFlags(int acc) {
294         if ((acc & AccessFlag.INTERFACE) == 0)
295             acc |= AccessFlag.SUPER;
296 
297         accessFlags = acc;
298     }
299 
300     /**
301      * Returns access and property flags of this nested class.
302      * This method returns -1 if the class is not a nested class.
303      *
304      * <p>The returned value is obtained from <code>inner_class_access_flags</code>
305      * of the entry representing this nested class itself
306      * in <code>InnerClasses_attribute</code>>.
307      */
getInnerAccessFlags()308     public int getInnerAccessFlags() {
309         InnerClassesAttribute ica
310             = (InnerClassesAttribute)getAttribute(InnerClassesAttribute.tag);
311         if (ica == null)
312             return -1;
313 
314         String name = getName();
315         int n = ica.tableLength();
316         for (int i = 0; i < n; ++i)
317             if (name.equals(ica.innerClass(i)))
318                 return ica.accessFlags(i);
319 
320         return -1;
321     }
322 
323     /**
324      * Returns the class name.
325      */
getName()326     public String getName() {
327         return thisclassname;
328     }
329 
330     /**
331      * Sets the class name. This method substitutes the new name for all
332      * occurrences of the old class name in the class file.
333      */
setName(String name)334     public void setName(String name) {
335         renameClass(thisclassname, name);
336     }
337 
338     /**
339      * Returns the super class name.
340      */
getSuperclass()341     public String getSuperclass() {
342         if (cachedSuperclass == null)
343             cachedSuperclass = constPool.getClassInfo(superClass);
344 
345         return cachedSuperclass;
346     }
347 
348     /**
349      * Returns the index of the constant pool entry representing the super
350      * class.
351      */
getSuperclassId()352     public int getSuperclassId() {
353         return superClass;
354     }
355 
356     /**
357      * Sets the super class.
358      *
359      * <p>
360      * The new super class should inherit from the old super class.
361      * This method modifies constructors so that they call constructors declared
362      * in the new super class.
363      */
setSuperclass(String superclass)364     public void setSuperclass(String superclass) throws CannotCompileException {
365         if (superclass == null)
366             superclass = "java.lang.Object";
367 
368         try {
369             this.superClass = constPool.addClassInfo(superclass);
370             ArrayList list = methods;
371             int n = list.size();
372             for (int i = 0; i < n; ++i) {
373                 MethodInfo minfo = (MethodInfo)list.get(i);
374                 minfo.setSuperclass(superclass);
375             }
376         }
377         catch (BadBytecode e) {
378             throw new CannotCompileException(e);
379         }
380         cachedSuperclass = superclass;
381     }
382 
383     /**
384      * Replaces all occurrences of a class name in the class file.
385      *
386      * <p>
387      * If class X is substituted for class Y in the class file, X and Y must
388      * have the same signature. If Y provides a method m(), X must provide it
389      * even if X inherits m() from the super class. If this fact is not
390      * guaranteed, the bytecode verifier may cause an error.
391      *
392      * @param oldname
393      *            the replaced class name
394      * @param newname
395      *            the substituted class name
396      */
renameClass(String oldname, String newname)397     public final void renameClass(String oldname, String newname) {
398         ArrayList list;
399         int n;
400 
401         if (oldname.equals(newname))
402             return;
403 
404         if (oldname.equals(thisclassname))
405             thisclassname = newname;
406 
407         oldname = Descriptor.toJvmName(oldname);
408         newname = Descriptor.toJvmName(newname);
409         constPool.renameClass(oldname, newname);
410 
411         AttributeInfo.renameClass(attributes, oldname, newname);
412         list = methods;
413         n = list.size();
414         for (int i = 0; i < n; ++i) {
415             MethodInfo minfo = (MethodInfo)list.get(i);
416             String desc = minfo.getDescriptor();
417             minfo.setDescriptor(Descriptor.rename(desc, oldname, newname));
418             AttributeInfo.renameClass(minfo.getAttributes(), oldname, newname);
419         }
420 
421         list = fields;
422         n = list.size();
423         for (int i = 0; i < n; ++i) {
424             FieldInfo finfo = (FieldInfo)list.get(i);
425             String desc = finfo.getDescriptor();
426             finfo.setDescriptor(Descriptor.rename(desc, oldname, newname));
427             AttributeInfo.renameClass(finfo.getAttributes(), oldname, newname);
428         }
429     }
430 
431     /**
432      * Replaces all occurrences of several class names in the class file.
433      *
434      * @param classnames
435      *            specifies which class name is replaced with which new name.
436      *            Class names must be described with the JVM-internal
437      *            representation like <code>java/lang/Object</code>.
438      * @see #renameClass(String,String)
439      */
renameClass(Map classnames)440     public final void renameClass(Map classnames) {
441         String jvmNewThisName = (String)classnames.get(Descriptor
442                 .toJvmName(thisclassname));
443         if (jvmNewThisName != null)
444             thisclassname = Descriptor.toJavaName(jvmNewThisName);
445 
446         constPool.renameClass(classnames);
447 
448         AttributeInfo.renameClass(attributes, classnames);
449         ArrayList list = methods;
450         int n = list.size();
451         for (int i = 0; i < n; ++i) {
452             MethodInfo minfo = (MethodInfo)list.get(i);
453             String desc = minfo.getDescriptor();
454             minfo.setDescriptor(Descriptor.rename(desc, classnames));
455             AttributeInfo.renameClass(minfo.getAttributes(), classnames);
456         }
457 
458         list = fields;
459         n = list.size();
460         for (int i = 0; i < n; ++i) {
461             FieldInfo finfo = (FieldInfo)list.get(i);
462             String desc = finfo.getDescriptor();
463             finfo.setDescriptor(Descriptor.rename(desc, classnames));
464             AttributeInfo.renameClass(finfo.getAttributes(), classnames);
465         }
466     }
467 
468     /**
469      * Internal-use only.
470      * <code>CtClass.getRefClasses()</code> calls this method.
471      */
getRefClasses(Map classnames)472     public final void getRefClasses(Map classnames) {
473         constPool.renameClass(classnames);
474 
475         AttributeInfo.getRefClasses(attributes, classnames);
476         ArrayList list = methods;
477         int n = list.size();
478         for (int i = 0; i < n; ++i) {
479             MethodInfo minfo = (MethodInfo)list.get(i);
480             String desc = minfo.getDescriptor();
481             Descriptor.rename(desc, classnames);
482             AttributeInfo.getRefClasses(minfo.getAttributes(), classnames);
483         }
484 
485         list = fields;
486         n = list.size();
487         for (int i = 0; i < n; ++i) {
488             FieldInfo finfo = (FieldInfo)list.get(i);
489             String desc = finfo.getDescriptor();
490             Descriptor.rename(desc, classnames);
491             AttributeInfo.getRefClasses(finfo.getAttributes(), classnames);
492         }
493     }
494 
495     /**
496      * Returns the names of the interfaces implemented by the class.
497      * The returned array is read only.
498      */
getInterfaces()499     public String[] getInterfaces() {
500         if (cachedInterfaces != null)
501             return cachedInterfaces;
502 
503         String[] rtn = null;
504         if (interfaces == null)
505             rtn = new String[0];
506         else {
507             int n = interfaces.length;
508             String[] list = new String[n];
509             for (int i = 0; i < n; ++i)
510                 list[i] = constPool.getClassInfo(interfaces[i]);
511 
512             rtn = list;
513         }
514 
515         cachedInterfaces = rtn;
516         return rtn;
517     }
518 
519     /**
520      * Sets the interfaces.
521      *
522      * @param nameList
523      *            the names of the interfaces.
524      */
setInterfaces(String[] nameList)525     public void setInterfaces(String[] nameList) {
526         cachedInterfaces = null;
527         if (nameList != null) {
528             int n = nameList.length;
529             interfaces = new int[n];
530             for (int i = 0; i < n; ++i)
531                 interfaces[i] = constPool.addClassInfo(nameList[i]);
532         }
533     }
534 
535     /**
536      * Appends an interface to the interfaces implemented by the class.
537      */
addInterface(String name)538     public void addInterface(String name) {
539         cachedInterfaces = null;
540         int info = constPool.addClassInfo(name);
541         if (interfaces == null) {
542             interfaces = new int[1];
543             interfaces[0] = info;
544         }
545         else {
546             int n = interfaces.length;
547             int[] newarray = new int[n + 1];
548             System.arraycopy(interfaces, 0, newarray, 0, n);
549             newarray[n] = info;
550             interfaces = newarray;
551         }
552     }
553 
554     /**
555      * Returns all the fields declared in the class.
556      *
557      * @return a list of <code>FieldInfo</code>.
558      * @see FieldInfo
559      */
getFields()560     public List getFields() {
561         return fields;
562     }
563 
564     /**
565      * Appends a field to the class.
566      *
567      * @throws DuplicateMemberException         when the field is already included.
568      */
addField(FieldInfo finfo)569     public void addField(FieldInfo finfo) throws DuplicateMemberException {
570         testExistingField(finfo.getName(), finfo.getDescriptor());
571         fields.add(finfo);
572     }
573 
574     /**
575      * Just appends a field to the class.
576      * It does not check field duplication.
577      * Use this method only when minimizing performance overheads
578      * is seriously required.
579      *
580      * @since 3.13
581      */
addField2(FieldInfo finfo)582     public final void addField2(FieldInfo finfo) {
583         fields.add(finfo);
584     }
585 
testExistingField(String name, String descriptor)586     private void testExistingField(String name, String descriptor)
587             throws DuplicateMemberException {
588         ListIterator it = fields.listIterator(0);
589         while (it.hasNext()) {
590             FieldInfo minfo = (FieldInfo)it.next();
591             if (minfo.getName().equals(name))
592                 throw new DuplicateMemberException("duplicate field: " + name);
593         }
594     }
595 
596     /**
597      * Returns all the methods declared in the class.
598      *
599      * @return a list of <code>MethodInfo</code>.
600      * @see MethodInfo
601      */
getMethods()602     public List getMethods() {
603         return methods;
604     }
605 
606     /**
607      * Returns the method with the specified name. If there are multiple methods
608      * with that name, this method returns one of them.
609      *
610      * @return null if no such method is found.
611      */
getMethod(String name)612     public MethodInfo getMethod(String name) {
613         ArrayList list = methods;
614         int n = list.size();
615         for (int i = 0; i < n; ++i) {
616             MethodInfo minfo = (MethodInfo)list.get(i);
617             if (minfo.getName().equals(name))
618                 return minfo;
619         }
620 
621         return null;
622     }
623 
624     /**
625      * Returns a static initializer (class initializer), or null if it does not
626      * exist.
627      */
getStaticInitializer()628     public MethodInfo getStaticInitializer() {
629         return getMethod(MethodInfo.nameClinit);
630     }
631 
632     /**
633      * Appends a method to the class.
634      * If there is a bridge method with the same name and signature,
635      * then the bridge method is removed before a new method is added.
636      *
637      * @throws DuplicateMemberException         when the method is already included.
638      */
addMethod(MethodInfo minfo)639     public void addMethod(MethodInfo minfo) throws DuplicateMemberException {
640         testExistingMethod(minfo);
641         methods.add(minfo);
642     }
643 
644     /**
645      * Just appends a method to the class.
646      * It does not check method duplication or remove a bridge method.
647      * Use this method only when minimizing performance overheads
648      * is seriously required.
649      *
650      * @since 3.13
651      */
addMethod2(MethodInfo minfo)652     public final void addMethod2(MethodInfo minfo) {
653         methods.add(minfo);
654     }
655 
testExistingMethod(MethodInfo newMinfo)656     private void testExistingMethod(MethodInfo newMinfo)
657         throws DuplicateMemberException
658     {
659         String name = newMinfo.getName();
660         String descriptor = newMinfo.getDescriptor();
661         ListIterator it = methods.listIterator(0);
662         while (it.hasNext())
663             if (isDuplicated(newMinfo, name, descriptor, (MethodInfo)it.next(), it))
664                 throw new DuplicateMemberException("duplicate method: " + name
665                                                    + " in " + this.getName());
666     }
667 
isDuplicated(MethodInfo newMethod, String newName, String newDesc, MethodInfo minfo, ListIterator it)668     private static boolean isDuplicated(MethodInfo newMethod, String newName,
669                                         String newDesc, MethodInfo minfo,
670                                         ListIterator it)
671     {
672         if (!minfo.getName().equals(newName))
673             return false;
674 
675         String desc = minfo.getDescriptor();
676         if (!Descriptor.eqParamTypes(desc, newDesc))
677            return false;
678 
679         if (desc.equals(newDesc)) {
680             if (notBridgeMethod(minfo))
681                 return true;
682             else {
683                 it.remove();
684                 return false;
685             }
686         }
687         else
688            return notBridgeMethod(minfo) && notBridgeMethod(newMethod);
689     }
690 
691     /* For a bridge method, see Sec. 15.12.4.5 of JLS 3rd Ed.
692      */
notBridgeMethod(MethodInfo minfo)693     private static boolean notBridgeMethod(MethodInfo minfo) {
694         return (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0;
695     }
696 
697     /**
698      * Returns all the attributes.  The returned <code>List</code> object
699      * is shared with this object.  If you add a new attribute to the list,
700      * the attribute is also added to the classs file represented by this
701      * object.  If you remove an attribute from the list, it is also removed
702      * from the class file.
703      *
704      * @return a list of <code>AttributeInfo</code> objects.
705      * @see AttributeInfo
706      */
getAttributes()707     public List getAttributes() {
708         return attributes;
709     }
710 
711     /**
712      * Returns the attribute with the specified name.  If there are multiple
713      * attributes with that name, this method returns either of them.   It
714      * returns null if the specified attributed is not found.
715      *
716      * @param name          attribute name
717      * @see #getAttributes()
718      */
getAttribute(String name)719     public AttributeInfo getAttribute(String name) {
720         ArrayList list = attributes;
721         int n = list.size();
722         for (int i = 0; i < n; ++i) {
723             AttributeInfo ai = (AttributeInfo)list.get(i);
724             if (ai.getName().equals(name))
725                 return ai;
726         }
727 
728         return null;
729     }
730 
731     /**
732      * Appends an attribute. If there is already an attribute with the same
733      * name, the new one substitutes for it.
734      *
735      * @see #getAttributes()
736      */
addAttribute(AttributeInfo info)737     public void addAttribute(AttributeInfo info) {
738         AttributeInfo.remove(attributes, info.getName());
739         attributes.add(info);
740     }
741 
742     /**
743      * Returns the source file containing this class.
744      *
745      * @return null if this information is not available.
746      */
getSourceFile()747     public String getSourceFile() {
748         SourceFileAttribute sf
749             = (SourceFileAttribute)getAttribute(SourceFileAttribute.tag);
750         if (sf == null)
751             return null;
752         else
753             return sf.getFileName();
754     }
755 
read(DataInputStream in)756     private void read(DataInputStream in) throws IOException {
757         int i, n;
758         int magic = in.readInt();
759         if (magic != 0xCAFEBABE)
760             throw new IOException("bad magic number: " + Integer.toHexString(magic));
761 
762         minor = in.readUnsignedShort();
763         major = in.readUnsignedShort();
764         constPool = new ConstPool(in);
765         accessFlags = in.readUnsignedShort();
766         thisClass = in.readUnsignedShort();
767         constPool.setThisClassInfo(thisClass);
768         superClass = in.readUnsignedShort();
769         n = in.readUnsignedShort();
770         if (n == 0)
771             interfaces = null;
772         else {
773             interfaces = new int[n];
774             for (i = 0; i < n; ++i)
775                 interfaces[i] = in.readUnsignedShort();
776         }
777 
778         ConstPool cp = constPool;
779         n = in.readUnsignedShort();
780         fields = new ArrayList();
781         for (i = 0; i < n; ++i)
782             addField2(new FieldInfo(cp, in));
783 
784         n = in.readUnsignedShort();
785         methods = new ArrayList();
786         for (i = 0; i < n; ++i)
787             addMethod2(new MethodInfo(cp, in));
788 
789         attributes = new ArrayList();
790         n = in.readUnsignedShort();
791         for (i = 0; i < n; ++i)
792             addAttribute(AttributeInfo.read(cp, in));
793 
794         thisclassname = constPool.getClassInfo(thisClass);
795     }
796 
797     /**
798      * Writes a class file represened by this object into an output stream.
799      */
write(DataOutputStream out)800     public void write(DataOutputStream out) throws IOException {
801         int i, n;
802 
803         out.writeInt(0xCAFEBABE); // magic
804         out.writeShort(minor); // minor version
805         out.writeShort(major); // major version
806         constPool.write(out); // constant pool
807         out.writeShort(accessFlags);
808         out.writeShort(thisClass);
809         out.writeShort(superClass);
810 
811         if (interfaces == null)
812             n = 0;
813         else
814             n = interfaces.length;
815 
816         out.writeShort(n);
817         for (i = 0; i < n; ++i)
818             out.writeShort(interfaces[i]);
819 
820         ArrayList list = fields;
821         n = list.size();
822         out.writeShort(n);
823         for (i = 0; i < n; ++i) {
824             FieldInfo finfo = (FieldInfo)list.get(i);
825             finfo.write(out);
826         }
827 
828         list = methods;
829         n = list.size();
830         out.writeShort(n);
831         for (i = 0; i < n; ++i) {
832             MethodInfo minfo = (MethodInfo)list.get(i);
833             minfo.write(out);
834         }
835 
836         out.writeShort(attributes.size());
837         AttributeInfo.writeAll(attributes, out);
838     }
839 
840     /**
841      * Get the Major version.
842      *
843      * @return the major version
844      */
getMajorVersion()845     public int getMajorVersion() {
846         return major;
847     }
848 
849     /**
850      * Set the major version.
851      *
852      * @param major
853      *            the major version
854      */
setMajorVersion(int major)855     public void setMajorVersion(int major) {
856         this.major = major;
857     }
858 
859     /**
860      * Get the minor version.
861      *
862      * @return the minor version
863      */
getMinorVersion()864     public int getMinorVersion() {
865         return minor;
866     }
867 
868     /**
869      * Set the minor version.
870      *
871      * @param minor
872      *            the minor version
873      */
setMinorVersion(int minor)874     public void setMinorVersion(int minor) {
875         this.minor = minor;
876     }
877 
878     /**
879      * Sets the major and minor version to Java 5.
880      *
881      * If the major version is older than 49, Java 5
882      * extensions such as annotations are ignored
883      * by the JVM.
884      */
setVersionToJava5()885     public void setVersionToJava5() {
886         this.major = 49;
887         this.minor = 0;
888     }
889 }
890