Home | History | Annotate | Download | only in bytecode
      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      */
    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      */
    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 
    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 
    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      */
    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 
    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      */
    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      */
    254     public ConstPool getConstPool() {
    255         return constPool;
    256     }
    257 
    258     /**
    259      * Returns true if this is an interface.
    260      */
    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      */
    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      */
    275     public boolean isAbstract() {
    276         return (accessFlags & AccessFlag.ABSTRACT) != 0;
    277     }
    278 
    279     /**
    280      * Returns access flags.
    281      *
    282      * @see javassist.bytecode.AccessFlag
    283      */
    284     public int getAccessFlags() {
    285         return accessFlags;
    286     }
    287 
    288     /**
    289      * Changes access flags.
    290      *
    291      * @see javassist.bytecode.AccessFlag
    292      */
    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      */
    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      */
    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      */
    334     public void setName(String name) {
    335         renameClass(thisclassname, name);
    336     }
    337 
    338     /**
    339      * Returns the super class name.
    340      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    582     public final void addField2(FieldInfo finfo) {
    583         fields.add(finfo);
    584     }
    585 
    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      */
    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      */
    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      */
    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      */
    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      */
    652     public final void addMethod2(MethodInfo minfo) {
    653         methods.add(minfo);
    654     }
    655 
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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      */
    845     public int getMajorVersion() {
    846         return major;
    847     }
    848 
    849     /**
    850      * Set the major version.
    851      *
    852      * @param major
    853      *            the major version
    854      */
    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      */
    864     public int getMinorVersion() {
    865         return minor;
    866     }
    867 
    868     /**
    869      * Set the minor version.
    870      *
    871      * @param minor
    872      *            the minor version
    873      */
    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      */
    885     public void setVersionToJava5() {
    886         this.major = 49;
    887         this.minor = 0;
    888     }
    889 }
    890