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