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