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