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