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