    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 *   https://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.bcel.classfile;
    020
    021import java.io.ByteArrayOutputStream;
    022import java.io.DataOutputStream;
    023import java.io.File;
    024import java.io.FileOutputStream;
    025import java.io.IOException;
    026import java.io.OutputStream;
    027import java.util.ArrayList;
    028import java.util.Arrays;
    029import java.util.List;
    030import java.util.Objects;
    031import java.util.Set;
    032import java.util.StringTokenizer;
    033import java.util.TreeSet;
    034
    035import org.apache.bcel.Const;
    036import org.apache.bcel.generic.Type;
    037import org.apache.bcel.util.BCELComparator;
    038import org.apache.bcel.util.ClassQueue;
    039import org.apache.bcel.util.SyntheticRepository;
    040import org.apache.commons.lang3.ArrayUtils;
    041
    042/**
    043 * Represents a Java class, i.e., the data structures, constant pool, fields, methods and commands contained in a Java
    044 * .class file. See <a href="https://docs.oracle.com/javase/specs/">JVM specification</a> for details. The intent of
    045 * this class is to represent a parsed or otherwise existing class file. Those interested in programmatically generating
    046 * classes should see the <a href="../generic/ClassGen.html">ClassGen</a> class.
    047 *
    048 * @see org.apache.bcel.generic.ClassGen
    049 */
    050public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> {
    051
    052    /**
    053     * The standard class file extension.
    054     *
    055     * @since 6.7.0
    056     */
    057    public static final String EXTENSION = ".class";
    058
    059    /**
    060     * Empty array.
    061     *
    062     * @since 6.6.0
    063     */
    064    public static final JavaClass[] EMPTY_ARRAY = {};
    065
    066    public static final byte HEAP = 1;
    067    public static final byte FILE = 2;
    068    public static final byte ZIP = 3;
    069    private static final boolean debug = Boolean.getBoolean("JavaClass.debug"); // Debugging on/off
    070
    071    private static BCELComparator<JavaClass> bcelComparator = new BCELComparator<JavaClass>() {
    072
    073        @Override
    074        public boolean equals(final JavaClass a, final JavaClass b) {
    075            return a == b || a != null && b != null && Objects.equals(a.getClassName(), b.getClassName());
    076        }
    077
    078        @Override
    079        public int hashCode(final JavaClass o) {
    080            return o != null ? Objects.hashCode(o.getClassName()) : 0;
    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<JavaClass> 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<JavaClass> 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    private boolean isRecord;
    139
    140    private boolean computedNestedTypeStatus;
    141    private boolean computedRecord;
    142
    143    /**
    144     * In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any
    145     * better.
    146     */
    147    private transient org.apache.bcel.util.Repository repository = SyntheticRepository.getInstance();
    148
    149    /**
    150     * Constructor gets all contents as arguments.
    151     *
    152     * @param classNameIndex Class name
    153     * @param superclassNameIndex Superclass name
    154     * @param fileName File name
    155     * @param major Major compiler version
    156     * @param minor Minor compiler version
    157     * @param accessFlags Access rights defined by bit flags
    158     * @param constantPool Array of constants
    159     * @param interfaces Implemented interfaces
    160     * @param fields Class fields
    161     * @param methods Class methods
    162     * @param attributes Class attributes
    163     */
    164    public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
    165        final ConstantPool constantPool, final int[] interfaces, final Field[] fields, final Method[] methods, final Attribute[] attributes) {
    166        this(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes, HEAP);
    167    }
    168
    169    /**
    170     * Constructor gets all contents as arguments.
    171     *
    172     * @param classNameIndex Index into constant pool referencing a ConstantClass that represents this class.
    173     * @param superclassNameIndex Index into constant pool referencing a ConstantClass that represents this class's
    174     *        superclass.
    175     * @param fileName File name
    176     * @param major Major compiler version
    177     * @param minor Minor compiler version
    178     * @param accessFlags Access rights defined by bit flags
    179     * @param constantPool Array of constants
    180     * @param interfaces Implemented interfaces
    181     * @param fields Class fields
    182     * @param methods Class methods
    183     * @param attributes Class attributes
    184     * @param source Read from file or generated in memory?
    185     */
    186    public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
    187        final ConstantPool constantPool, int[] interfaces, Field[] fields, Method[] methods, Attribute[] attributes, final byte source) {
    188        super(accessFlags);
    189        interfaces = ArrayUtils.nullToEmpty(interfaces);
    190        if (attributes == null) {
    191            attributes = Attribute.EMPTY_ARRAY;
    192        }
    193        if (fields == null) {
    194            fields = Field.EMPTY_ARRAY;
    195        }
    196        if (methods == null) {
    197            methods = Method.EMPTY_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 computeIsRecord() {
    265        if (computedRecord) {
    266            return;
    267        }
    268        for (final Attribute attribute : this.attributes) {
    269            if (attribute instanceof Record) {
    270                isRecord = true;
    271                break;
    272            }
    273        }
    274        this.computedRecord = true;
    275    }
    276
    277    private void computeNestedTypeStatus() {
    278        if (computedNestedTypeStatus) {
    279            return;
    280        }
    281        for (final Attribute attribute : this.attributes) {
    282            if (attribute instanceof InnerClasses) {
    283                ((InnerClasses) attribute).forEach(innerClass ->  {
    284                    boolean innerClassAttributeRefersToMe = false;
    285                    String innerClassName = constantPool.getConstantString(innerClass.getInnerClassIndex(), Const.CONSTANT_Class);
    286                    innerClassName = Utility.compactClassName(innerClassName, false);
    287                    if (innerClassName.equals(getClassName())) {
    288                        innerClassAttributeRefersToMe = true;
    289                    }
    290                    if (innerClassAttributeRefersToMe) {
    291                        this.isNested = true;
    292                        if (innerClass.getInnerNameIndex() == 0) {
    293                            this.isAnonymous = true;
    294                        }
    295                    }
    296                });
    297            }
    298        }
    299        this.computedNestedTypeStatus = true;
    300    }
    301
    302    /**
    303     * @return deep copy of this class
    304     */
    305    public JavaClass copy() {
    306        try {
    307            final JavaClass c = (JavaClass) clone();
    308            c.constantPool = constantPool.copy();
    309            c.interfaces = interfaces.clone();
    310            c.interfaceNames = interfaceNames.clone();
    311            c.fields = new Field[fields.length];
    312            Arrays.setAll(c.fields, i -> fields[i].copy(c.constantPool));
    313            c.methods = new Method[methods.length];
    314            Arrays.setAll(c.methods, i -> methods[i].copy(c.constantPool));
    315            c.attributes = new Attribute[attributes.length];
    316            Arrays.setAll(c.attributes, i -> attributes[i].copy(c.constantPool));
    317            return c;
    318        } catch (final CloneNotSupportedException e) {
    319            return null;
    320        }
    321    }
    322
    323    /**
    324     * Dump Java class to output stream in binary format.
    325     *
    326     * @param file Output stream
    327     * @throws IOException if an I/O error occurs.
    328     */
    329    public void dump(final DataOutputStream file) throws IOException {
    330        file.writeInt(Const.JVM_CLASSFILE_MAGIC);
    331        file.writeShort(minor);
    332        file.writeShort(major);
    333        constantPool.dump(file);
    334        file.writeShort(super.getAccessFlags());
    335        file.writeShort(classNameIndex);
    336        file.writeShort(superclassNameIndex);
    337        file.writeShort(interfaces.length);
    338        for (final int interface1 : interfaces) {
    339            file.writeShort(interface1);
    340        }
    341        file.writeShort(fields.length);
    342        for (final Field field : fields) {
    343            field.dump(file);
    344        }
    345        file.writeShort(methods.length);
    346        for (final Method method : methods) {
    347            method.dump(file);
    348        }
    349        if (attributes != null) {
    350            file.writeShort(attributes.length);
    351            for (final Attribute attribute : attributes) {
    352                attribute.dump(file);
    353            }
    354        } else {
    355            file.writeShort(0);
    356        }
    357        file.flush();
    358    }
    359
    360    /**
    361     * Dump class to a file.
    362     *
    363     * @param file Output file
    364     * @throws IOException if an I/O error occurs.
    365     */
    366    public void dump(final File file) throws IOException {
    367        final String parent = file.getParent();
    368        if (parent != null) {
    369            final File dir = new File(parent);
    370            if (!dir.mkdirs() && !dir.isDirectory()) {
    371                throw new IOException("Could not create the directory " + dir);
    372            }
    373        }
    374        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
    375            dump(dos);
    376        }
    377    }
    378
    379    /**
    380     * Dump Java class to output stream in binary format.
    381     *
    382     * @param file Output stream
    383     * @throws IOException if an I/O error occurs.
    384     */
    385    public void dump(final OutputStream file) throws IOException {
    386        dump(new DataOutputStream(file));
    387    }
    388
    389    /**
    390     * Dump class to a file named fileName.
    391     *
    392     * @param fileName Output file name
    393     * @throws IOException if an I/O error occurs.
    394     */
    395    public void dump(final String fileName) throws IOException {
    396        dump(new File(fileName));
    397    }
    398
    399    /**
    400     * Return value as defined by given BCELComparator strategy. By default two JavaClass objects are said to be equal when
    401     * their class names are equal.
    402     *
    403     * @see Object#equals(Object)
    404     */
    405    @Override
    406    public boolean equals(final Object obj) {
    407        return obj instanceof JavaClass && bcelComparator.equals(this, (JavaClass) obj);
    408    }
    409
    410    /**
    411     * Finds a visible field by name and type in this class and its super classes.
    412     * @param fieldName the field name to find
    413     * @param fieldType the field type to find
    414     * @return field matching given name and type, null if field is not found or not accessible from this class.
    415     * @throws ClassNotFoundException
    416     * @since 6.8.0
    417     */
    418    public Field findField(final String fieldName, final Type fieldType) throws ClassNotFoundException {
    419        for (final Field field : fields) {
    420            if (field.getName().equals(fieldName)) {
    421                final Type fType = Type.getType(field.getSignature());
    422                /*
    423                 * TODO: Check if assignment compatibility is sufficient. What does Sun do?
    424                 */
    425                if (fType.equals(fieldType)) {
    426                    return field;
    427                }
    428            }
    429        }
    430
    431        final JavaClass superclass = getSuperClass();
    432        if (superclass != null && !"java.lang.Object".equals(superclass.getClassName())) {
    433            final Field f = superclass.findField(fieldName, fieldType);
    434            if (f != null && (f.isPublic() || f.isProtected() || !f.isPrivate() && packageName.equals(superclass.getPackageName()))) {
    435                return f;
    436            }
    437        }
    438        final JavaClass[] implementedInterfaces = getInterfaces();
    439        if (implementedInterfaces != null) {
    440            for (final JavaClass implementedInterface : implementedInterfaces) {
    441                final Field f = implementedInterface.findField(fieldName, fieldType);
    442                if (f != null) {
    443                    return f;
    444                }
    445            }
    446        }
    447        return null;
    448    }
    449
    450    /**
    451     * Gets all interfaces implemented by this JavaClass (transitively).
    452     *
    453     * @throws ClassNotFoundException if any of the class's superclasses or interfaces can't be found.
    454     */
    455    public JavaClass[] getAllInterfaces() throws ClassNotFoundException {
    456        final ClassQueue queue = new ClassQueue();
    457        final Set<JavaClass> allInterfaces = new TreeSet<>();
    458        queue.enqueue(this);
    459        while (!queue.empty()) {
    460            final JavaClass clazz = queue.dequeue();
    461            final JavaClass souper = clazz.getSuperClass();
    462            final JavaClass[] interfaces = clazz.getInterfaces();
    463            if (clazz.isInterface()) {
    464                allInterfaces.add(clazz);
    465            } else if (souper != null) {
    466                queue.enqueue(souper);
    467            }
    468            for (final JavaClass iface : interfaces) {
    469                queue.enqueue(iface);
    470            }
    471        }
    472        return allInterfaces.toArray(EMPTY_ARRAY);
    473    }
    474
    475    /**
    476     * @return Annotations on the class
    477     * @since 6.0
    478     */
    479    public AnnotationEntry[] getAnnotationEntries() {
    480        if (annotations == null) {
    481            annotations = AnnotationEntry.createAnnotationEntries(getAttributes());
    482        }
    483
    484        return annotations;
    485    }
    486
    487    /**
    488     * Gets attribute for given tag.
    489     * @return Attribute for given tag, null if not found.
    490     * Refer to {@link org.apache.bcel.Const#ATTR_UNKNOWN} constants named ATTR_* for possible values.
    491     * @since 6.10.0
    492     */
    493    @SuppressWarnings("unchecked")
    494    public final <T extends Attribute> T getAttribute(final byte tag) {
    495        for (final Attribute attribute : getAttributes()) {
    496            if (attribute.getTag() == tag) {
    497                return (T) attribute;
    498            }
    499        }
    500        return null;
    501    }
    502
    503    /**
    504     * @return Attributes of the class.
    505     */
    506    public Attribute[] getAttributes() {
    507        return attributes;
    508    }
    509
    510    /**
    511     * @return class in binary format
    512     */
    513    public byte[] getBytes() {
    514        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
    515        try (DataOutputStream dos = new DataOutputStream(baos)) {
    516            dump(dos);
    517        } catch (final IOException e) {
    518            e.printStackTrace();
    519        }
    520        return baos.toByteArray();
    521    }
    522
    523    /**
    524     * @return Class name.
    525     */
    526    public String getClassName() {
    527        return className;
    528    }
    529
    530    /**
    531     * @return Class name index.
    532     */
    533    public int getClassNameIndex() {
    534        return classNameIndex;
    535    }
    536
    537    /**
    538     * @return Constant pool.
    539     */
    540    public ConstantPool getConstantPool() {
    541        return constantPool;
    542    }
    543
    544    /**
    545     * @return Fields, i.e., variables of the class. Like the JVM spec mandates for the classfile format, these fields are
    546     *         those specific to this class, and not those of the superclass or superinterfaces.
    547     */
    548    public Field[] getFields() {
    549        return fields;
    550    }
    551
    552    /**
    553     * @return File name of class, aka SourceFile attribute value
    554     */
    555    public String getFileName() {
    556        return fileName;
    557    }
    558
    559    /**
    560     * @return Indices in constant pool of implemented interfaces.
    561     */
    562    public int[] getInterfaceIndices() {
    563        return interfaces;
    564    }
    565
    566    /**
    567     * @return Names of implemented interfaces.
    568     */
    569    public String[] getInterfaceNames() {
    570        return interfaceNames;
    571    }
    572
    573    /**
    574     * Gets interfaces directly implemented by this JavaClass.
    575     *
    576     * @throws ClassNotFoundException if any of the class's interfaces can't be found.
    577     */
    578    public JavaClass[] getInterfaces() throws ClassNotFoundException {
    579        final String[] interfaces = getInterfaceNames();
    580        final JavaClass[] classes = new JavaClass[interfaces.length];
    581        for (int i = 0; i < interfaces.length; i++) {
    582            classes[i] = repository.loadClass(interfaces[i]);
    583        }
    584        return classes;
    585    }
    586
    587    /**
    588     * @return Major number of class file version.
    589     */
    590    public int getMajor() {
    591        return major;
    592    }
    593
    594    /**
    595     * @return A {@link Method} corresponding to java.lang.reflect.Method if any
    596     */
    597    public Method getMethod(final java.lang.reflect.Method m) {
    598        for (final Method method : methods) {
    599            if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) {
    600                return method;
    601            }
    602        }
    603        return null;
    604    }
    605
    606    /**
    607     * @return Methods of the class.
    608     */
    609    public Method[] getMethods() {
    610        return methods;
    611    }
    612
    613    /**
    614     * @return Minor number of class file version.
    615     */
    616    public int getMinor() {
    617        return minor;
    618    }
    619
    620    /**
    621     * @return Package name.
    622     */
    623    public String getPackageName() {
    624        return packageName;
    625    }
    626
    627    /**
    628     * Gets the ClassRepository which holds its definition. By default this is the same as
    629     * SyntheticRepository.getInstance();
    630     */
    631    public org.apache.bcel.util.Repository getRepository() {
    632        return repository;
    633    }
    634
    635    /**
    636     * @return either HEAP (generated), FILE, or ZIP
    637     */
    638    public final byte getSource() {
    639        return source;
    640    }
    641
    642    /**
    643     * @return file name where this class was read from
    644     */
    645    public String getSourceFileName() {
    646        return sourceFileName;
    647    }
    648
    649    /**
    650     * Gets the source file path including the package path.
    651     *
    652     * @return path to original source file of parsed class, relative to original source directory.
    653     * @since 6.7.0
    654     */
    655    public String getSourceFilePath() {
    656        final StringBuilder outFileName = new StringBuilder();
    657        if (!packageName.isEmpty()) {
    658            outFileName.append(Utility.packageToPath(packageName));
    659            outFileName.append('/');
    660        }
    661        outFileName.append(sourceFileName);
    662        return outFileName.toString();
    663    }
    664
    665    /**
    666     * @return the superclass for this JavaClass object, or null if this is {@link Object}
    667     * @throws ClassNotFoundException if the superclass can't be found
    668     */
    669    public JavaClass getSuperClass() throws ClassNotFoundException {
    670        if ("java.lang.Object".equals(getClassName())) {
    671            return null;
    672        }
    673        return repository.loadClass(getSuperclassName());
    674    }
    675
    676    /**
    677     * @return list of super classes of this class in ascending order, i.e., {@link Object} is always the last element.
    678     * @throws ClassNotFoundException if any of the superclasses can't be found
    679     */
    680    public JavaClass[] getSuperClasses() throws ClassNotFoundException {
    681        JavaClass clazz = this;
    682        final List<JavaClass> allSuperClasses = new ArrayList<>();
    683        for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) {
    684            allSuperClasses.add(clazz);
    685        }
    686        return allSuperClasses.toArray(EMPTY_ARRAY);
    687    }
    688
    689    /**
    690     * returns the super class name of this class. In the case that this class is {@link Object}, it will return itself
    691     * ({@link Object}). This is probably incorrect but isn't fixed at this time to not break existing clients.
    692     *
    693     * @return Superclass name.
    694     */
    695    public String getSuperclassName() {
    696        return superclassName;
    697    }
    698
    699    /**
    700     * @return Class name index.
    701     */
    702    public int getSuperclassNameIndex() {
    703        return superclassNameIndex;
    704    }
    705
    706    /**
    707     * Return value as defined by given BCELComparator strategy. By default return the hash code of the class name.
    708     *
    709     * @see Object#hashCode()
    710     */
    711    @Override
    712    public int hashCode() {
    713        return bcelComparator.hashCode(this);
    714    }
    715
    716    /**
    717     * @return true, if this class is an implementation of interface inter
    718     * @throws ClassNotFoundException if superclasses or superinterfaces of this class can't be found
    719     */
    720    public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException {
    721        if (!inter.isInterface()) {
    722            throw new IllegalArgumentException(inter.getClassName() + " is no interface");
    723        }
    724        if (equals(inter)) {
    725            return true;
    726        }
    727        final JavaClass[] superInterfaces = getAllInterfaces();
    728        for (final JavaClass superInterface : superInterfaces) {
    729            if (superInterface.equals(inter)) {
    730                return true;
    731            }
    732        }
    733        return false;
    734    }
    735
    736    /**
    737     * Equivalent to runtime "instanceof" operator.
    738     *
    739     * @return true if this JavaClass is derived from the super class
    740     * @throws ClassNotFoundException if superclasses or superinterfaces of this object can't be found
    741     */
    742    public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException {
    743        if (equals(superclass)) {
    744            return true;
    745        }
    746        for (final JavaClass clazz : getSuperClasses()) {
    747            if (clazz.equals(superclass)) {
    748                return true;
    749            }
    750        }
    751        if (superclass.isInterface()) {
    752            return implementationOf(superclass);
    753        }
    754        return false;
    755    }
    756
    757    /**
    758     * @since 6.0
    759     */
    760    public final boolean isAnonymous() {
    761        computeNestedTypeStatus();
    762        return this.isAnonymous;
    763    }
    764
    765    public final boolean isClass() {
    766        return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0;
    767    }
    768
    769    /**
    770     * @since 6.0
    771     */
    772    public final boolean isNested() {
    773        computeNestedTypeStatus();
    774        return this.isNested;
    775    }
    776
    777    /**
    778     * Tests whether this class was declared as a record
    779     *
    780     * @return true if a record attribute is present, false otherwise.
    781     * @since 6.9.0
    782     */
    783    public boolean isRecord() {
    784        computeIsRecord();
    785        return this.isRecord;
    786    }
    787
    788    public final boolean isSuper() {
    789        return (super.getAccessFlags() & Const.ACC_SUPER) != 0;
    790    }
    791
    792    /**
    793     * @param attributes .
    794     */
    795    public void setAttributes(final Attribute[] attributes) {
    796        this.attributes = attributes != null ? attributes : Attribute.EMPTY_ARRAY;
    797    }
    798
    799    /**
    800     * @param className .
    801     */
    802    public void setClassName(final String className) {
    803        this.className = className;
    804    }
    805
    806    /**
    807     * @param classNameIndex .
    808     */
    809    public void setClassNameIndex(final int classNameIndex) {
    810        this.classNameIndex = classNameIndex;
    811    }
    812
    813    /**
    814     * @param constantPool .
    815     */
    816    public void setConstantPool(final ConstantPool constantPool) {
    817        this.constantPool = constantPool;
    818    }
    819
    820    /**
    821     * @param fields .
    822     */
    823    public void setFields(final Field[] fields) {
    824        this.fields = fields != null ? fields : Field.EMPTY_ARRAY;
    825    }
    826
    827    /**
    828     * Sets File name of class, aka SourceFile attribute value
    829     */
    830    public void setFileName(final String fileName) {
    831        this.fileName = fileName;
    832    }
    833
    834    /**
    835     * @param interfaceNames .
    836     */
    837    public void setInterfaceNames(final String[] interfaceNames) {
    838        this.interfaceNames = ArrayUtils.nullToEmpty(interfaceNames);
    839    }
    840
    841    /**
    842     * @param interfaces .
    843     */
    844    public void setInterfaces(final int[] interfaces) {
    845        this.interfaces = ArrayUtils.nullToEmpty(interfaces);
    846    }
    847
    848    /**
    849     * @param major .
    850     */
    851    public void setMajor(final int major) {
    852        this.major = major;
    853    }
    854
    855    /**
    856     * @param methods .
    857     */
    858    public void setMethods(final Method[] methods) {
    859        this.methods = methods != null ? methods : Method.EMPTY_ARRAY;
    860    }
    861
    862    /**
    863     * @param minor .
    864     */
    865    public void setMinor(final int minor) {
    866        this.minor = minor;
    867    }
    868
    869    /**
    870     * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done.
    871     */
    872    public void setRepository(final org.apache.bcel.util.Repository repository) { // TODO make protected?
    873        this.repository = repository;
    874    }
    875
    876    /**
    877     * Sets absolute path to file this class was read from.
    878     */
    879    public void setSourceFileName(final String sourceFileName) {
    880        this.sourceFileName = sourceFileName;
    881    }
    882
    883    /**
    884     * @param superclassName .
    885     */
    886    public void setSuperclassName(final String superclassName) {
    887        this.superclassName = superclassName;
    888    }
    889
    890    /**
    891     * @param superclassNameIndex .
    892     */
    893    public void setSuperclassNameIndex(final int superclassNameIndex) {
    894        this.superclassNameIndex = superclassNameIndex;
    895    }
    896
    897    /**
    898     * @return String representing class contents.
    899     */
    900    @Override
    901    public String toString() {
    902        String access = Utility.accessToString(super.getAccessFlags(), true);
    903        access = access.isEmpty() ? "" : access + " ";
    904        final StringBuilder buf = new StringBuilder(128);
    905        buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ")
    906            .append(Utility.compactClassName(superclassName, false)).append('\n');
    907        final int size = interfaces.length;
    908        if (size > 0) {
    909            buf.append("implements\t\t");
    910            for (int i = 0; i < size; i++) {
    911                buf.append(interfaceNames[i]);
    912                if (i < size - 1) {
    913                    buf.append(", ");
    914                }
    915            }
    916            buf.append('\n');
    917        }
    918        buf.append("file name\t\t").append(fileName).append('\n');
    919        buf.append("compiled from\t\t").append(sourceFileName).append('\n');
    920        buf.append("compiler version\t").append(major).append(".").append(minor).append('\n');
    921        buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n');
    922        buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n");
    923        buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n");
    924        if (attributes.length > 0) {
    925            buf.append("\nAttribute(s):\n");
    926            for (final Attribute attribute : attributes) {
    927                buf.append(indent(attribute));
    928            }
    929        }
    930        final AnnotationEntry[] annotations = getAnnotationEntries();
    931        if (annotations != null && annotations.length > 0) {
    932            buf.append("\nAnnotation(s):\n");
    933            for (final AnnotationEntry annotation : annotations) {
    934                buf.append(indent(annotation));
    935            }
    936        }
    937        if (fields.length > 0) {
    938            buf.append("\n").append(fields.length).append(" fields:\n");
    939            for (final Field field : fields) {
    940                buf.append("\t").append(field).append('\n');
    941            }
    942        }
    943        if (methods.length > 0) {
    944            buf.append("\n").append(methods.length).append(" methods:\n");
    945            for (final Method method : methods) {
    946                buf.append("\t").append(method).append('\n');
    947            }
    948        }
    949        return buf.toString();
    950    }
    951}