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