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$ 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 */ JavaClass(final int class_name_index, final int superclass_name_index, final String file_name, final int major, final int minor, final int access_flags, final ConstantPool constant_pool, int[] interfaces, Field[] fields, Method[] methods, Attribute[] attributes, final byte source)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 */ JavaClass(final int class_name_index, final int superclass_name_index, final String file_name, final int major, final int minor, final int access_flags, final ConstantPool constant_pool, final int[] interfaces, final Field[] fields, final Method[] methods, final Attribute[] attributes)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 accept( final Visitor v )212 public void accept( final Visitor v ) { 213 v.visitJavaClass(this); 214 } 215 216 217 /* Print debug information depending on `JavaClass.debug' 218 */ Debug( final String str )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 */ dump(final File file)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 */ dump( final String _file_name )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 */ getBytes()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 */ dump( final OutputStream file )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 */ dump( final DataOutputStream file )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 */ getAttributes()332 public Attribute[] getAttributes() { 333 return attributes; 334 } 335 336 /** 337 * @return Annotations on the class 338 * @since 6.0 339 */ getAnnotationEntries()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 */ getClassName()351 public String getClassName() { 352 return class_name; 353 } 354 355 356 /** 357 * @return Package name. 358 */ getPackageName()359 public String getPackageName() { 360 return package_name; 361 } 362 363 364 /** 365 * @return Class name index. 366 */ getClassNameIndex()367 public int getClassNameIndex() { 368 return class_name_index; 369 } 370 371 372 /** 373 * @return Constant pool. 374 */ getConstantPool()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 */ getFields()385 public Field[] getFields() { 386 return fields; 387 } 388 389 390 /** 391 * @return File name of class, aka SourceFile attribute value 392 */ getFileName()393 public String getFileName() { 394 return file_name; 395 } 396 397 398 /** 399 * @return Names of implemented interfaces. 400 */ getInterfaceNames()401 public String[] getInterfaceNames() { 402 return interface_names; 403 } 404 405 406 /** 407 * @return Indices in constant pool of implemented interfaces. 408 */ getInterfaceIndices()409 public int[] getInterfaceIndices() { 410 return interfaces; 411 } 412 413 414 /** 415 * @return Major number of class file version. 416 */ getMajor()417 public int getMajor() { 418 return major; 419 } 420 421 422 /** 423 * @return Methods of the class. 424 */ getMethods()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 */ getMethod( final java.lang.reflect.Method m )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 */ getMinor()448 public int getMinor() { 449 return minor; 450 } 451 452 453 /** 454 * @return sbsolute path to file where this class was read from 455 */ getSourceFileName()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 */ getSuperclassName()468 public String getSuperclassName() { 469 return superclass_name; 470 } 471 472 473 /** 474 * @return Class name index. 475 */ getSuperclassNameIndex()476 public int getSuperclassNameIndex() { 477 return superclass_name_index; 478 } 479 480 /** 481 * @param attributes . 482 */ setAttributes( final Attribute[] attributes )483 public void setAttributes( final Attribute[] attributes ) { 484 this.attributes = attributes; 485 } 486 487 488 /** 489 * @param class_name . 490 */ setClassName( final String class_name )491 public void setClassName( final String class_name ) { 492 this.class_name = class_name; 493 } 494 495 496 /** 497 * @param class_name_index . 498 */ setClassNameIndex( final int class_name_index )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 */ setConstantPool( final ConstantPool constant_pool )507 public void setConstantPool( final ConstantPool constant_pool ) { 508 this.constant_pool = constant_pool; 509 } 510 511 512 /** 513 * @param fields . 514 */ setFields( final Field[] fields )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 */ setFileName( final String file_name )523 public void setFileName( final String file_name ) { 524 this.file_name = file_name; 525 } 526 527 528 /** 529 * @param interface_names . 530 */ setInterfaceNames( final String[] interface_names )531 public void setInterfaceNames( final String[] interface_names ) { 532 this.interface_names = interface_names; 533 } 534 535 536 /** 537 * @param interfaces . 538 */ setInterfaces( final int[] interfaces )539 public void setInterfaces( final int[] interfaces ) { 540 this.interfaces = interfaces; 541 } 542 543 544 /** 545 * @param major . 546 */ setMajor( final int major )547 public void setMajor( final int major ) { 548 this.major = major; 549 } 550 551 552 /** 553 * @param methods . 554 */ setMethods( final Method[] methods )555 public void setMethods( final Method[] methods ) { 556 this.methods = methods; 557 } 558 559 560 /** 561 * @param minor . 562 */ setMinor( final int minor )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 */ setSourceFileName( final String source_file_name )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 */ setSuperclassName( final String superclass_name )579 public void setSuperclassName( final String superclass_name ) { 580 this.superclass_name = superclass_name; 581 } 582 583 584 /** 585 * @param superclass_name_index . 586 */ setSuperclassNameIndex( final int superclass_name_index )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 toString()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 indent( final Object obj )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 */ copy()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 isSuper()688 public final boolean isSuper() { 689 return (super.getAccessFlags() & Const.ACC_SUPER) != 0; 690 } 691 692 isClass()693 public final boolean isClass() { 694 return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0; 695 } 696 697 /** 698 * @since 6.0 699 */ isAnonymous()700 public final boolean isAnonymous() { 701 computeNestedTypeStatus(); 702 return this.isAnonymous; 703 } 704 705 /** 706 * @since 6.0 707 */ isNested()708 public final boolean isNested() { 709 computeNestedTypeStatus(); 710 return this.isNested; 711 } 712 computeNestedTypeStatus()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 */ getSource()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 */ getRepository()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 */ setRepository( final org.apache.bcel.util.Repository repository )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 */ instanceOf( final JavaClass super_class )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 */ implementationOf( final JavaClass inter )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 */ getSuperClass()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 */ getSuperClasses()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 */ getInterfaces()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 */ getAllInterfaces()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 */ getComparator()882 public static BCELComparator getComparator() { 883 return bcelComparator; 884 } 885 886 887 /** 888 * @param comparator Comparison strategy object 889 */ setComparator( final BCELComparator comparator )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 equals( final Object obj )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 compareTo( final JavaClass obj )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 hashCode()926 public int hashCode() { 927 return bcelComparator.hashCode(this); 928 } 929 } 930