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