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