1/*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20package com.sun.org.apache.bcel.internal.classfile;
21
22import java.io.ByteArrayOutputStream;
23import java.io.DataOutputStream;
24import java.io.File;
25import java.io.FileOutputStream;
26import java.io.IOException;
27import java.io.OutputStream;
28import java.util.ArrayList;
29import java.util.List;
30import java.util.Set;
31import java.util.StringTokenizer;
32import java.util.TreeSet;
33
34import com.sun.org.apache.bcel.internal.Const;
35import com.sun.org.apache.bcel.internal.generic.Type;
36import com.sun.org.apache.bcel.internal.util.BCELComparator;
37import com.sun.org.apache.bcel.internal.util.ClassQueue;
38import com.sun.org.apache.bcel.internal.util.SyntheticRepository;
39import jdk.xml.internal.SecuritySupport;
40
41/**
42 * Represents a Java class, i.e., the data structures, constant pool, fields,
43 * methods and commands contained in a Java .class file. See <a
44 * href="http://docs.oracle.com/javase/specs/">JVM specification</a> for
45 * details. The intent of this class is to represent a parsed or otherwise
46 * existing class file. Those interested in programatically generating classes
47 * should see the <a href="../generic/ClassGen.html">ClassGen</a> class.
48 *
49 * @version $Id: JavaClass.java 1750227 2016-06-25 21:47:10Z ggregory $
50 * @see com.sun.org.apache.bcel.internal.generic.ClassGen
51 */
52public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> {
53
54    private String file_name;
55    private String package_name;
56    private String source_file_name = "<Unknown>";
57    private int class_name_index;
58    private int superclass_name_index;
59    private String class_name;
60    private String superclass_name;
61    private int major;
62    private int minor; // Compiler version
63    private ConstantPool constant_pool; // Constant pool
64    private int[] interfaces; // implemented interfaces
65    private String[] interface_names;
66    private Field[] fields; // Fields, i.e., variables of class
67    private Method[] methods; // methods defined in the class
68    private Attribute[] attributes; // attributes defined in the class
69    private AnnotationEntry[] annotations;   // annotations defined on the class
70    private byte source = HEAP; // Generated in memory
71    private boolean isAnonymous = false;
72    private boolean isNested = false;
73    private boolean computedNestedTypeStatus = false;
74    public static final byte HEAP = 1;
75    public static final byte FILE = 2;
76    public static final byte ZIP = 3;
77
78    private static BCELComparator bcelComparator = new BCELComparator() {
79
80        @Override
81        public boolean equals(final Object o1, final Object o2) {
82            final JavaClass THIS = (JavaClass) o1;
83            final JavaClass THAT = (JavaClass) o2;
84            return THIS.getClassName().equals(THAT.getClassName());
85        }
86
87        @Override
88        public int hashCode(final Object o) {
89            final JavaClass THIS = (JavaClass) o;
90            return THIS.getClassName().hashCode();
91        }
92    };
93    /**
94     * In cases where we go ahead and create something, use the default
95     * SyntheticRepository, because we don't know any better.
96     */
97    private transient com.sun.org.apache.bcel.internal.util.Repository repository
98            = SyntheticRepository.getInstance();
99
100    /**
101     * Constructor gets all contents as arguments.
102     *
103     * @param class_name_index Index into constant pool referencing a
104     * ConstantClass that represents this class.
105     * @param superclass_name_index Index into constant pool referencing a
106     * ConstantClass that represents this class's superclass.
107     * @param file_name File name
108     * @param major Major compiler version
109     * @param minor Minor compiler version
110     * @param access_flags Access rights defined by bit flags
111     * @param constant_pool Array of constants
112     * @param interfaces Implemented interfaces
113     * @param fields Class fields
114     * @param methods Class methods
115     * @param attributes Class attributes
116     * @param source Read from file or generated in memory?
117     */
118    public JavaClass(final int class_name_index, final int superclass_name_index,
119            final String file_name, final int major, final int minor, final int access_flags,
120            final ConstantPool constant_pool, int[] interfaces, Field[] fields,
121            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     * Constructor gets all contents as arguments.
182     *
183     * @param class_name_index Class name
184     * @param superclass_name_index Superclass name
185     * @param file_name File name
186     * @param major Major compiler version
187     * @param minor Minor compiler version
188     * @param access_flags Access rights defined by bit flags
189     * @param constant_pool Array of constants
190     * @param interfaces Implemented interfaces
191     * @param fields Class fields
192     * @param methods Class methods
193     * @param attributes Class attributes
194     */
195    public JavaClass(final int class_name_index, final int superclass_name_index,
196            final String file_name, final int major, final int minor, final int access_flags,
197            final ConstantPool constant_pool, final int[] interfaces, final Field[] fields,
198            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     * Called by objects that are traversing the nodes of the tree implicitly
205     * defined by the contents of a Java class. I.e., the hierarchy of methods,
206     * fields, attributes, etc. spawns a tree of objects.
207     *
208     * @param v Visitor object
209     */
210    @Override
211    public void accept(final Visitor v) {
212        v.visitJavaClass(this);
213    }
214
215    /**
216     * Dump class to a file.
217     *
218     * @param file Output file
219     * @throws IOException
220     */
221    public void dump(final File file) throws IOException {
222        final String parent = file.getParent();
223        if (parent != null) {
224            final File dir = new File(parent);
225            if (!dir.mkdirs()) { // either was not created or already existed
226                if (!SecuritySupport.isDirectory(dir)) {
227                    throw new IOException("Could not create the directory " + dir);
228                }
229            }
230        }
231        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
232            dump(dos);
233        }
234    }
235
236    /**
237     * Dump class to a file named file_name.
238     *
239     * @param _file_name Output file name
240     * @throws IOException
241     */
242    public void dump(final String _file_name) throws IOException {
243        dump(new File(_file_name));
244    }
245
246    /**
247     * @return class in binary format
248     */
249    public byte[] getBytes() {
250        final ByteArrayOutputStream s = new ByteArrayOutputStream();
251        final DataOutputStream ds = new DataOutputStream(s);
252        try {
253            dump(ds);
254        } catch (final IOException e) {
255            System.err.println("Error dumping class: " + e.getMessage());
256        } finally {
257            try {
258                ds.close();
259            } catch (final IOException e2) {
260                System.err.println("Error dumping class: " + e2.getMessage());
261            }
262        }
263        return s.toByteArray();
264    }
265
266    /**
267     * Dump Java class to output stream in binary format.
268     *
269     * @param file Output stream
270     * @throws IOException
271     */
272    public void dump(final OutputStream file) throws IOException {
273        dump(new DataOutputStream(file));
274    }
275
276    /**
277     * Dump Java class to output stream in binary format.
278     *
279     * @param file Output stream
280     * @throws IOException
281     */
282    private void dump(final DataOutputStream file) throws IOException {
283        file.writeInt(Const.JVM_CLASSFILE_MAGIC);
284        file.writeShort(minor);
285        file.writeShort(major);
286        constant_pool.dump(file);
287        file.writeShort(super.getAccessFlags());
288        file.writeShort(class_name_index);
289        file.writeShort(superclass_name_index);
290        file.writeShort(interfaces.length);
291        for (final int interface1 : interfaces) {
292            file.writeShort(interface1);
293        }
294        file.writeShort(fields.length);
295        for (final Field field : fields) {
296            field.dump(file);
297        }
298        file.writeShort(methods.length);
299        for (final Method method : methods) {
300            method.dump(file);
301        }
302        if (attributes != null) {
303            file.writeShort(attributes.length);
304            for (final Attribute attribute : attributes) {
305                attribute.dump(file);
306            }
307        } else {
308            file.writeShort(0);
309        }
310        file.flush();
311    }
312
313    /**
314     * @return Attributes of the class.
315     */
316    public Attribute[] getAttributes() {
317        return attributes;
318    }
319
320    /**
321     * @return Annotations on the class
322     * @since 6.0
323     */
324    public AnnotationEntry[] getAnnotationEntries() {
325        if (annotations == null) {
326            annotations = AnnotationEntry.createAnnotationEntries(getAttributes());
327        }
328
329        return annotations;
330    }
331
332    /**
333     * @return Class name.
334     */
335    public String getClassName() {
336        return class_name;
337    }
338
339    /**
340     * @return Package name.
341     */
342    public String getPackageName() {
343        return package_name;
344    }
345
346    /**
347     * @return Class name index.
348     */
349    public int getClassNameIndex() {
350        return class_name_index;
351    }
352
353    /**
354     * @return Constant pool.
355     */
356    public ConstantPool getConstantPool() {
357        return constant_pool;
358    }
359
360    /**
361     * @return Fields, i.e., variables of the class. Like the JVM spec mandates
362     * for the classfile format, these fields are those specific to this class,
363     * and not those of the superclass or superinterfaces.
364     */
365    public Field[] getFields() {
366        return fields;
367    }
368
369    /**
370     * @return File name of class, aka SourceFile attribute value
371     */
372    public String getFileName() {
373        return file_name;
374    }
375
376    /**
377     * @return Names of implemented interfaces.
378     */
379    public String[] getInterfaceNames() {
380        return interface_names;
381    }
382
383    /**
384     * @return Indices in constant pool of implemented interfaces.
385     */
386    public int[] getInterfaceIndices() {
387        return interfaces;
388    }
389
390    /**
391     * @return Major number of class file version.
392     */
393    public int getMajor() {
394        return major;
395    }
396
397    /**
398     * @return Methods of the class.
399     */
400    public Method[] getMethods() {
401        return methods;
402    }
403
404    /**
405     * @return A {@link Method} corresponding to java.lang.reflect.Method if any
406     */
407    public Method getMethod(final java.lang.reflect.Method m) {
408        for (final Method method : methods) {
409            if (m.getName().equals(method.getName()) && (m.getModifiers() == method.getModifiers())
410                    && Type.getSignature(m).equals(method.getSignature())) {
411                return method;
412            }
413        }
414        return null;
415    }
416
417    /**
418     * @return Minor number of class file version.
419     */
420    public int getMinor() {
421        return minor;
422    }
423
424    /**
425     * @return sbsolute path to file where this class was read from
426     */
427    public String getSourceFileName() {
428        return source_file_name;
429    }
430
431    /**
432     * returns the super class name of this class. In the case that this class
433     * is java.lang.Object, it will return itself (java.lang.Object). This is
434     * probably incorrect but isn't fixed at this time to not break existing
435     * clients.
436     *
437     * @return Superclass name.
438     */
439    public String getSuperclassName() {
440        return superclass_name;
441    }
442
443    /**
444     * @return Class name index.
445     */
446    public int getSuperclassNameIndex() {
447        return superclass_name_index;
448    }
449
450    /**
451     * @param attributes .
452     */
453    public void setAttributes(final Attribute[] attributes) {
454        this.attributes = attributes;
455    }
456
457    /**
458     * @param class_name .
459     */
460    public void setClassName(final String class_name) {
461        this.class_name = class_name;
462    }
463
464    /**
465     * @param class_name_index .
466     */
467    public void setClassNameIndex(final int class_name_index) {
468        this.class_name_index = class_name_index;
469    }
470
471    /**
472     * @param constant_pool .
473     */
474    public void setConstantPool(final ConstantPool constant_pool) {
475        this.constant_pool = constant_pool;
476    }
477
478    /**
479     * @param fields .
480     */
481    public void setFields(final Field[] fields) {
482        this.fields = fields;
483    }
484
485    /**
486     * Set File name of class, aka SourceFile attribute value
487     */
488    public void setFileName(final String file_name) {
489        this.file_name = file_name;
490    }
491
492    /**
493     * @param interface_names .
494     */
495    public void setInterfaceNames(final String[] interface_names) {
496        this.interface_names = interface_names;
497    }
498
499    /**
500     * @param interfaces .
501     */
502    public void setInterfaces(final int[] interfaces) {
503        this.interfaces = interfaces;
504    }
505
506    /**
507     * @param major .
508     */
509    public void setMajor(final int major) {
510        this.major = major;
511    }
512
513    /**
514     * @param methods .
515     */
516    public void setMethods(final Method[] methods) {
517        this.methods = methods;
518    }
519
520    /**
521     * @param minor .
522     */
523    public void setMinor(final int minor) {
524        this.minor = minor;
525    }
526
527    /**
528     * Set absolute path to file this class was read from.
529     */
530    public void setSourceFileName(final String source_file_name) {
531        this.source_file_name = source_file_name;
532    }
533
534    /**
535     * @param superclass_name .
536     */
537    public void setSuperclassName(final String superclass_name) {
538        this.superclass_name = superclass_name;
539    }
540
541    /**
542     * @param superclass_name_index .
543     */
544    public void setSuperclassNameIndex(final int superclass_name_index) {
545        this.superclass_name_index = superclass_name_index;
546    }
547
548    /**
549     * @return String representing class contents.
550     */
551    @Override
552    public String toString() {
553        String access = Utility.accessToString(super.getAccessFlags(), true);
554        access = access.isEmpty() ? "" : (access + " ");
555        final StringBuilder buf = new StringBuilder(128);
556        buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(
557                class_name).append(" extends ").append(
558                        Utility.compactClassName(superclass_name, false)).append('\n');
559        final int size = interfaces.length;
560        if (size > 0) {
561            buf.append("implements\t\t");
562            for (int i = 0; i < size; i++) {
563                buf.append(interface_names[i]);
564                if (i < size - 1) {
565                    buf.append(", ");
566                }
567            }
568            buf.append('\n');
569        }
570        buf.append("filename\t\t").append(file_name).append('\n');
571        buf.append("compiled from\t\t").append(source_file_name).append('\n');
572        buf.append("compiler version\t").append(major).append(".").append(minor).append('\n');
573        buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n');
574        buf.append("constant pool\t\t").append(constant_pool.getLength()).append(" entries\n");
575        buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n");
576        if (attributes.length > 0) {
577            buf.append("\nAttribute(s):\n");
578            for (final Attribute attribute : attributes) {
579                buf.append(indent(attribute));
580            }
581        }
582        final AnnotationEntry[] annotations = getAnnotationEntries();
583        if (annotations != null && annotations.length > 0) {
584            buf.append("\nAnnotation(s):\n");
585            for (final AnnotationEntry annotation : annotations) {
586                buf.append(indent(annotation));
587            }
588        }
589        if (fields.length > 0) {
590            buf.append("\n").append(fields.length).append(" fields:\n");
591            for (final Field field : fields) {
592                buf.append("\t").append(field).append('\n');
593            }
594        }
595        if (methods.length > 0) {
596            buf.append("\n").append(methods.length).append(" methods:\n");
597            for (final Method method : methods) {
598                buf.append("\t").append(method).append('\n');
599            }
600        }
601        return buf.toString();
602    }
603
604    private static String indent(final Object obj) {
605        final StringTokenizer tok = new StringTokenizer(obj.toString(), "\n");
606        final StringBuilder buf = new StringBuilder();
607        while (tok.hasMoreTokens()) {
608            buf.append("\t").append(tok.nextToken()).append("\n");
609        }
610        return buf.toString();
611    }
612
613    /**
614     * @return deep copy of this class
615     */
616    public JavaClass copy() {
617        JavaClass c = null;
618        try {
619            c = (JavaClass) clone();
620            c.constant_pool = constant_pool.copy();
621            c.interfaces = interfaces.clone();
622            c.interface_names = interface_names.clone();
623            c.fields = new Field[fields.length];
624            for (int i = 0; i < fields.length; i++) {
625                c.fields[i] = fields[i].copy(c.constant_pool);
626            }
627            c.methods = new Method[methods.length];
628            for (int i = 0; i < methods.length; i++) {
629                c.methods[i] = methods[i].copy(c.constant_pool);
630            }
631            c.attributes = new Attribute[attributes.length];
632            for (int i = 0; i < attributes.length; i++) {
633                c.attributes[i] = attributes[i].copy(c.constant_pool);
634            }
635        } catch (final CloneNotSupportedException e) {
636            // TODO should this throw?
637        }
638        return c;
639    }
640
641    public final boolean isSuper() {
642        return (super.getAccessFlags() & Const.ACC_SUPER) != 0;
643    }
644
645    public final boolean isClass() {
646        return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0;
647    }
648
649    /**
650     * @since 6.0
651     */
652    public final boolean isAnonymous() {
653        computeNestedTypeStatus();
654        return this.isAnonymous;
655    }
656
657    /**
658     * @since 6.0
659     */
660    public final boolean isNested() {
661        computeNestedTypeStatus();
662        return this.isNested;
663    }
664
665    private void computeNestedTypeStatus() {
666        if (computedNestedTypeStatus) {
667            return;
668        }
669        for (final Attribute attribute : this.attributes) {
670            if (attribute instanceof InnerClasses) {
671                final InnerClass[] innerClasses = ((InnerClasses) attribute).getInnerClasses();
672                for (final InnerClass innerClasse : innerClasses) {
673                    boolean innerClassAttributeRefersToMe = false;
674                    String inner_class_name = constant_pool.getConstantString(innerClasse.getInnerClassIndex(),
675                            Const.CONSTANT_Class);
676                    inner_class_name = Utility.compactClassName(inner_class_name);
677                    if (inner_class_name.equals(getClassName())) {
678                        innerClassAttributeRefersToMe = true;
679                    }
680                    if (innerClassAttributeRefersToMe) {
681                        this.isNested = true;
682                        if (innerClasse.getInnerNameIndex() == 0) {
683                            this.isAnonymous = true;
684                        }
685                    }
686                }
687            }
688        }
689        this.computedNestedTypeStatus = true;
690    }
691
692    /**
693     * @return returns either HEAP (generated), FILE, or ZIP
694     */
695    public final byte getSource() {
696        return source;
697    }
698
699    /**
700     * ******************* New repository functionality ********************
701     */
702    /**
703     * Gets the ClassRepository which holds its definition. By default this is
704     * the same as SyntheticRepository.getInstance();
705     */
706    public com.sun.org.apache.bcel.internal.util.Repository getRepository() {
707        return repository;
708    }
709
710    /**
711     * Sets the ClassRepository which loaded the JavaClass. Should be called
712     * immediately after parsing is done.
713     */
714    public void setRepository(final com.sun.org.apache.bcel.internal.util.Repository repository) {
715        this.repository = repository;
716    }
717
718    /**
719     * Equivalent to runtime "instanceof" operator.
720     *
721     * @return true if this JavaClass is derived from the super class
722     * @throws ClassNotFoundException if superclasses or superinterfaces of this
723     * object can't be found
724     */
725    public final boolean instanceOf(final JavaClass super_class) throws ClassNotFoundException {
726        if (this.equals(super_class)) {
727            return true;
728        }
729        final JavaClass[] super_classes = getSuperClasses();
730        for (final JavaClass super_classe : super_classes) {
731            if (super_classe.equals(super_class)) {
732                return true;
733            }
734        }
735        if (super_class.isInterface()) {
736            return implementationOf(super_class);
737        }
738        return false;
739    }
740
741    /**
742     * @return true, if this class is an implementation of interface inter
743     * @throws ClassNotFoundException if superclasses or superinterfaces of this
744     * class can't be found
745     */
746    public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException {
747        if (!inter.isInterface()) {
748            throw new IllegalArgumentException(inter.getClassName() + " is no interface");
749        }
750        if (this.equals(inter)) {
751            return true;
752        }
753        final JavaClass[] super_interfaces = getAllInterfaces();
754        for (final JavaClass super_interface : super_interfaces) {
755            if (super_interface.equals(inter)) {
756                return true;
757            }
758        }
759        return false;
760    }
761
762    /**
763     * @return the superclass for this JavaClass object, or null if this is
764     * java.lang.Object
765     * @throws ClassNotFoundException if the superclass can't be found
766     */
767    public JavaClass getSuperClass() throws ClassNotFoundException {
768        if ("java.lang.Object".equals(getClassName())) {
769            return null;
770        }
771        return repository.loadClass(getSuperclassName());
772    }
773
774    /**
775     * @return list of super classes of this class in ascending order, i.e.,
776     * java.lang.Object is always the last element
777     * @throws ClassNotFoundException if any of the superclasses can't be found
778     */
779    public JavaClass[] getSuperClasses() throws ClassNotFoundException {
780        JavaClass clazz = this;
781        final List<JavaClass> allSuperClasses = new ArrayList<>();
782        for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) {
783            allSuperClasses.add(clazz);
784        }
785        return allSuperClasses.toArray(new JavaClass[allSuperClasses.size()]);
786    }
787
788    /**
789     * Get interfaces directly implemented by this JavaClass.
790     */
791    public JavaClass[] getInterfaces() throws ClassNotFoundException {
792        final String[] _interfaces = getInterfaceNames();
793        final JavaClass[] classes = new JavaClass[_interfaces.length];
794        for (int i = 0; i < _interfaces.length; i++) {
795            classes[i] = repository.loadClass(_interfaces[i]);
796        }
797        return classes;
798    }
799
800    /**
801     * Get all interfaces implemented by this JavaClass (transitively).
802     */
803    public JavaClass[] getAllInterfaces() throws ClassNotFoundException {
804        final ClassQueue queue = new ClassQueue();
805        final Set<JavaClass> allInterfaces = new TreeSet<>();
806        queue.enqueue(this);
807        while (!queue.empty()) {
808            final JavaClass clazz = queue.dequeue();
809            final JavaClass souper = clazz.getSuperClass();
810            final JavaClass[] _interfaces = clazz.getInterfaces();
811            if (clazz.isInterface()) {
812                allInterfaces.add(clazz);
813            } else {
814                if (souper != null) {
815                    queue.enqueue(souper);
816                }
817            }
818            for (final JavaClass _interface : _interfaces) {
819                queue.enqueue(_interface);
820            }
821        }
822        return allInterfaces.toArray(new JavaClass[allInterfaces.size()]);
823    }
824
825    /**
826     * @return Comparison strategy object
827     */
828    public static BCELComparator getComparator() {
829        return bcelComparator;
830    }
831
832    /**
833     * @param comparator Comparison strategy object
834     */
835    public static void setComparator(final BCELComparator comparator) {
836        bcelComparator = comparator;
837    }
838
839    /**
840     * Return value as defined by given BCELComparator strategy. By default two
841     * JavaClass objects are said to be equal when their class names are equal.
842     *
843     * @see java.lang.Object#equals(java.lang.Object)
844     */
845    @Override
846    public boolean equals(final Object obj) {
847        return bcelComparator.equals(this, obj);
848    }
849
850    /**
851     * Return the natural ordering of two JavaClasses. This ordering is based on
852     * the class name
853     *
854     * @since 6.0
855     */
856    @Override
857    public int compareTo(final JavaClass obj) {
858        return getClassName().compareTo(obj.getClassName());
859    }
860
861    /**
862     * Return value as defined by given BCELComparator strategy. By default
863     * return the hashcode of the class name.
864     *
865     * @see java.lang.Object#hashCode()
866     */
867    @Override
868    public int hashCode() {
869        return bcelComparator.hashCode(this);
870    }
871}
872