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