View Javadoc

    1   /*
    2    * Copyright  2000-2004 The Apache Software Foundation
    3    *
    4    *  Licensed under the Apache License, Version 2.0 (the "License");
    5    *  you may not use this file except in compliance with the License.
    6    *  You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   *  Unless required by applicable law or agreed to in writing, software
    11   *  distributed under the License is distributed on an "AS IS" BASIS,
    12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *  See the License for the specific language governing permissions and
    14   *  limitations under the License.
    15   *
    16   */
    17  package org.apache.bcel.generic;
    18
    19  import java.util.ArrayList;
    20  import java.util.Iterator;
    21  import java.util.List;
    22  import org.apache.bcel.Constants;
    23  import org.apache.bcel.classfile.AccessFlags;
    24  import org.apache.bcel.classfile.Attribute;
    25  import org.apache.bcel.classfile.ConstantPool;
    26  import org.apache.bcel.classfile.Field;
    27  import org.apache.bcel.classfile.JavaClass;
    28  import org.apache.bcel.classfile.Method;
    29  import org.apache.bcel.classfile.SourceFile;
    30  import org.apache.bcel.util.BCELComparator;
    31
    32  /***
    33   * Template class for building up a java class. May be initialized with an
    34   * existing java class (file).
    35   *
    36   * @see JavaClass
    37   * @version $Id: ClassGen.java 386056 2006-03-15 11:31:56Z tcurdt $
    38   * @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
    39   */
    40  public class ClassGen extends AccessFlags implements Cloneable {
    41
    42      /* Corresponds to the fields found in a JavaClass object.
    43       */
    44      private String class_name, super_class_name, file_name;
    45      private int class_name_index = -1, superclass_name_index = -1;
    46      private int major = Constants.MAJOR_1_1, minor = Constants.MINOR_1_1;
    47      private ConstantPoolGen cp; // Template for building up constant pool
    48      // ArrayLists instead of arrays to gather fields, methods, etc.
    49      private List field_vec = new ArrayList();
    50      private List method_vec = new ArrayList();
    51      private List attribute_vec = new ArrayList();
    52      private List interface_vec = new ArrayList();
    53      private static BCELComparator _cmp = new BCELComparator() {
    54
    55          public boolean equals( Object o1, Object o2 ) {
    56              ClassGen THIS = (ClassGen) o1;
    57              ClassGen THAT = (ClassGen) o2;
    58              return THIS.getClassName().equals(THAT.getClassName());
    59          }
    60
    61
    62          public int hashCode( Object o ) {
    63              ClassGen THIS = (ClassGen) o;
    64              return THIS.getClassName().hashCode();
    65          }
    66      };
    67
    68
    69      /*** Convenience constructor to set up some important values initially.
    70       *
    71       * @param class_name fully qualified class name
    72       * @param super_class_name fully qualified superclass name
    73       * @param file_name source file name
    74       * @param access_flags access qualifiers
    75       * @param interfaces implemented interfaces
    76       * @param cp constant pool to use
    77       */
    78      public ClassGen(String class_name, String super_class_name, String file_name, int access_flags,
    79              String[] interfaces, ConstantPoolGen cp) {
    80          this.class_name = class_name;
    81          this.super_class_name = super_class_name;
    82          this.file_name = file_name;
    83          this.access_flags = access_flags;
    84          this.cp = cp;
    85          // Put everything needed by default into the constant pool and the vectors
    86          if (file_name != null) {
    87              addAttribute(new SourceFile(cp.addUtf8("SourceFile"), 2, cp.addUtf8(file_name), cp
    88                      .getConstantPool()));
    89          }
    90          class_name_index = cp.addClass(class_name);
    91          superclass_name_index = cp.addClass(super_class_name);
    92          if (interfaces != null) {
    93              for (int i = 0; i < interfaces.length; i++) {
    94                  addInterface(interfaces[i]);
    95              }
    96          }
    97      }
    98
    99
    100     /*** Convenience constructor to set up some important values initially.
    101      *
    102      * @param class_name fully qualified class name
    103      * @param super_class_name fully qualified superclass name
    104      * @param file_name source file name
    105      * @param access_flags access qualifiers
    106      * @param interfaces implemented interfaces
    107      */
    108     public ClassGen(String class_name, String super_class_name, String file_name, int access_flags,
    109             String[] interfaces) {
    110         this(class_name, super_class_name, file_name, access_flags, interfaces,
    111                 new ConstantPoolGen());
    112     }
    113
    114
    115     /***
    116      * Initialize with existing class.
    117      * @param clazz JavaClass object (e.g. read from file)
    118      */
    119     public ClassGen(JavaClass clazz) {
    120         class_name_index = clazz.getClassNameIndex();
    121         superclass_name_index = clazz.getSuperclassNameIndex();
    122         class_name = clazz.getClassName();
    123         super_class_name = clazz.getSuperclassName();
    124         file_name = clazz.getSourceFileName();
    125         access_flags = clazz.getAccessFlags();
    126         cp = new ConstantPoolGen(clazz.getConstantPool());
    127         major = clazz.getMajor();
    128         minor = clazz.getMinor();
    129         Attribute[] attributes = clazz.getAttributes();
    130         Method[] methods = clazz.getMethods();
    131         Field[] fields = clazz.getFields();
    132         String[] interfaces = clazz.getInterfaceNames();
    133         for (int i = 0; i < interfaces.length; i++) {
    134             addInterface(interfaces[i]);
    135         }
    136         for (int i = 0; i < attributes.length; i++) {
    137             addAttribute(attributes[i]);
    138         }
    139         for (int i = 0; i < methods.length; i++) {
    140             addMethod(methods[i]);
    141         }
    142         for (int i = 0; i < fields.length; i++) {
    143             addField(fields[i]);
    144         }
    145     }
    146
    147
    148     /***
    149      * @return the (finally) built up Java class object.
    150      */
    151     public JavaClass getJavaClass() {
    152         int[] interfaces = getInterfaces();
    153         Field[] fields = getFields();
    154         Method[] methods = getMethods();
    155         Attribute[] attributes = getAttributes();
    156         // Must be last since the above calls may still add something to it
    157         ConstantPool _cp = this.cp.getFinalConstantPool();
    158         return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor,
    159                 access_flags, _cp, interfaces, fields, methods, attributes);
    160     }
    161
    162
    163     /***
    164      * Add an interface to this class, i.e., this class has to implement it.
    165      * @param name interface to implement (fully qualified class name)
    166      */
    167     public void addInterface( String name ) {
    168         interface_vec.add(name);
    169     }
    170
    171
    172     /***
    173      * Remove an interface from this class.
    174      * @param name interface to remove (fully qualified name)
    175      */
    176     public void removeInterface( String name ) {
    177         interface_vec.remove(name);
    178     }
    179
    180
    181     /***
    182      * @return major version number of class file
    183      */
    184     public int getMajor() {
    185         return major;
    186     }
    187
    188
    189     /*** Set major version number of class file, default value is 45 (JDK 1.1)
    190      * @param major major version number
    191      */
    192     public void setMajor( int major ) {
    193         this.major = major;
    194     }
    195
    196
    197     /*** Set minor version number of class file, default value is 3 (JDK 1.1)
    198      * @param minor minor version number
    199      */
    200     public void setMinor( int minor ) {
    201         this.minor = minor;
    202     }
    203
    204
    205     /***
    206      * @return minor version number of class file
    207      */
    208     public int getMinor() {
    209         return minor;
    210     }
    211
    212
    213     /***
    214      * Add an attribute to this class.
    215      * @param a attribute to add
    216      */
    217     public void addAttribute( Attribute a ) {
    218         attribute_vec.add(a);
    219     }
    220
    221
    222     /***
    223      * Add a method to this class.
    224      * @param m method to add
    225      */
    226     public void addMethod( Method m ) {
    227         method_vec.add(m);
    228     }
    229
    230
    231     /***
    232      * Convenience method.
    233      *
    234      * Add an empty constructor to this class that does nothing but calling super().
    235      * @param access_flags rights for constructor
    236      */
    237     public void addEmptyConstructor( int access_flags ) {
    238         InstructionList il = new InstructionList();
    239         il.append(InstructionConstants.THIS); // Push `this'
    240         il.append(new INVOKESPECIAL(cp.addMethodref(super_class_name, "<init>", "()V")));
    241         il.append(InstructionConstants.RETURN);
    242         MethodGen mg = new MethodGen(access_flags, Type.VOID, Type.NO_ARGS, null, "<init>",
    243                 class_name, il, cp);
    244         mg.setMaxStack(1);
    245         addMethod(mg.getMethod());
    246     }
    247
    248
    249     /***
    250      * Add a field to this class.
    251      * @param f field to add
    252      */
    253     public void addField( Field f ) {
    254         field_vec.add(f);
    255     }
    256
    257
    258     public boolean containsField( Field f ) {
    259         return field_vec.contains(f);
    260     }
    261
    262
    263     /*** @return field object with given name, or null
    264      */
    265     public Field containsField( String name ) {
    266         for (Iterator e = field_vec.iterator(); e.hasNext();) {
    267             Field f = (Field) e.next();
    268             if (f.getName().equals(name)) {
    269                 return f;
    270             }
    271         }
    272         return null;
    273     }
    274
    275
    276     /*** @return method object with given name and signature, or null
    277      */
    278     public Method containsMethod( String name, String signature ) {
    279         for (Iterator e = method_vec.iterator(); e.hasNext();) {
    280             Method m = (Method) e.next();
    281             if (m.getName().equals(name) && m.getSignature().equals(signature)) {
    282                 return m;
    283             }
    284         }
    285         return null;
    286     }
    287
    288
    289     /***
    290      * Remove an attribute from this class.
    291      * @param a attribute to remove
    292      */
    293     public void removeAttribute( Attribute a ) {
    294         attribute_vec.remove(a);
    295     }
    296
    297
    298     /***
    299      * Remove a method from this class.
    300      * @param m method to remove
    301      */
    302     public void removeMethod( Method m ) {
    303         method_vec.remove(m);
    304     }
    305
    306
    307     /*** Replace given method with new one. If the old one does not exist
    308      * add the new_ method to the class anyway.
    309      */
    310     public void replaceMethod( Method old, Method new_ ) {
    311         if (new_ == null) {
    312             throw new ClassGenException("Replacement method must not be null");
    313         }
    314         int i = method_vec.indexOf(old);
    315         if (i < 0) {
    316             method_vec.add(new_);
    317         } else {
    318             method_vec.set(i, new_);
    319         }
    320     }
    321
    322
    323     /*** Replace given field with new one. If the old one does not exist
    324      * add the new_ field to the class anyway.
    325      */
    326     public void replaceField( Field old, Field new_ ) {
    327         if (new_ == null) {
    328             throw new ClassGenException("Replacement method must not be null");
    329         }
    330         int i = field_vec.indexOf(old);
    331         if (i < 0) {
    332             field_vec.add(new_);
    333         } else {
    334             field_vec.set(i, new_);
    335         }
    336     }
    337
    338
    339     /***
    340      * Remove a field to this class.
    341      * @param f field to remove
    342      */
    343     public void removeField( Field f ) {
    344         field_vec.remove(f);
    345     }
    346
    347
    348     public String getClassName() {
    349         return class_name;
    350     }
    351
    352
    353     public String getSuperclassName() {
    354         return super_class_name;
    355     }
    356
    357
    358     public String getFileName() {
    359         return file_name;
    360     }
    361
    362
    363     public void setClassName( String name ) {
    364         class_name = name.replace('/', '.');
    365         class_name_index = cp.addClass(name);
    366     }
    367
    368
    369     public void setSuperclassName( String name ) {
    370         super_class_name = name.replace('/', '.');
    371         superclass_name_index = cp.addClass(name);
    372     }
    373
    374
    375     public Method[] getMethods() {
    376         return (Method[]) method_vec.toArray(new Method[method_vec.size()]);
    377     }
    378
    379
    380     public void setMethods( Method[] methods ) {
    381         method_vec.clear();
    382         for (int m = 0; m < methods.length; m++) {
    383             addMethod(methods[m]);
    384         }
    385     }
    386
    387
    388     public void setMethodAt( Method method, int pos ) {
    389         method_vec.set(pos, method);
    390     }
    391
    392
    393     public Method getMethodAt( int pos ) {
    394         return (Method) method_vec.get(pos);
    395     }
    396
    397
    398     public String[] getInterfaceNames() {
    399         int size = interface_vec.size();
    400         String[] interfaces = new String[size];
    401         interface_vec.toArray(interfaces);
    402         return interfaces;
    403     }
    404
    405
    406     public int[] getInterfaces() {
    407         int size = interface_vec.size();
    408         int[] interfaces = new int[size];
    409         for (int i = 0; i < size; i++) {
    410             interfaces[i] = cp.addClass((String) interface_vec.get(i));
    411         }
    412         return interfaces;
    413     }
    414
    415
    416     public Field[] getFields() {
    417         return (Field[]) field_vec.toArray(new Field[field_vec.size()]);
    418     }
    419
    420
    421     public Attribute[] getAttributes() {
    422         return (Attribute[]) attribute_vec.toArray(new Attribute[attribute_vec.size()]);
    423     }
    424
    425
    426     public ConstantPoolGen getConstantPool() {
    427         return cp;
    428     }
    429
    430
    431     public void setConstantPool( ConstantPoolGen constant_pool ) {
    432         cp = constant_pool;
    433     }
    434
    435
    436     public void setClassNameIndex( int class_name_index ) {
    437         this.class_name_index = class_name_index;
    438         class_name = cp.getConstantPool().getConstantString(class_name_index,
    439                 Constants.CONSTANT_Class).replace('/', '.');
    440     }
    441
    442
    443     public void setSuperclassNameIndex( int superclass_name_index ) {
    444         this.superclass_name_index = superclass_name_index;
    445         super_class_name = cp.getConstantPool().getConstantString(superclass_name_index,
    446                 Constants.CONSTANT_Class).replace('/', '.');
    447     }
    448
    449
    450     public int getSuperclassNameIndex() {
    451         return superclass_name_index;
    452     }
    453
    454
    455     public int getClassNameIndex() {
    456         return class_name_index;
    457     }
    458
    459     private ArrayList observers;
    460
    461
    462     /*** Add observer for this object.
    463      */
    464     public void addObserver( ClassObserver o ) {
    465         if (observers == null) {
    466             observers = new ArrayList();
    467         }
    468         observers.add(o);
    469     }
    470
    471
    472     /*** Remove observer for this object.
    473      */
    474     public void removeObserver( ClassObserver o ) {
    475         if (observers != null) {
    476             observers.remove(o);
    477         }
    478     }
    479
    480
    481     /*** Call notify() method on all observers. This method is not called
    482      * automatically whenever the state has changed, but has to be
    483      * called by the user after he has finished editing the object.
    484      */
    485     public void update() {
    486         if (observers != null) {
    487             for (Iterator e = observers.iterator(); e.hasNext();) {
    488                 ((ClassObserver) e.next()).notify(this);
    489             }
    490         }
    491     }
    492
    493
    494     public Object clone() {
    495         try {
    496             return super.clone();
    497         } catch (CloneNotSupportedException e) {
    498             System.err.println(e);
    499             return null;
    500         }
    501     }
    502
    503
    504     /***
    505      * @return Comparison strategy object
    506      */
    507     public static BCELComparator getComparator() {
    508         return _cmp;
    509     }
    510
    511
    512     /***
    513      * @param comparator Comparison strategy object
    514      */
    515     public static void setComparator( BCELComparator comparator ) {
    516         _cmp = comparator;
    517     }
    518
    519
    520     /***
    521      * Return value as defined by given BCELComparator strategy.
    522      * By default two ClassGen objects are said to be equal when
    523      * their class names are equal.
    524      *
    525      * @see java.lang.Object#equals(java.lang.Object)
    526      */
    527     public boolean equals( Object obj ) {
    528         return _cmp.equals(this, obj);
    529     }
    530
    531
    532     /***
    533      * Return value as defined by given BCELComparator strategy.
    534      * By default return the hashcode of the class name.
    535      *
    536      * @see java.lang.Object#hashCode()
    537      */
    538     public int hashCode() {
    539         return _cmp.hashCode(this);
    540     }
    541 }