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 * Finds a visible field by name and type in this class and its super classes. 399 * @param fieldName the field name to find 400 * @param fieldType the field type to find 401 * @return field matching given name and type, null if field is not found or not accessible from this class. 402 * @throws ClassNotFoundException 403 * @since 6.8.0 404 */ 405 public Field findField(final String fieldName, final Type fieldType) throws ClassNotFoundException { 406 for (final Field field : fields) { 407 if (field.getName().equals(fieldName)) { 408 final Type fType = Type.getType(field.getSignature()); 409 /* 410 * TODO: Check if assignment compatibility is sufficient. What does Sun do? 411 */ 412 if (fType.equals(fieldType)) { 413 return field; 414 } 415 } 416 } 417 418 final JavaClass superclass = getSuperClass(); 419 if (superclass != null && !"java.lang.Object".equals(superclass.getClassName())) { 420 final Field f = superclass.findField(fieldName, fieldType); 421 if (f != null && (f.isPublic() || f.isProtected() || !f.isPrivate() && packageName.equals(superclass.getPackageName()))) { 422 return f; 423 } 424 } 425 final JavaClass[] implementedInterfaces = getInterfaces(); 426 if (implementedInterfaces != null) { 427 for (final JavaClass implementedInterface : implementedInterfaces) { 428 final Field f = implementedInterface.findField(fieldName, fieldType); 429 if (f != null) { 430 return f; 431 } 432 } 433 } 434 return null; 435 } 436 437 /** 438 * Gets all interfaces implemented by this JavaClass (transitively). 439 * 440 * @throws ClassNotFoundException if any of the class's superclasses or interfaces can't be found. 441 */ 442 public JavaClass[] getAllInterfaces() throws ClassNotFoundException { 443 final ClassQueue queue = new ClassQueue(); 444 final Set<JavaClass> allInterfaces = new TreeSet<>(); 445 queue.enqueue(this); 446 while (!queue.empty()) { 447 final JavaClass clazz = queue.dequeue(); 448 final JavaClass souper = clazz.getSuperClass(); 449 final JavaClass[] interfaces = clazz.getInterfaces(); 450 if (clazz.isInterface()) { 451 allInterfaces.add(clazz); 452 } else if (souper != null) { 453 queue.enqueue(souper); 454 } 455 for (final JavaClass iface : interfaces) { 456 queue.enqueue(iface); 457 } 458 } 459 return allInterfaces.toArray(JavaClass.EMPTY_ARRAY); 460 } 461 462 /** 463 * @return Annotations on the class 464 * @since 6.0 465 */ 466 public AnnotationEntry[] getAnnotationEntries() { 467 if (annotations == null) { 468 annotations = AnnotationEntry.createAnnotationEntries(getAttributes()); 469 } 470 471 return annotations; 472 } 473 474 /** 475 * @return Attributes of the class. 476 */ 477 public Attribute[] getAttributes() { 478 return attributes; 479 } 480 481 /** 482 * @return class in binary format 483 */ 484 public byte[] getBytes() { 485 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 486 try (DataOutputStream dos = new DataOutputStream(baos)) { 487 dump(dos); 488 } catch (final IOException e) { 489 e.printStackTrace(); 490 } 491 return baos.toByteArray(); 492 } 493 494 /** 495 * @return Class name. 496 */ 497 public String getClassName() { 498 return className; 499 } 500 501 /** 502 * @return Class name index. 503 */ 504 public int getClassNameIndex() { 505 return classNameIndex; 506 } 507 508 /** 509 * @return Constant pool. 510 */ 511 public ConstantPool getConstantPool() { 512 return constantPool; 513 } 514 515 /** 516 * @return Fields, i.e., variables of the class. Like the JVM spec mandates for the classfile format, these fields are 517 * those specific to this class, and not those of the superclass or superinterfaces. 518 */ 519 public Field[] getFields() { 520 return fields; 521 } 522 523 /** 524 * @return File name of class, aka SourceFile attribute value 525 */ 526 public String getFileName() { 527 return fileName; 528 } 529 530 /** 531 * @return Indices in constant pool of implemented interfaces. 532 */ 533 public int[] getInterfaceIndices() { 534 return interfaces; 535 } 536 537 /** 538 * @return Names of implemented interfaces. 539 */ 540 public String[] getInterfaceNames() { 541 return interfaceNames; 542 } 543 544 /** 545 * Gets interfaces directly implemented by this JavaClass. 546 * 547 * @throws ClassNotFoundException if any of the class's interfaces can't be found. 548 */ 549 public JavaClass[] getInterfaces() throws ClassNotFoundException { 550 final String[] interfaces = getInterfaceNames(); 551 final JavaClass[] classes = new JavaClass[interfaces.length]; 552 for (int i = 0; i < interfaces.length; i++) { 553 classes[i] = repository.loadClass(interfaces[i]); 554 } 555 return classes; 556 } 557 558 /** 559 * @return Major number of class file version. 560 */ 561 public int getMajor() { 562 return major; 563 } 564 565 /** 566 * @return A {@link Method} corresponding to java.lang.reflect.Method if any 567 */ 568 public Method getMethod(final java.lang.reflect.Method m) { 569 for (final Method method : methods) { 570 if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) { 571 return method; 572 } 573 } 574 return null; 575 } 576 577 /** 578 * @return Methods of the class. 579 */ 580 public Method[] getMethods() { 581 return methods; 582 } 583 584 /** 585 * @return Minor number of class file version. 586 */ 587 public int getMinor() { 588 return minor; 589 } 590 591 /** 592 * @return Package name. 593 */ 594 public String getPackageName() { 595 return packageName; 596 } 597 598 /** 599 * Gets the ClassRepository which holds its definition. By default this is the same as 600 * SyntheticRepository.getInstance(); 601 */ 602 public org.apache.bcel.util.Repository getRepository() { 603 return repository; 604 } 605 606 /** 607 * @return returns either HEAP (generated), FILE, or ZIP 608 */ 609 public final byte getSource() { 610 return source; 611 } 612 613 /** 614 * @return file name where this class was read from 615 */ 616 public String getSourceFileName() { 617 return sourceFileName; 618 } 619 620 /** 621 * Gets the source file path including the package path. 622 * 623 * @return path to original source file of parsed class, relative to original source directory. 624 * @since 6.7.0 625 */ 626 public String getSourceFilePath() { 627 final StringBuilder outFileName = new StringBuilder(); 628 if (!packageName.isEmpty()) { 629 outFileName.append(Utility.packageToPath(packageName)); 630 outFileName.append('/'); 631 } 632 outFileName.append(sourceFileName); 633 return outFileName.toString(); 634 } 635 636 /** 637 * @return the superclass for this JavaClass object, or null if this is java.lang.Object 638 * @throws ClassNotFoundException if the superclass can't be found 639 */ 640 public JavaClass getSuperClass() throws ClassNotFoundException { 641 if ("java.lang.Object".equals(getClassName())) { 642 return null; 643 } 644 return repository.loadClass(getSuperclassName()); 645 } 646 647 /** 648 * @return list of super classes of this class in ascending order, i.e., java.lang.Object is always the last element 649 * @throws ClassNotFoundException if any of the superclasses can't be found 650 */ 651 public JavaClass[] getSuperClasses() throws ClassNotFoundException { 652 JavaClass clazz = this; 653 final List<JavaClass> allSuperClasses = new ArrayList<>(); 654 for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) { 655 allSuperClasses.add(clazz); 656 } 657 return allSuperClasses.toArray(JavaClass.EMPTY_ARRAY); 658 } 659 660 /** 661 * returns the super class name of this class. In the case that this class is java.lang.Object, it will return itself 662 * (java.lang.Object). This is probably incorrect but isn't fixed at this time to not break existing clients. 663 * 664 * @return Superclass name. 665 */ 666 public String getSuperclassName() { 667 return superclassName; 668 } 669 670 /** 671 * @return Class name index. 672 */ 673 public int getSuperclassNameIndex() { 674 return superclassNameIndex; 675 } 676 677 /** 678 * Return value as defined by given BCELComparator strategy. By default return the hash code of the class name. 679 * 680 * @see Object#hashCode() 681 */ 682 @Override 683 public int hashCode() { 684 return bcelComparator.hashCode(this); 685 } 686 687 /** 688 * @return true, if this class is an implementation of interface inter 689 * @throws ClassNotFoundException if superclasses or superinterfaces of this class can't be found 690 */ 691 public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException { 692 if (!inter.isInterface()) { 693 throw new IllegalArgumentException(inter.getClassName() + " is no interface"); 694 } 695 if (this.equals(inter)) { 696 return true; 697 } 698 final JavaClass[] superInterfaces = getAllInterfaces(); 699 for (final JavaClass superInterface : superInterfaces) { 700 if (superInterface.equals(inter)) { 701 return true; 702 } 703 } 704 return false; 705 } 706 707 /** 708 * Equivalent to runtime "instanceof" operator. 709 * 710 * @return true if this JavaClass is derived from the super class 711 * @throws ClassNotFoundException if superclasses or superinterfaces of this object can't be found 712 */ 713 public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException { 714 if (this.equals(superclass)) { 715 return true; 716 } 717 for (final JavaClass clazz : getSuperClasses()) { 718 if (clazz.equals(superclass)) { 719 return true; 720 } 721 } 722 if (superclass.isInterface()) { 723 return implementationOf(superclass); 724 } 725 return false; 726 } 727 728 /** 729 * @since 6.0 730 */ 731 public final boolean isAnonymous() { 732 computeNestedTypeStatus(); 733 return this.isAnonymous; 734 } 735 736 public final boolean isClass() { 737 return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0; 738 } 739 740 /** 741 * @since 6.0 742 */ 743 public final boolean isNested() { 744 computeNestedTypeStatus(); 745 return this.isNested; 746 } 747 748 public final boolean isSuper() { 749 return (super.getAccessFlags() & Const.ACC_SUPER) != 0; 750 } 751 752 /** 753 * @param attributes . 754 */ 755 public void setAttributes(final Attribute[] attributes) { 756 this.attributes = attributes; 757 } 758 759 /** 760 * @param className . 761 */ 762 public void setClassName(final String className) { 763 this.className = className; 764 } 765 766 /** 767 * @param classNameIndex . 768 */ 769 public void setClassNameIndex(final int classNameIndex) { 770 this.classNameIndex = classNameIndex; 771 } 772 773 /** 774 * @param constantPool . 775 */ 776 public void setConstantPool(final ConstantPool constantPool) { 777 this.constantPool = constantPool; 778 } 779 780 /** 781 * @param fields . 782 */ 783 public void setFields(final Field[] fields) { 784 this.fields = fields; 785 } 786 787 /** 788 * Sets File name of class, aka SourceFile attribute value 789 */ 790 public void setFileName(final String fileName) { 791 this.fileName = fileName; 792 } 793 794 /** 795 * @param interfaceNames . 796 */ 797 public void setInterfaceNames(final String[] interfaceNames) { 798 this.interfaceNames = interfaceNames; 799 } 800 801 /** 802 * @param interfaces . 803 */ 804 public void setInterfaces(final int[] interfaces) { 805 this.interfaces = interfaces; 806 } 807 808 /** 809 * @param major . 810 */ 811 public void setMajor(final int major) { 812 this.major = major; 813 } 814 815 /** 816 * @param methods . 817 */ 818 public void setMethods(final Method[] methods) { 819 this.methods = methods; 820 } 821 822 /** 823 * @param minor . 824 */ 825 public void setMinor(final int minor) { 826 this.minor = minor; 827 } 828 829 /** 830 * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done. 831 */ 832 public void setRepository(final org.apache.bcel.util.Repository repository) { // TODO make protected? 833 this.repository = repository; 834 } 835 836 /** 837 * Sets absolute path to file this class was read from. 838 */ 839 public void setSourceFileName(final String sourceFileName) { 840 this.sourceFileName = sourceFileName; 841 } 842 843 /** 844 * @param superclassName . 845 */ 846 public void setSuperclassName(final String superclassName) { 847 this.superclassName = superclassName; 848 } 849 850 /** 851 * @param superclassNameIndex . 852 */ 853 public void setSuperclassNameIndex(final int superclassNameIndex) { 854 this.superclassNameIndex = superclassNameIndex; 855 } 856 857 /** 858 * @return String representing class contents. 859 */ 860 @Override 861 public String toString() { 862 String access = Utility.accessToString(super.getAccessFlags(), true); 863 access = access.isEmpty() ? "" : access + " "; 864 final StringBuilder buf = new StringBuilder(128); 865 buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ") 866 .append(Utility.compactClassName(superclassName, false)).append('\n'); 867 final int size = interfaces.length; 868 if (size > 0) { 869 buf.append("implements\t\t"); 870 for (int i = 0; i < size; i++) { 871 buf.append(interfaceNames[i]); 872 if (i < size - 1) { 873 buf.append(", "); 874 } 875 } 876 buf.append('\n'); 877 } 878 buf.append("file name\t\t").append(fileName).append('\n'); 879 buf.append("compiled from\t\t").append(sourceFileName).append('\n'); 880 buf.append("compiler version\t").append(major).append(".").append(minor).append('\n'); 881 buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n'); 882 buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n"); 883 buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n"); 884 if (attributes.length > 0) { 885 buf.append("\nAttribute(s):\n"); 886 for (final Attribute attribute : attributes) { 887 buf.append(indent(attribute)); 888 } 889 } 890 final AnnotationEntry[] annotations = getAnnotationEntries(); 891 if (annotations != null && annotations.length > 0) { 892 buf.append("\nAnnotation(s):\n"); 893 for (final AnnotationEntry annotation : annotations) { 894 buf.append(indent(annotation)); 895 } 896 } 897 if (fields.length > 0) { 898 buf.append("\n").append(fields.length).append(" fields:\n"); 899 for (final Field field : fields) { 900 buf.append("\t").append(field).append('\n'); 901 } 902 } 903 if (methods.length > 0) { 904 buf.append("\n").append(methods.length).append(" methods:\n"); 905 for (final Method method : methods) { 906 buf.append("\t").append(method).append('\n'); 907 } 908 } 909 return buf.toString(); 910 } 911}