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