View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   *
17   */
18  package org.apache.bcel.classfile;
19  
20  import java.io.ByteArrayOutputStream;
21  import java.io.DataOutputStream;
22  import java.io.File;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.OutputStream;
26  import java.util.ArrayList;
27  import java.util.List;
28  import java.util.Set;
29  import java.util.StringTokenizer;
30  import java.util.TreeSet;
31  
32  import org.apache.bcel.Const;
33  import org.apache.bcel.generic.Type;
34  import org.apache.bcel.util.BCELComparator;
35  import org.apache.bcel.util.ClassQueue;
36  import org.apache.bcel.util.SyntheticRepository;
37  
38  /**
39   * Represents a Java class, i.e., the data structures, constant pool,
40   * fields, methods and commands contained in a Java .class file.
41   * See <a href="http://docs.oracle.com/javase/specs/">JVM specification</a> for details.
42   * The intent of this class is to represent a parsed or otherwise existing
43   * class file.  Those interested in programatically generating classes
44   * should see the <a href="../generic/ClassGen.html">ClassGen</a> class.
45  
46   * @version $Id: JavaClass.html 1018313 2017-09-18 09:03:04Z britter $
47   * @see org.apache.bcel.generic.ClassGen
48   */
49  public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> {
50  
51      private String file_name;
52      private String package_name;
53      private String source_file_name = "<Unknown>";
54      private int class_name_index;
55      private int superclass_name_index;
56      private String class_name;
57      private String superclass_name;
58      private int major;
59      private int minor; // Compiler version
60      private ConstantPool constant_pool; // Constant pool
61      private int[] interfaces; // implemented interfaces
62      private String[] interface_names;
63      private Field[] fields; // Fields, i.e., variables of class
64      private Method[] methods; // methods defined in the class
65      private Attribute[] attributes; // attributes defined in the class
66      private AnnotationEntry[] annotations;   // annotations defined on the class
67      private byte source = HEAP; // Generated in memory
68      private boolean isAnonymous = false;
69      private boolean isNested = false;
70      private boolean computedNestedTypeStatus = false;
71      public static final byte HEAP = 1;
72      public static final byte FILE = 2;
73      public static final byte ZIP = 3;
74      private static final boolean debug = Boolean.getBoolean("JavaClass.debug"); // Debugging on/off
75  
76      private static BCELComparator bcelComparator = new BCELComparator() {
77  
78          @Override
79          public boolean equals( final Object o1, final Object o2 ) {
80              final JavaClass THIS = (JavaClass) o1;
81              final JavaClass THAT = (JavaClass) o2;
82              return THIS.getClassName().equals(THAT.getClassName());
83          }
84  
85  
86          @Override
87          public int hashCode( final Object o ) {
88              final JavaClass THIS = (JavaClass) o;
89              return THIS.getClassName().hashCode();
90          }
91      };
92      /**
93       * In cases where we go ahead and create something,
94       * use the default SyntheticRepository, because we
95       * don't know any better.
96       */
97      private transient org.apache.bcel.util.Repository repository = SyntheticRepository
98              .getInstance();
99  
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("filename\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);
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 }