    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 *
    017 */
    018package org.apache.bcel.generic;
    019
    020import java.util.ArrayList;
    021import java.util.List;
    022import java.util.Objects;
    023
    024import org.apache.bcel.Const;
    025import org.apache.bcel.classfile.AccessFlags;
    026import org.apache.bcel.classfile.AnnotationEntry;
    027import org.apache.bcel.classfile.Annotations;
    028import org.apache.bcel.classfile.Attribute;
    029import org.apache.bcel.classfile.ConstantPool;
    030import org.apache.bcel.classfile.Field;
    031import org.apache.bcel.classfile.JavaClass;
    032import org.apache.bcel.classfile.Method;
    033import org.apache.bcel.classfile.RuntimeInvisibleAnnotations;
    034import org.apache.bcel.classfile.RuntimeVisibleAnnotations;
    035import org.apache.bcel.classfile.SourceFile;
    036import org.apache.bcel.util.BCELComparator;
    037
    038/**
    039 * Template class for building up a java class. May be initialized with an
    040 * existing java class (file).
    041 *
    042 * @see JavaClass
    043 * @version $Id$
    044 */
    045public class ClassGen extends AccessFlags implements Cloneable {
    046
    047    /* Corresponds to the fields found in a JavaClass object.
    048     */
    049    private String class_name;
    050    private String super_class_name;
    051    private final String file_name;
    052    private int class_name_index = -1;
    053    private int superclass_name_index = -1;
    054    private int major = Const.MAJOR_1_1;
    055    private int minor = Const.MINOR_1_1;
    056    private ConstantPoolGen cp; // Template for building up constant pool
    057    // ArrayLists instead of arrays to gather fields, methods, etc.
    058    private final List<Field> field_vec = new ArrayList<>();
    059    private final List<Method> method_vec = new ArrayList<>();
    060    private final List<Attribute> attribute_vec = new ArrayList<>();
    061    private final List<String> interface_vec = new ArrayList<>();
    062    private final List<AnnotationEntryGen> annotation_vec = new ArrayList<>();
    063
    064    private static BCELComparator _cmp = new BCELComparator() {
    065
    066        @Override
    067        public boolean equals( final Object o1, final Object o2 ) {
    068            final ClassGen THIS = (ClassGen) o1;
    069            final ClassGen THAT = (ClassGen) o2;
    070            return Objects.equals(THIS.getClassName(), THAT.getClassName());
    071        }
    072
    073
    074        @Override
    075        public int hashCode( final Object o ) {
    076            final ClassGen THIS = (ClassGen) o;
    077            return THIS.getClassName().hashCode();
    078        }
    079    };
    080
    081
    082    /** Convenience constructor to set up some important values initially.
    083     *
    084     * @param class_name fully qualified class name
    085     * @param super_class_name fully qualified superclass name
    086     * @param file_name source file name
    087     * @param access_flags access qualifiers
    088     * @param interfaces implemented interfaces
    089     * @param cp constant pool to use
    090     */
    091    public ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags,
    092            final String[] interfaces, final ConstantPoolGen cp) {
    093        super(access_flags);
    094        this.class_name = class_name;
    095        this.super_class_name = super_class_name;
    096        this.file_name = file_name;
    097        this.cp = cp;
    098        // Put everything needed by default into the constant pool and the vectors
    099        if (file_name != null) {
    100            addAttribute(new SourceFile(cp.addUtf8("SourceFile"), 2, cp.addUtf8(file_name), cp
    101                    .getConstantPool()));
    102        }
    103        class_name_index = cp.addClass(class_name);
    104        superclass_name_index = cp.addClass(super_class_name);
    105        if (interfaces != null) {
    106            for (final String interface1 : interfaces) {
    107                addInterface(interface1);
    108            }
    109        }
    110    }
    111
    112
    113    /** Convenience constructor to set up some important values initially.
    114     *
    115     * @param class_name fully qualified class name
    116     * @param super_class_name fully qualified superclass name
    117     * @param file_name source file name
    118     * @param access_flags access qualifiers
    119     * @param interfaces implemented interfaces
    120     */
    121    public ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags,
    122            final String[] interfaces) {
    123        this(class_name, super_class_name, file_name, access_flags, interfaces,
    124                new ConstantPoolGen());
    125    }
    126
    127
    128    /**
    129     * Initialize with existing class.
    130     * @param clazz JavaClass object (e.g. read from file)
    131     */
    132    public ClassGen(final JavaClass clazz) {
    133        super(clazz.getAccessFlags());
    134        class_name_index = clazz.getClassNameIndex();
    135        superclass_name_index = clazz.getSuperclassNameIndex();
    136        class_name = clazz.getClassName();
    137        super_class_name = clazz.getSuperclassName();
    138        file_name = clazz.getSourceFileName();
    139        cp = new ConstantPoolGen(clazz.getConstantPool());
    140        major = clazz.getMajor();
    141        minor = clazz.getMinor();
    142        final Attribute[] attributes = clazz.getAttributes();
    143        // J5TODO: Could make unpacking lazy, done on first reference
    144        final AnnotationEntryGen[] annotations = unpackAnnotations(attributes);
    145        final Method[] methods = clazz.getMethods();
    146        final Field[] fields = clazz.getFields();
    147        final String[] interfaces = clazz.getInterfaceNames();
    148        for (final String interface1 : interfaces) {
    149            addInterface(interface1);
    150        }
    151        for (final Attribute attribute : attributes) {
    152            if (!(attribute instanceof Annotations)) {
    153                addAttribute(attribute);
    154            }
    155        }
    156        for (final AnnotationEntryGen annotation : annotations) {
    157            addAnnotationEntry(annotation);
    158        }
    159        for (final Method method : methods) {
    160            addMethod(method);
    161        }
    162        for (final Field field : fields) {
    163            addField(field);
    164        }
    165    }
    166
    167    /**
    168     * Look for attributes representing annotations and unpack them.
    169     */
    170    private AnnotationEntryGen[] unpackAnnotations(final Attribute[] attrs)
    171    {
    172        final List<AnnotationEntryGen> annotationGenObjs = new ArrayList<>();
    173        for (final Attribute attr : attrs) {
    174            if (attr instanceof RuntimeVisibleAnnotations)
    175            {
    176                final RuntimeVisibleAnnotations rva = (RuntimeVisibleAnnotations) attr;
    177                final AnnotationEntry[] annos = rva.getAnnotationEntries();
    178                for (final AnnotationEntry a : annos) {
    179                    annotationGenObjs.add(new AnnotationEntryGen(a,
    180                            getConstantPool(), false));
    181                }
    182            }
    183            else
    184                if (attr instanceof RuntimeInvisibleAnnotations)
    185                {
    186                    final RuntimeInvisibleAnnotations ria = (RuntimeInvisibleAnnotations) attr;
    187                    final AnnotationEntry[] annos = ria.getAnnotationEntries();
    188                    for (final AnnotationEntry a : annos) {
    189                        annotationGenObjs.add(new AnnotationEntryGen(a,
    190                                getConstantPool(), false));
    191                    }
    192                }
    193        }
    194        return annotationGenObjs.toArray(new AnnotationEntryGen[annotationGenObjs.size()]);
    195    }
    196
    197
    198    /**
    199     * @return the (finally) built up Java class object.
    200     */
    201    public JavaClass getJavaClass() {
    202        final int[] interfaces = getInterfaces();
    203        final Field[] fields = getFields();
    204        final Method[] methods = getMethods();
    205        Attribute[] attributes = null;
    206        if (annotation_vec.isEmpty()) {
    207            attributes = getAttributes();
    208        } else {
    209            // TODO: Sometime later, trash any attributes called 'RuntimeVisibleAnnotations' or 'RuntimeInvisibleAnnotations'
    210            final Attribute[] annAttributes  = AnnotationEntryGen.getAnnotationAttributes(cp, getAnnotationEntries());
    211            attributes = new Attribute[attribute_vec.size()+annAttributes.length];
    212            attribute_vec.toArray(attributes);
    213            System.arraycopy(annAttributes,0,attributes,attribute_vec.size(),annAttributes.length);
    214        }
    215        // Must be last since the above calls may still add something to it
    216        final ConstantPool _cp = this.cp.getFinalConstantPool();
    217        return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor,
    218                super.getAccessFlags(), _cp, interfaces, fields, methods, attributes);
    219    }
    220
    221
    222    /**
    223     * Add an interface to this class, i.e., this class has to implement it.
    224     * @param name interface to implement (fully qualified class name)
    225     */
    226    public void addInterface( final String name ) {
    227        interface_vec.add(name);
    228    }
    229
    230
    231    /**
    232     * Remove an interface from this class.
    233     * @param name interface to remove (fully qualified name)
    234     */
    235    public void removeInterface( final String name ) {
    236        interface_vec.remove(name);
    237    }
    238
    239
    240    /**
    241     * @return major version number of class file
    242     */
    243    public int getMajor() {
    244        return major;
    245    }
    246
    247
    248    /** Set major version number of class file, default value is 45 (JDK 1.1)
    249     * @param major major version number
    250     */
    251    public void setMajor( final int major ) { // TODO could be package-protected - only called by test code
    252        this.major = major;
    253    }
    254
    255
    256    /** Set minor version number of class file, default value is 3 (JDK 1.1)
    257     * @param minor minor version number
    258     */
    259    public void setMinor( final int minor ) {  // TODO could be package-protected - only called by test code
    260        this.minor = minor;
    261    }
    262
    263    /**
    264     * @return minor version number of class file
    265     */
    266    public int getMinor() {
    267        return minor;
    268    }
    269
    270
    271    /**
    272     * Add an attribute to this class.
    273     * @param a attribute to add
    274     */
    275    public void addAttribute( final Attribute a ) {
    276        attribute_vec.add(a);
    277    }
    278
    279    public void addAnnotationEntry(final AnnotationEntryGen a) {
    280        annotation_vec.add(a);
    281    }
    282
    283
    284    /**
    285     * Add a method to this class.
    286     * @param m method to add
    287     */
    288    public void addMethod( final Method m ) {
    289        method_vec.add(m);
    290    }
    291
    292
    293    /**
    294     * Convenience method.
    295     *
    296     * Add an empty constructor to this class that does nothing but calling super().
    297     * @param access_flags rights for constructor
    298     */
    299    public void addEmptyConstructor( final int access_flags ) {
    300        final InstructionList il = new InstructionList();
    301        il.append(InstructionConst.THIS); // Push `this'
    302        il.append(new INVOKESPECIAL(cp.addMethodref(super_class_name, "<init>", "()V")));
    303        il.append(InstructionConst.RETURN);
    304        final MethodGen mg = new MethodGen(access_flags, Type.VOID, Type.NO_ARGS, null, "<init>",
    305                class_name, il, cp);
    306        mg.setMaxStack(1);
    307        addMethod(mg.getMethod());
    308    }
    309
    310
    311    /**
    312     * Add a field to this class.
    313     * @param f field to add
    314     */
    315    public void addField( final Field f ) {
    316        field_vec.add(f);
    317    }
    318
    319
    320    public boolean containsField( final Field f ) {
    321        return field_vec.contains(f);
    322    }
    323
    324
    325    /** @return field object with given name, or null
    326     */
    327    public Field containsField( final String name ) {
    328        for (final Field f : field_vec) {
    329            if (f.getName().equals(name)) {
    330                return f;
    331            }
    332        }
    333        return null;
    334    }
    335
    336
    337    /** @return method object with given name and signature, or null
    338     */
    339    public Method containsMethod( final String name, final String signature ) {
    340        for (final Method m : method_vec) {
    341            if (m.getName().equals(name) && m.getSignature().equals(signature)) {
    342                return m;
    343            }
    344        }
    345        return null;
    346    }
    347
    348
    349    /**
    350     * Remove an attribute from this class.
    351     * @param a attribute to remove
    352     */
    353    public void removeAttribute( final Attribute a ) {
    354        attribute_vec.remove(a);
    355    }
    356
    357
    358    /**
    359     * Remove a method from this class.
    360     * @param m method to remove
    361     */
    362    public void removeMethod( final Method m ) {
    363        method_vec.remove(m);
    364    }
    365
    366
    367    /** Replace given method with new one. If the old one does not exist
    368     * add the new_ method to the class anyway.
    369     */
    370    public void replaceMethod( final Method old, final Method new_ ) {
    371        if (new_ == null) {
    372            throw new ClassGenException("Replacement method must not be null");
    373        }
    374        final int i = method_vec.indexOf(old);
    375        if (i < 0) {
    376            method_vec.add(new_);
    377        } else {
    378            method_vec.set(i, new_);
    379        }
    380    }
    381
    382
    383    /** Replace given field with new one. If the old one does not exist
    384     * add the new_ field to the class anyway.
    385     */
    386    public void replaceField( final Field old, final Field new_ ) {
    387        if (new_ == null) {
    388            throw new ClassGenException("Replacement method must not be null");
    389        }
    390        final int i = field_vec.indexOf(old);
    391        if (i < 0) {
    392            field_vec.add(new_);
    393        } else {
    394            field_vec.set(i, new_);
    395        }
    396    }
    397
    398
    399    /**
    400     * Remove a field to this class.
    401     * @param f field to remove
    402     */
    403    public void removeField( final Field f ) {
    404        field_vec.remove(f);
    405    }
    406
    407
    408    public String getClassName() {
    409        return class_name;
    410    }
    411
    412
    413    public String getSuperclassName() {
    414        return super_class_name;
    415    }
    416
    417
    418    public String getFileName() {
    419        return file_name;
    420    }
    421
    422
    423    public void setClassName( final String name ) {
    424        class_name = name.replace('/', '.');
    425        class_name_index = cp.addClass(name);
    426    }
    427
    428
    429    public void setSuperclassName( final String name ) {
    430        super_class_name = name.replace('/', '.');
    431        superclass_name_index = cp.addClass(name);
    432    }
    433
    434
    435    public Method[] getMethods() {
    436        return method_vec.toArray(new Method[method_vec.size()]);
    437    }
    438
    439
    440    public void setMethods( final Method[] methods ) {
    441        method_vec.clear();
    442        for (final Method method : methods) {
    443            addMethod(method);
    444        }
    445    }
    446
    447
    448    public void setMethodAt( final Method method, final int pos ) {
    449        method_vec.set(pos, method);
    450    }
    451
    452
    453    public Method getMethodAt( final int pos ) {
    454        return method_vec.get(pos);
    455    }
    456
    457
    458    public String[] getInterfaceNames() {
    459        final int size = interface_vec.size();
    460        final String[] interfaces = new String[size];
    461        interface_vec.toArray(interfaces);
    462        return interfaces;
    463    }
    464
    465
    466    public int[] getInterfaces() {
    467        final int size = interface_vec.size();
    468        final int[] interfaces = new int[size];
    469        for (int i = 0; i < size; i++) {
    470            interfaces[i] = cp.addClass(interface_vec.get(i));
    471        }
    472        return interfaces;
    473    }
    474
    475
    476    public Field[] getFields() {
    477        return field_vec.toArray(new Field[field_vec.size()]);
    478    }
    479
    480
    481    public Attribute[] getAttributes() {
    482        return attribute_vec.toArray(new Attribute[attribute_vec.size()]);
    483    }
    484
    485    //  J5TODO: Should we make calling unpackAnnotations() lazy and put it in here?
    486    public AnnotationEntryGen[] getAnnotationEntries() {
    487        return annotation_vec.toArray(new AnnotationEntryGen[annotation_vec.size()]);
    488    }
    489
    490
    491    public ConstantPoolGen getConstantPool() {
    492        return cp;
    493    }
    494
    495
    496    public void setConstantPool( final ConstantPoolGen constant_pool ) {
    497        cp = constant_pool;
    498    }
    499
    500
    501    public void setClassNameIndex( final int class_name_index ) {
    502        this.class_name_index = class_name_index;
    503        class_name = cp.getConstantPool().getConstantString(class_name_index,
    504                Const.CONSTANT_Class).replace('/', '.');
    505    }
    506
    507
    508    public void setSuperclassNameIndex( final int superclass_name_index ) {
    509        this.superclass_name_index = superclass_name_index;
    510        super_class_name = cp.getConstantPool().getConstantString(superclass_name_index,
    511                Const.CONSTANT_Class).replace('/', '.');
    512    }
    513
    514
    515    public int getSuperclassNameIndex() {
    516        return superclass_name_index;
    517    }
    518
    519
    520    public int getClassNameIndex() {
    521        return class_name_index;
    522    }
    523
    524    private List<ClassObserver> observers;
    525
    526
    527    /** Add observer for this object.
    528     */
    529    public void addObserver( final ClassObserver o ) {
    530        if (observers == null) {
    531            observers = new ArrayList<>();
    532        }
    533        observers.add(o);
    534    }
    535
    536
    537    /** Remove observer for this object.
    538     */
    539    public void removeObserver( final ClassObserver o ) {
    540        if (observers != null) {
    541            observers.remove(o);
    542        }
    543    }
    544
    545
    546    /** Call notify() method on all observers. This method is not called
    547     * automatically whenever the state has changed, but has to be
    548     * called by the user after he has finished editing the object.
    549     */
    550    public void update() {
    551        if (observers != null) {
    552            for (final ClassObserver observer : observers) {
    553                observer.notify(this);
    554            }
    555        }
    556    }
    557
    558
    559    @Override
    560    public Object clone() {
    561        try {
    562            return super.clone();
    563        } catch (final CloneNotSupportedException e) {
    564            throw new Error("Clone Not Supported"); // never happens
    565        }
    566    }
    567
    568
    569    /**
    570     * @return Comparison strategy object
    571     */
    572    public static BCELComparator getComparator() {
    573        return _cmp;
    574    }
    575
    576
    577    /**
    578     * @param comparator Comparison strategy object
    579     */
    580    public static void setComparator( final BCELComparator comparator ) {
    581        _cmp = comparator;
    582    }
    583
    584
    585    /**
    586     * Return value as defined by given BCELComparator strategy.
    587     * By default two ClassGen objects are said to be equal when
    588     * their class names are equal.
    589     *
    590     * @see java.lang.Object#equals(java.lang.Object)
    591     */
    592    @Override
    593    public boolean equals( final Object obj ) {
    594        return _cmp.equals(this, obj);
    595    }
    596
    597
    598    /**
    599     * Return value as defined by given BCELComparator strategy.
    600     * By default return the hashcode of the class name.
    601     *
    602     * @see java.lang.Object#hashCode()
    603     */
    604    @Override
    605    public int hashCode() {
    606        return _cmp.hashCode(this);
    607    }
    608}