    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 */
    017package org.apache.bcel.classfile;
    018
    019import java.io.ByteArrayOutputStream;
    020import java.io.DataOutputStream;
    021import java.io.File;
    022import java.io.FileOutputStream;
    023import java.io.IOException;
    024import java.io.OutputStream;
    025import java.util.ArrayList;
    026import java.util.Arrays;
    027import java.util.List;
    028import java.util.Objects;
    029import java.util.Set;
    030import java.util.StringTokenizer;
    031import java.util.TreeSet;
    032
    033import org.apache.bcel.Const;
    034import org.apache.bcel.generic.Type;
    035import org.apache.bcel.util.BCELComparator;
    036import org.apache.bcel.util.ClassQueue;
    037import org.apache.bcel.util.SyntheticRepository;
    038import org.apache.commons.lang3.ArrayUtils;
    039
    040/**
    041 * Represents a Java class, i.e., the data structures, constant pool, fields, methods and commands contained in a Java
    042 * .class file. See <a href="https://docs.oracle.com/javase/specs/">JVM specification</a> for details. The intent of
    043 * this class is to represent a parsed or otherwise existing class file. Those interested in programmatically generating
    044 * classes should see the <a href="../generic/ClassGen.html">ClassGen</a> class.
    045 *
    046 * @see org.apache.bcel.generic.ClassGen
    047 */
    048public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> {
    049
    050    /**
    051     * The standard class file extension.
    052     *
    053     * @since 6.7.0
    054     */
    055    public static final String EXTENSION = ".class";
    056
    057    /**
    058     * Empty array.
    059     *
    060     * @since 6.6.0
    061     */
    062    public static final JavaClass[] EMPTY_ARRAY = {};
    063
    064    public static final byte HEAP = 1;
    065    public static final byte FILE = 2;
    066    public static final byte ZIP = 3;
    067    private static final boolean debug = Boolean.getBoolean("JavaClass.debug"); // Debugging on/off
    068    private static BCELComparator bcelComparator = new BCELComparator() {
    069
    070        @Override
    071        public boolean equals(final Object o1, final Object o2) {
    072            final JavaClass THIS = (JavaClass) o1;
    073            final JavaClass THAT = (JavaClass) o2;
    074            return Objects.equals(THIS.getClassName(), THAT.getClassName());
    075        }
    076
    077        @Override
    078        public int hashCode(final Object o) {
    079            final JavaClass THIS = (JavaClass) o;
    080            return THIS.getClassName().hashCode();
    081        }
    082    };
    083
    084    /*
    085     * Print debug information depending on 'JavaClass.debug'
    086     */
    087    static void Debug(final String str) {
    088        if (debug) {
    089            System.out.println(str);
    090        }
    091    }
    092
    093    /**
    094     * @return Comparison strategy object
    095     */
    096    public static BCELComparator getComparator() {
    097        return bcelComparator;
    098    }
    099
    100    private static String indent(final Object obj) {
    101        final StringTokenizer tokenizer = new StringTokenizer(obj.toString(), "\n");
    102        final StringBuilder buf = new StringBuilder();
    103        while (tokenizer.hasMoreTokens()) {
    104            buf.append("\t").append(tokenizer.nextToken()).append("\n");
    105        }
    106        return buf.toString();
    107    }
    108
    109    /**
    110     * @param comparator Comparison strategy object
    111     */
    112    public static void setComparator(final BCELComparator comparator) {
    113        bcelComparator = comparator;
    114    }
    115
    116    private String fileName;
    117    private final String packageName;
    118    private String sourceFileName = "<Unknown>";
    119    private int classNameIndex;
    120    private int superclassNameIndex;
    121    private String className;
    122    private String superclassName;
    123    private int major;
    124    private int minor; // Compiler version
    125    private ConstantPool constantPool; // Constant pool
    126    private int[] interfaces; // implemented interfaces
    127    private String[] interfaceNames;
    128    private Field[] fields; // Fields, i.e., variables of class
    129    private Method[] methods; // methods defined in the class
    130    private Attribute[] attributes; // attributes defined in the class
    131
    132    private AnnotationEntry[] annotations; // annotations defined on the class
    133    private byte source = HEAP; // Generated in memory
    134
    135    private boolean isAnonymous;
    136
    137    private boolean isNested;
    138
    139    private boolean computedNestedTypeStatus;
    140
    141    /**
    142     * In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any
    143     * better.
    144     */
    145    private transient org.apache.bcel.util.Repository repository = SyntheticRepository.getInstance();
    146
    147    /**
    148     * Constructor gets all contents as arguments.
    149     *
    150     * @param classNameIndex Class name
    151     * @param superclassNameIndex Superclass name
    152     * @param fileName File name
    153     * @param major Major compiler version
    154     * @param minor Minor compiler version
    155     * @param accessFlags Access rights defined by bit flags
    156     * @param constantPool Array of constants
    157     * @param interfaces Implemented interfaces
    158     * @param fields Class fields
    159     * @param methods Class methods
    160     * @param attributes Class attributes
    161     */
    162    public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
    163        final ConstantPool constantPool, final int[] interfaces, final Field[] fields, final Method[] methods, final Attribute[] attributes) {
    164        this(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes, HEAP);
    165    }
    166
    167    /**
    168     * Constructor gets all contents as arguments.
    169     *
    170     * @param classNameIndex Index into constant pool referencing a ConstantClass that represents this class.
    171     * @param superclassNameIndex Index into constant pool referencing a ConstantClass that represents this class's
    172     *        superclass.
    173     * @param fileName File name
    174     * @param major Major compiler version
    175     * @param minor Minor compiler version
    176     * @param accessFlags Access rights defined by bit flags
    177     * @param constantPool Array of constants
    178     * @param interfaces Implemented interfaces
    179     * @param fields Class fields
    180     * @param methods Class methods
    181     * @param attributes Class attributes
    182     * @param source Read from file or generated in memory?
    183     */
    184    public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
    185        final ConstantPool constantPool, int[] interfaces, Field[] fields, Method[] methods, Attribute[] attributes, final byte source) {
    186        super(accessFlags);
    187        if (interfaces == null) {
    188            interfaces = ArrayUtils.EMPTY_INT_ARRAY;
    189        }
    190        if (attributes == null) {
    191            attributes = Attribute.EMPTY_ARRAY;
    192        }
    193        if (fields == null) {
    194            fields = Field.EMPTY_FIELD_ARRAY;
    195        }
    196        if (methods == null) {
    197            methods = Method.EMPTY_METHOD_ARRAY;
    198        }
    199        this.classNameIndex = classNameIndex;
    200        this.superclassNameIndex = superclassNameIndex;
    201        this.fileName = fileName;
    202        this.major = major;
    203        this.minor = minor;
    204        this.constantPool = constantPool;
    205        this.interfaces = interfaces;
    206        this.fields = fields;
    207        this.methods = methods;
    208        this.attributes = attributes;
    209        this.source = source;
    210        // Get source file name if available
    211        for (final Attribute attribute : attributes) {
    212            if (attribute instanceof SourceFile) {
    213                sourceFileName = ((SourceFile) attribute).getSourceFileName();
    214                break;
    215            }
    216        }
    217        /*
    218         * According to the specification the following entries must be of type 'ConstantClass' but we check that anyway via the
    219         * 'ConstPool.getConstant' method.
    220         */
    221        className = constantPool.getConstantString(classNameIndex, Const.CONSTANT_Class);
    222        className = Utility.compactClassName(className, false);
    223        final int index = className.lastIndexOf('.');
    224        if (index < 0) {
    225            packageName = "";
    226        } else {
    227            packageName = className.substring(0, index);
    228        }
    229        if (superclassNameIndex > 0) {
    230            // May be zero -> class is java.lang.Object
    231            superclassName = constantPool.getConstantString(superclassNameIndex, Const.CONSTANT_Class);
    232            superclassName = Utility.compactClassName(superclassName, false);
    233        } else {
    234            superclassName = "java.lang.Object";
    235        }
    236        interfaceNames = new String[interfaces.length];
    237        for (int i = 0; i < interfaces.length; i++) {
    238            final String str = constantPool.getConstantString(interfaces[i], Const.CONSTANT_Class);
    239            interfaceNames[i] = Utility.compactClassName(str, false);
    240        }
    241    }
    242
    243    /**
    244     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
    245     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
    246     *
    247     * @param v Visitor object
    248     */
    249    @Override
    250    public void accept(final Visitor v) {
    251        v.visitJavaClass(this);
    252    }
    253
    254    /**
    255     * Return the natural ordering of two JavaClasses. This ordering is based on the class name
    256     *
    257     * @since 6.0
    258     */
    259    @Override
    260    public int compareTo(final JavaClass obj) {
    261        return getClassName().compareTo(obj.getClassName());
    262    }
    263
    264    private void computeNestedTypeStatus() {
    265        if (computedNestedTypeStatus) {
    266            return;
    267        }
    268        for (final Attribute attribute : this.attributes) {
    269            if (attribute instanceof InnerClasses) {
    270                ((InnerClasses) attribute).forEach(innerClass ->  {
    271                    boolean innerClassAttributeRefersToMe = false;
    272                    String innerClassName = constantPool.getConstantString(innerClass.getInnerClassIndex(), Const.CONSTANT_Class);
    273                    innerClassName = Utility.compactClassName(innerClassName, false);
    274                    if (innerClassName.equals(getClassName())) {
    275                        innerClassAttributeRefersToMe = true;
    276                    }
    277                    if (innerClassAttributeRefersToMe) {
    278                        this.isNested = true;
    279                        if (innerClass.getInnerNameIndex() == 0) {
    280                            this.isAnonymous = true;
    281                        }
    282                    }
    283                });
    284            }
    285        }
    286        this.computedNestedTypeStatus = true;
    287    }
    288
    289    /**
    290     * @return deep copy of this class
    291     */
    292    public JavaClass copy() {
    293        try {
    294            final JavaClass c = (JavaClass) clone();
    295            c.constantPool = constantPool.copy();
    296            c.interfaces = interfaces.clone();
    297            c.interfaceNames = interfaceNames.clone();
    298            c.fields = new Field[fields.length];
    299            Arrays.setAll(c.fields, i -> fields[i].copy(c.constantPool));
    300            c.methods = new Method[methods.length];
    301            Arrays.setAll(c.methods, i -> methods[i].copy(c.constantPool));
    302            c.attributes = new Attribute[attributes.length];
    303            Arrays.setAll(c.attributes, i -> attributes[i].copy(c.constantPool));
    304            return c;
    305        } catch (final CloneNotSupportedException e) {
    306            return null;
    307        }
    308    }
    309
    310    /**
    311     * Dump Java class to output stream in binary format.
    312     *
    313     * @param file Output stream
    314     * @throws IOException if an I/O error occurs.
    315     */
    316    public void dump(final DataOutputStream file) throws IOException {
    317        file.writeInt(Const.JVM_CLASSFILE_MAGIC);
    318        file.writeShort(minor);
    319        file.writeShort(major);
    320        constantPool.dump(file);
    321        file.writeShort(super.getAccessFlags());
    322        file.writeShort(classNameIndex);
    323        file.writeShort(superclassNameIndex);
    324        file.writeShort(interfaces.length);
    325        for (final int interface1 : interfaces) {
    326            file.writeShort(interface1);
    327        }
    328        file.writeShort(fields.length);
    329        for (final Field field : fields) {
    330            field.dump(file);
    331        }
    332        file.writeShort(methods.length);
    333        for (final Method method : methods) {
    334            method.dump(file);
    335        }
    336        if (attributes != null) {
    337            file.writeShort(attributes.length);
    338            for (final Attribute attribute : attributes) {
    339                attribute.dump(file);
    340            }
    341        } else {
    342            file.writeShort(0);
    343        }
    344        file.flush();
    345    }
    346
    347    /**
    348     * Dump class to a file.
    349     *
    350     * @param file Output file
    351     * @throws IOException if an I/O error occurs.
    352     */
    353    public void dump(final File file) throws IOException {
    354        final String parent = file.getParent();
    355        if (parent != null) {
    356            final File dir = new File(parent);
    357            if (!dir.mkdirs() && !dir.isDirectory()) {
    358                throw new IOException("Could not create the directory " + dir);
    359            }
    360        }
    361        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
    362            dump(dos);
    363        }
    364    }
    365
    366    /**
    367     * Dump Java class to output stream in binary format.
    368     *
    369     * @param file Output stream
    370     * @throws IOException if an I/O error occurs.
    371     */
    372    public void dump(final OutputStream file) throws IOException {
    373        dump(new DataOutputStream(file));
    374    }
    375
    376    /**
    377     * Dump class to a file named fileName.
    378     *
    379     * @param fileName Output file name
    380     * @throws IOException if an I/O error occurs.
    381     */
    382    public void dump(final String fileName) throws IOException {
    383        dump(new File(fileName));
    384    }
    385
    386    /**
    387     * Return value as defined by given BCELComparator strategy. By default two JavaClass objects are said to be equal when
    388     * their class names are equal.
    389     *
    390     * @see Object#equals(Object)
    391     */
    392    @Override
    393    public boolean equals(final Object obj) {
    394        return bcelComparator.equals(this, obj);
    395    }
    396
    397    /**
    398     * Get all interfaces implemented by this JavaClass (transitively).
    399     *
    400     * @throws ClassNotFoundException if any of the class's superclasses or interfaces can't be found.
    401     */
    402    public JavaClass[] getAllInterfaces() throws ClassNotFoundException {
    403        final ClassQueue queue = new ClassQueue();
    404        final Set<JavaClass> allInterfaces = new TreeSet<>();
    405        queue.enqueue(this);
    406        while (!queue.empty()) {
    407            final JavaClass clazz = queue.dequeue();
    408            final JavaClass souper = clazz.getSuperClass();
    409            final JavaClass[] interfaces = clazz.getInterfaces();
    410            if (clazz.isInterface()) {
    411                allInterfaces.add(clazz);
    412            } else if (souper != null) {
    413                queue.enqueue(souper);
    414            }
    415            for (final JavaClass iface : interfaces) {
    416                queue.enqueue(iface);
    417            }
    418        }
    419        return allInterfaces.toArray(JavaClass.EMPTY_ARRAY);
    420    }
    421
    422    /**
    423     * @return Annotations on the class
    424     * @since 6.0
    425     */
    426    public AnnotationEntry[] getAnnotationEntries() {
    427        if (annotations == null) {
    428            annotations = AnnotationEntry.createAnnotationEntries(getAttributes());
    429        }
    430
    431        return annotations;
    432    }
    433
    434    /**
    435     * @return Attributes of the class.
    436     */
    437    public Attribute[] getAttributes() {
    438        return attributes;
    439    }
    440
    441    /**
    442     * @return class in binary format
    443     */
    444    public byte[] getBytes() {
    445        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
    446        try (DataOutputStream dos = new DataOutputStream(baos)) {
    447            dump(dos);
    448        } catch (final IOException e) {
    449            e.printStackTrace();
    450        }
    451        return baos.toByteArray();
    452    }
    453
    454    /**
    455     * @return Class name.
    456     */
    457    public String getClassName() {
    458        return className;
    459    }
    460
    461    /**
    462     * @return Class name index.
    463     */
    464    public int getClassNameIndex() {
    465        return classNameIndex;
    466    }
    467
    468    /**
    469     * @return Constant pool.
    470     */
    471    public ConstantPool getConstantPool() {
    472        return constantPool;
    473    }
    474
    475    /**
    476     * @return Fields, i.e., variables of the class. Like the JVM spec mandates for the classfile format, these fields are
    477     *         those specific to this class, and not those of the superclass or superinterfaces.
    478     */
    479    public Field[] getFields() {
    480        return fields;
    481    }
    482
    483    /**
    484     * @return File name of class, aka SourceFile attribute value
    485     */
    486    public String getFileName() {
    487        return fileName;
    488    }
    489
    490    /**
    491     * @return Indices in constant pool of implemented interfaces.
    492     */
    493    public int[] getInterfaceIndices() {
    494        return interfaces;
    495    }
    496
    497    /**
    498     * @return Names of implemented interfaces.
    499     */
    500    public String[] getInterfaceNames() {
    501        return interfaceNames;
    502    }
    503
    504    /**
    505     * Get interfaces directly implemented by this JavaClass.
    506     *
    507     * @throws ClassNotFoundException if any of the class's interfaces can't be found.
    508     */
    509    public JavaClass[] getInterfaces() throws ClassNotFoundException {
    510        final String[] interfaces = getInterfaceNames();
    511        final JavaClass[] classes = new JavaClass[interfaces.length];
    512        for (int i = 0; i < interfaces.length; i++) {
    513            classes[i] = repository.loadClass(interfaces[i]);
    514        }
    515        return classes;
    516    }
    517
    518    /**
    519     * @return Major number of class file version.
    520     */
    521    public int getMajor() {
    522        return major;
    523    }
    524
    525    /**
    526     * @return A {@link Method} corresponding to java.lang.reflect.Method if any
    527     */
    528    public Method getMethod(final java.lang.reflect.Method m) {
    529        for (final Method method : methods) {
    530            if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) {
    531                return method;
    532            }
    533        }
    534        return null;
    535    }
    536
    537    /**
    538     * @return Methods of the class.
    539     */
    540    public Method[] getMethods() {
    541        return methods;
    542    }
    543
    544    /**
    545     * @return Minor number of class file version.
    546     */
    547    public int getMinor() {
    548        return minor;
    549    }
    550
    551    /**
    552     * @return Package name.
    553     */
    554    public String getPackageName() {
    555        return packageName;
    556    }
    557
    558    /**
    559     * Gets the ClassRepository which holds its definition. By default this is the same as
    560     * SyntheticRepository.getInstance();
    561     */
    562    public org.apache.bcel.util.Repository getRepository() {
    563        return repository;
    564    }
    565
    566    /**
    567     * @return returns either HEAP (generated), FILE, or ZIP
    568     */
    569    public final byte getSource() {
    570        return source;
    571    }
    572
    573    /**
    574     * @return file name where this class was read from
    575     */
    576    public String getSourceFileName() {
    577        return sourceFileName;
    578    }
    579
    580    /**
    581     * Gets the source file path including the package path.
    582     *
    583     * @return path to original source file of parsed class, relative to original source directory.
    584     * @since 6.7.0
    585     */
    586    public String getSourceFilePath() {
    587        final StringBuilder outFileName = new StringBuilder();
    588        if (!packageName.isEmpty()) {
    589            outFileName.append(Utility.packageToPath(packageName));
    590            outFileName.append('/');
    591        }
    592        outFileName.append(sourceFileName);
    593        return outFileName.toString();
    594    }
    595
    596    /**
    597     * @return the superclass for this JavaClass object, or null if this is java.lang.Object
    598     * @throws ClassNotFoundException if the superclass can't be found
    599     */
    600    public JavaClass getSuperClass() throws ClassNotFoundException {
    601        if ("java.lang.Object".equals(getClassName())) {
    602            return null;
    603        }
    604        return repository.loadClass(getSuperclassName());
    605    }
    606
    607    /**
    608     * @return list of super classes of this class in ascending order, i.e., java.lang.Object is always the last element
    609     * @throws ClassNotFoundException if any of the superclasses can't be found
    610     */
    611    public JavaClass[] getSuperClasses() throws ClassNotFoundException {
    612        JavaClass clazz = this;
    613        final List<JavaClass> allSuperClasses = new ArrayList<>();
    614        for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) {
    615            allSuperClasses.add(clazz);
    616        }
    617        return allSuperClasses.toArray(JavaClass.EMPTY_ARRAY);
    618    }
    619
    620    /**
    621     * returns the super class name of this class. In the case that this class is java.lang.Object, it will return itself
    622     * (java.lang.Object). This is probably incorrect but isn't fixed at this time to not break existing clients.
    623     *
    624     * @return Superclass name.
    625     */
    626    public String getSuperclassName() {
    627        return superclassName;
    628    }
    629
    630    /**
    631     * @return Class name index.
    632     */
    633    public int getSuperclassNameIndex() {
    634        return superclassNameIndex;
    635    }
    636
    637    /**
    638     * Return value as defined by given BCELComparator strategy. By default return the hashcode of the class name.
    639     *
    640     * @see Object#hashCode()
    641     */
    642    @Override
    643    public int hashCode() {
    644        return bcelComparator.hashCode(this);
    645    }
    646
    647    /**
    648     * @return true, if this class is an implementation of interface inter
    649     * @throws ClassNotFoundException if superclasses or superinterfaces of this class can't be found
    650     */
    651    public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException {
    652        if (!inter.isInterface()) {
    653            throw new IllegalArgumentException(inter.getClassName() + " is no interface");
    654        }
    655        if (this.equals(inter)) {
    656            return true;
    657        }
    658        final JavaClass[] superInterfaces = getAllInterfaces();
    659        for (final JavaClass superInterface : superInterfaces) {
    660            if (superInterface.equals(inter)) {
    661                return true;
    662            }
    663        }
    664        return false;
    665    }
    666
    667    /**
    668     * Equivalent to runtime "instanceof" operator.
    669     *
    670     * @return true if this JavaClass is derived from the super class
    671     * @throws ClassNotFoundException if superclasses or superinterfaces of this object can't be found
    672     */
    673    public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException {
    674        if (this.equals(superclass)) {
    675            return true;
    676        }
    677        for (final JavaClass clazz : getSuperClasses()) {
    678            if (clazz.equals(superclass)) {
    679                return true;
    680            }
    681        }
    682        if (superclass.isInterface()) {
    683            return implementationOf(superclass);
    684        }
    685        return false;
    686    }
    687
    688    /**
    689     * @since 6.0
    690     */
    691    public final boolean isAnonymous() {
    692        computeNestedTypeStatus();
    693        return this.isAnonymous;
    694    }
    695
    696    public final boolean isClass() {
    697        return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0;
    698    }
    699
    700    /**
    701     * @since 6.0
    702     */
    703    public final boolean isNested() {
    704        computeNestedTypeStatus();
    705        return this.isNested;
    706    }
    707
    708    public final boolean isSuper() {
    709        return (super.getAccessFlags() & Const.ACC_SUPER) != 0;
    710    }
    711
    712    /**
    713     * @param attributes .
    714     */
    715    public void setAttributes(final Attribute[] attributes) {
    716        this.attributes = attributes;
    717    }
    718
    719    /**
    720     * @param className .
    721     */
    722    public void setClassName(final String className) {
    723        this.className = className;
    724    }
    725
    726    /**
    727     * @param classNameIndex .
    728     */
    729    public void setClassNameIndex(final int classNameIndex) {
    730        this.classNameIndex = classNameIndex;
    731    }
    732
    733    /**
    734     * @param constantPool .
    735     */
    736    public void setConstantPool(final ConstantPool constantPool) {
    737        this.constantPool = constantPool;
    738    }
    739
    740    /**
    741     * @param fields .
    742     */
    743    public void setFields(final Field[] fields) {
    744        this.fields = fields;
    745    }
    746
    747    /**
    748     * Set File name of class, aka SourceFile attribute value
    749     */
    750    public void setFileName(final String fileName) {
    751        this.fileName = fileName;
    752    }
    753
    754    /**
    755     * @param interfaceNames .
    756     */
    757    public void setInterfaceNames(final String[] interfaceNames) {
    758        this.interfaceNames = interfaceNames;
    759    }
    760
    761    /**
    762     * @param interfaces .
    763     */
    764    public void setInterfaces(final int[] interfaces) {
    765        this.interfaces = interfaces;
    766    }
    767
    768    /**
    769     * @param major .
    770     */
    771    public void setMajor(final int major) {
    772        this.major = major;
    773    }
    774
    775    /**
    776     * @param methods .
    777     */
    778    public void setMethods(final Method[] methods) {
    779        this.methods = methods;
    780    }
    781
    782    /**
    783     * @param minor .
    784     */
    785    public void setMinor(final int minor) {
    786        this.minor = minor;
    787    }
    788
    789    /**
    790     * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done.
    791     */
    792    public void setRepository(final org.apache.bcel.util.Repository repository) { // TODO make protected?
    793        this.repository = repository;
    794    }
    795
    796    /**
    797     * Set absolute path to file this class was read from.
    798     */
    799    public void setSourceFileName(final String sourceFileName) {
    800        this.sourceFileName = sourceFileName;
    801    }
    802
    803    /**
    804     * @param superclassName .
    805     */
    806    public void setSuperclassName(final String superclassName) {
    807        this.superclassName = superclassName;
    808    }
    809
    810    /**
    811     * @param superclassNameIndex .
    812     */
    813    public void setSuperclassNameIndex(final int superclassNameIndex) {
    814        this.superclassNameIndex = superclassNameIndex;
    815    }
    816
    817    /**
    818     * @return String representing class contents.
    819     */
    820    @Override
    821    public String toString() {
    822        String access = Utility.accessToString(super.getAccessFlags(), true);
    823        access = access.isEmpty() ? "" : access + " ";
    824        final StringBuilder buf = new StringBuilder(128);
    825        buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ")
    826            .append(Utility.compactClassName(superclassName, false)).append('\n');
    827        final int size = interfaces.length;
    828        if (size > 0) {
    829            buf.append("implements\t\t");
    830            for (int i = 0; i < size; i++) {
    831                buf.append(interfaceNames[i]);
    832                if (i < size - 1) {
    833                    buf.append(", ");
    834                }
    835            }
    836            buf.append('\n');
    837        }
    838        buf.append("file name\t\t").append(fileName).append('\n');
    839        buf.append("compiled from\t\t").append(sourceFileName).append('\n');
    840        buf.append("compiler version\t").append(major).append(".").append(minor).append('\n');
    841        buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n');
    842        buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n");
    843        buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n");
    844        if (attributes.length > 0) {
    845            buf.append("\nAttribute(s):\n");
    846            for (final Attribute attribute : attributes) {
    847                buf.append(indent(attribute));
    848            }
    849        }
    850        final AnnotationEntry[] annotations = getAnnotationEntries();
    851        if (annotations != null && annotations.length > 0) {
    852            buf.append("\nAnnotation(s):\n");
    853            for (final AnnotationEntry annotation : annotations) {
    854                buf.append(indent(annotation));
    855            }
    856        }
    857        if (fields.length > 0) {
    858            buf.append("\n").append(fields.length).append(" fields:\n");
    859            for (final Field field : fields) {
    860                buf.append("\t").append(field).append('\n');
    861            }
    862        }
    863        if (methods.length > 0) {
    864            buf.append("\n").append(methods.length).append(" methods:\n");
    865            for (final Method method : methods) {
    866                buf.append("\t").append(method).append('\n');
    867            }
    868        }
    869        return buf.toString();
    870    }
    871}