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