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