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 * @see org.apache.bcel.generic.ClassGen
048 */
049public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> {
050
051    private String file_name;
052    private String package_name;
053    private String source_file_name = "<Unknown>";
054    private int class_name_index;
055    private int superclass_name_index;
056    private String class_name;
057    private String superclass_name;
058    private int major;
059    private int minor; // Compiler version
060    private ConstantPool constant_pool; // Constant pool
061    private int[] interfaces; // implemented interfaces
062    private String[] interface_names;
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 class_name_index Index into constant pool referencing a
105     * ConstantClass that represents this class.
106     * @param superclass_name_index Index into constant pool referencing a
107     * ConstantClass that represents this class's superclass.
108     * @param file_name 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 constant_pool 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 class_name_index, final int superclass_name_index, final String file_name, final int major,
120            final int minor, final int access_flags, final ConstantPool constant_pool, 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.class_name_index = class_name_index;
136        this.superclass_name_index = superclass_name_index;
137        this.file_name = file_name;
138        this.major = major;
139        this.minor = minor;
140        this.constant_pool = constant_pool;
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                source_file_name = ((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        class_name = constant_pool.getConstantString(class_name_index, Const.CONSTANT_Class);
158        class_name = Utility.compactClassName(class_name, false);
159        final int index = class_name.lastIndexOf('.');
160        if (index < 0) {
161            package_name = "";
162        } else {
163            package_name = class_name.substring(0, index);
164        }
165        if (superclass_name_index > 0) {
166            // May be zero -> class is java.lang.Object
167            superclass_name = constant_pool.getConstantString(superclass_name_index,
168                    Const.CONSTANT_Class);
169            superclass_name = Utility.compactClassName(superclass_name, false);
170        } else {
171            superclass_name = "java.lang.Object";
172        }
173        interface_names = new String[interfaces.length];
174        for (int i = 0; i < interfaces.length; i++) {
175            final String str = constant_pool.getConstantString(interfaces[i], Const.CONSTANT_Class);
176            interface_names[i] = Utility.compactClassName(str, false);
177        }
178    }
179
180
181    /**
182     * Constructor gets all contents as arguments.
183     *
184     * @param class_name_index Class name
185     * @param superclass_name_index Superclass name
186     * @param file_name 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 constant_pool 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 class_name_index, final int superclass_name_index, final String file_name, final int major,
197            final int minor, final int access_flags, final ConstantPool constant_pool, final int[] interfaces,
198            final Field[] fields, final Method[] methods, final Attribute[] attributes) {
199        this(class_name_index, superclass_name_index, file_name, major, minor, access_flags,
200                constant_pool, 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 file_name.
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        constant_pool.dump(file);
302        file.writeShort(super.getAccessFlags());
303        file.writeShort(class_name_index);
304        file.writeShort(superclass_name_index);
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 class_name;
353    }
354
355
356    /**
357     * @return Package name.
358     */
359    public String getPackageName() {
360        return package_name;
361    }
362
363
364    /**
365     * @return Class name index.
366     */
367    public int getClassNameIndex() {
368        return class_name_index;
369    }
370
371
372    /**
373     * @return Constant pool.
374     */
375    public ConstantPool getConstantPool() {
376        return constant_pool;
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 file_name;
395    }
396
397
398    /**
399     * @return Names of implemented interfaces.
400     */
401    public String[] getInterfaceNames() {
402        return interface_names;
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 source_file_name;
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 superclass_name;
470    }
471
472
473    /**
474     * @return Class name index.
475     */
476    public int getSuperclassNameIndex() {
477        return superclass_name_index;
478    }
479
480    /**
481     * @param attributes .
482     */
483    public void setAttributes( final Attribute[] attributes ) {
484        this.attributes = attributes;
485    }
486
487
488    /**
489     * @param class_name .
490     */
491    public void setClassName( final String class_name ) {
492        this.class_name = class_name;
493    }
494
495
496    /**
497     * @param class_name_index .
498     */
499    public void setClassNameIndex( final int class_name_index ) {
500        this.class_name_index = class_name_index;
501    }
502
503
504    /**
505     * @param constant_pool .
506     */
507    public void setConstantPool( final ConstantPool constant_pool ) {
508        this.constant_pool = constant_pool;
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 file_name ) {
524        this.file_name = file_name;
525    }
526
527
528    /**
529     * @param interface_names .
530     */
531    public void setInterfaceNames( final String[] interface_names ) {
532        this.interface_names = interface_names;
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 source_file_name ) {
572        this.source_file_name = source_file_name;
573    }
574
575
576    /**
577     * @param superclass_name .
578     */
579    public void setSuperclassName( final String superclass_name ) {
580        this.superclass_name = superclass_name;
581    }
582
583
584    /**
585     * @param superclass_name_index .
586     */
587    public void setSuperclassNameIndex( final int superclass_name_index ) {
588        this.superclass_name_index = superclass_name_index;
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                class_name).append(" extends ").append(
602                Utility.compactClassName(superclass_name, 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(interface_names[i]);
608                if (i < size - 1) {
609                    buf.append(", ");
610                }
611            }
612            buf.append('\n');
613        }
614        buf.append("file name\t\t").append(file_name).append('\n');
615        buf.append("compiled from\t\t").append(source_file_name).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(constant_pool.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.constant_pool = constant_pool.copy();
667            c.interfaces = interfaces.clone();
668            c.interface_names = interface_names.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.constant_pool);
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.constant_pool);
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.constant_pool);
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 = constant_pool.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}