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}