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