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