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 private boolean isRecord; 137 138 private boolean computedNestedTypeStatus; 139 private boolean computedRecord; 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 interfaces = ArrayUtils.nullToEmpty(interfaces); 188 if (attributes == null) { 189 attributes = Attribute.EMPTY_ARRAY; 190 } 191 if (fields == null) { 192 fields = Field.EMPTY_ARRAY; 193 } 194 if (methods == null) { 195 methods = Method.EMPTY_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 computeIsRecord() { 263 if (computedRecord) { 264 return; 265 } 266 for (final Attribute attribute : this.attributes) { 267 if (attribute instanceof Record) { 268 isRecord = true; 269 break; 270 } 271 } 272 this.computedRecord = true; 273 } 274 275 private void computeNestedTypeStatus() { 276 if (computedNestedTypeStatus) { 277 return; 278 } 279 for (final Attribute attribute : this.attributes) { 280 if (attribute instanceof InnerClasses) { 281 ((InnerClasses) attribute).forEach(innerClass -> { 282 boolean innerClassAttributeRefersToMe = false; 283 String innerClassName = constantPool.getConstantString(innerClass.getInnerClassIndex(), Const.CONSTANT_Class); 284 innerClassName = Utility.compactClassName(innerClassName, false); 285 if (innerClassName.equals(getClassName())) { 286 innerClassAttributeRefersToMe = true; 287 } 288 if (innerClassAttributeRefersToMe) { 289 this.isNested = true; 290 if (innerClass.getInnerNameIndex() == 0) { 291 this.isAnonymous = true; 292 } 293 } 294 }); 295 } 296 } 297 this.computedNestedTypeStatus = true; 298 } 299 300 /** 301 * @return deep copy of this class 302 */ 303 public JavaClass copy() { 304 try { 305 final JavaClass c = (JavaClass) clone(); 306 c.constantPool = constantPool.copy(); 307 c.interfaces = interfaces.clone(); 308 c.interfaceNames = interfaceNames.clone(); 309 c.fields = new Field[fields.length]; 310 Arrays.setAll(c.fields, i -> fields[i].copy(c.constantPool)); 311 c.methods = new Method[methods.length]; 312 Arrays.setAll(c.methods, i -> methods[i].copy(c.constantPool)); 313 c.attributes = new Attribute[attributes.length]; 314 Arrays.setAll(c.attributes, i -> attributes[i].copy(c.constantPool)); 315 return c; 316 } catch (final CloneNotSupportedException e) { 317 return null; 318 } 319 } 320 321 /** 322 * Dump Java class to output stream in binary format. 323 * 324 * @param file Output stream 325 * @throws IOException if an I/O error occurs. 326 */ 327 public void dump(final DataOutputStream file) throws IOException { 328 file.writeInt(Const.JVM_CLASSFILE_MAGIC); 329 file.writeShort(minor); 330 file.writeShort(major); 331 constantPool.dump(file); 332 file.writeShort(super.getAccessFlags()); 333 file.writeShort(classNameIndex); 334 file.writeShort(superclassNameIndex); 335 file.writeShort(interfaces.length); 336 for (final int interface1 : interfaces) { 337 file.writeShort(interface1); 338 } 339 file.writeShort(fields.length); 340 for (final Field field : fields) { 341 field.dump(file); 342 } 343 file.writeShort(methods.length); 344 for (final Method method : methods) { 345 method.dump(file); 346 } 347 if (attributes != null) { 348 file.writeShort(attributes.length); 349 for (final Attribute attribute : attributes) { 350 attribute.dump(file); 351 } 352 } else { 353 file.writeShort(0); 354 } 355 file.flush(); 356 } 357 358 /** 359 * Dump class to a file. 360 * 361 * @param file Output file 362 * @throws IOException if an I/O error occurs. 363 */ 364 public void dump(final File file) throws IOException { 365 final String parent = file.getParent(); 366 if (parent != null) { 367 final File dir = new File(parent); 368 if (!dir.mkdirs() && !dir.isDirectory()) { 369 throw new IOException("Could not create the directory " + dir); 370 } 371 } 372 try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) { 373 dump(dos); 374 } 375 } 376 377 /** 378 * Dump Java class to output stream in binary format. 379 * 380 * @param file Output stream 381 * @throws IOException if an I/O error occurs. 382 */ 383 public void dump(final OutputStream file) throws IOException { 384 dump(new DataOutputStream(file)); 385 } 386 387 /** 388 * Dump class to a file named fileName. 389 * 390 * @param fileName Output file name 391 * @throws IOException if an I/O error occurs. 392 */ 393 public void dump(final String fileName) throws IOException { 394 dump(new File(fileName)); 395 } 396 397 /** 398 * Return value as defined by given BCELComparator strategy. By default two JavaClass objects are said to be equal when 399 * their class names are equal. 400 * 401 * @see Object#equals(Object) 402 */ 403 @Override 404 public boolean equals(final Object obj) { 405 return obj instanceof JavaClass && bcelComparator.equals(this, (JavaClass) obj); 406 } 407 408 /** 409 * Finds a visible field by name and type in this class and its super classes. 410 * @param fieldName the field name to find 411 * @param fieldType the field type to find 412 * @return field matching given name and type, null if field is not found or not accessible from this class. 413 * @throws ClassNotFoundException 414 * @since 6.8.0 415 */ 416 public Field findField(final String fieldName, final Type fieldType) throws ClassNotFoundException { 417 for (final Field field : fields) { 418 if (field.getName().equals(fieldName)) { 419 final Type fType = Type.getType(field.getSignature()); 420 /* 421 * TODO: Check if assignment compatibility is sufficient. What does Sun do? 422 */ 423 if (fType.equals(fieldType)) { 424 return field; 425 } 426 } 427 } 428 429 final JavaClass superclass = getSuperClass(); 430 if (superclass != null && !"java.lang.Object".equals(superclass.getClassName())) { 431 final Field f = superclass.findField(fieldName, fieldType); 432 if (f != null && (f.isPublic() || f.isProtected() || !f.isPrivate() && packageName.equals(superclass.getPackageName()))) { 433 return f; 434 } 435 } 436 final JavaClass[] implementedInterfaces = getInterfaces(); 437 if (implementedInterfaces != null) { 438 for (final JavaClass implementedInterface : implementedInterfaces) { 439 final Field f = implementedInterface.findField(fieldName, fieldType); 440 if (f != null) { 441 return f; 442 } 443 } 444 } 445 return null; 446 } 447 448 /** 449 * Gets all interfaces implemented by this JavaClass (transitively). 450 * 451 * @throws ClassNotFoundException if any of the class's superclasses or interfaces can't be found. 452 */ 453 public JavaClass[] getAllInterfaces() throws ClassNotFoundException { 454 final ClassQueue queue = new ClassQueue(); 455 final Set<JavaClass> allInterfaces = new TreeSet<>(); 456 queue.enqueue(this); 457 while (!queue.empty()) { 458 final JavaClass clazz = queue.dequeue(); 459 final JavaClass souper = clazz.getSuperClass(); 460 final JavaClass[] interfaces = clazz.getInterfaces(); 461 if (clazz.isInterface()) { 462 allInterfaces.add(clazz); 463 } else if (souper != null) { 464 queue.enqueue(souper); 465 } 466 for (final JavaClass iface : interfaces) { 467 queue.enqueue(iface); 468 } 469 } 470 return allInterfaces.toArray(JavaClass.EMPTY_ARRAY); 471 } 472 473 /** 474 * @return Annotations on the class 475 * @since 6.0 476 */ 477 public AnnotationEntry[] getAnnotationEntries() { 478 if (annotations == null) { 479 annotations = AnnotationEntry.createAnnotationEntries(getAttributes()); 480 } 481 482 return annotations; 483 } 484 485 /** 486 * @return Attributes of the class. 487 */ 488 public Attribute[] getAttributes() { 489 return attributes; 490 } 491 492 /** 493 * @return class in binary format 494 */ 495 public byte[] getBytes() { 496 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 497 try (DataOutputStream dos = new DataOutputStream(baos)) { 498 dump(dos); 499 } catch (final IOException e) { 500 e.printStackTrace(); 501 } 502 return baos.toByteArray(); 503 } 504 505 /** 506 * @return Class name. 507 */ 508 public String getClassName() { 509 return className; 510 } 511 512 /** 513 * @return Class name index. 514 */ 515 public int getClassNameIndex() { 516 return classNameIndex; 517 } 518 519 /** 520 * @return Constant pool. 521 */ 522 public ConstantPool getConstantPool() { 523 return constantPool; 524 } 525 526 /** 527 * @return Fields, i.e., variables of the class. Like the JVM spec mandates for the classfile format, these fields are 528 * those specific to this class, and not those of the superclass or superinterfaces. 529 */ 530 public Field[] getFields() { 531 return fields; 532 } 533 534 /** 535 * @return File name of class, aka SourceFile attribute value 536 */ 537 public String getFileName() { 538 return fileName; 539 } 540 541 /** 542 * @return Indices in constant pool of implemented interfaces. 543 */ 544 public int[] getInterfaceIndices() { 545 return interfaces; 546 } 547 548 /** 549 * @return Names of implemented interfaces. 550 */ 551 public String[] getInterfaceNames() { 552 return interfaceNames; 553 } 554 555 /** 556 * Gets interfaces directly implemented by this JavaClass. 557 * 558 * @throws ClassNotFoundException if any of the class's interfaces can't be found. 559 */ 560 public JavaClass[] getInterfaces() throws ClassNotFoundException { 561 final String[] interfaces = getInterfaceNames(); 562 final JavaClass[] classes = new JavaClass[interfaces.length]; 563 for (int i = 0; i < interfaces.length; i++) { 564 classes[i] = repository.loadClass(interfaces[i]); 565 } 566 return classes; 567 } 568 569 /** 570 * @return Major number of class file version. 571 */ 572 public int getMajor() { 573 return major; 574 } 575 576 /** 577 * @return A {@link Method} corresponding to java.lang.reflect.Method if any 578 */ 579 public Method getMethod(final java.lang.reflect.Method m) { 580 for (final Method method : methods) { 581 if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) { 582 return method; 583 } 584 } 585 return null; 586 } 587 588 /** 589 * @return Methods of the class. 590 */ 591 public Method[] getMethods() { 592 return methods; 593 } 594 595 /** 596 * @return Minor number of class file version. 597 */ 598 public int getMinor() { 599 return minor; 600 } 601 602 /** 603 * @return Package name. 604 */ 605 public String getPackageName() { 606 return packageName; 607 } 608 609 /** 610 * Gets the ClassRepository which holds its definition. By default this is the same as 611 * SyntheticRepository.getInstance(); 612 */ 613 public org.apache.bcel.util.Repository getRepository() { 614 return repository; 615 } 616 617 /** 618 * @return returns either HEAP (generated), FILE, or ZIP 619 */ 620 public final byte getSource() { 621 return source; 622 } 623 624 /** 625 * @return file name where this class was read from 626 */ 627 public String getSourceFileName() { 628 return sourceFileName; 629 } 630 631 /** 632 * Gets the source file path including the package path. 633 * 634 * @return path to original source file of parsed class, relative to original source directory. 635 * @since 6.7.0 636 */ 637 public String getSourceFilePath() { 638 final StringBuilder outFileName = new StringBuilder(); 639 if (!packageName.isEmpty()) { 640 outFileName.append(Utility.packageToPath(packageName)); 641 outFileName.append('/'); 642 } 643 outFileName.append(sourceFileName); 644 return outFileName.toString(); 645 } 646 647 /** 648 * @return the superclass for this JavaClass object, or null if this is {@link Object} 649 * @throws ClassNotFoundException if the superclass can't be found 650 */ 651 public JavaClass getSuperClass() throws ClassNotFoundException { 652 if ("java.lang.Object".equals(getClassName())) { 653 return null; 654 } 655 return repository.loadClass(getSuperclassName()); 656 } 657 658 /** 659 * @return list of super classes of this class in ascending order, i.e., java.lang.Object is always the last element 660 * @throws ClassNotFoundException if any of the superclasses can't be found 661 */ 662 public JavaClass[] getSuperClasses() throws ClassNotFoundException { 663 JavaClass clazz = this; 664 final List<JavaClass> allSuperClasses = new ArrayList<>(); 665 for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) { 666 allSuperClasses.add(clazz); 667 } 668 return allSuperClasses.toArray(JavaClass.EMPTY_ARRAY); 669 } 670 671 /** 672 * returns the super class name of this class. In the case that this class is {@link Object}, it will return itself 673 * ({@link Object}). This is probably incorrect but isn't fixed at this time to not break existing clients. 674 * 675 * @return Superclass name. 676 */ 677 public String getSuperclassName() { 678 return superclassName; 679 } 680 681 /** 682 * @return Class name index. 683 */ 684 public int getSuperclassNameIndex() { 685 return superclassNameIndex; 686 } 687 688 /** 689 * Return value as defined by given BCELComparator strategy. By default return the hash code of the class name. 690 * 691 * @see Object#hashCode() 692 */ 693 @Override 694 public int hashCode() { 695 return bcelComparator.hashCode(this); 696 } 697 698 /** 699 * @return true, if this class is an implementation of interface inter 700 * @throws ClassNotFoundException if superclasses or superinterfaces of this class can't be found 701 */ 702 public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException { 703 if (!inter.isInterface()) { 704 throw new IllegalArgumentException(inter.getClassName() + " is no interface"); 705 } 706 if (this.equals(inter)) { 707 return true; 708 } 709 final JavaClass[] superInterfaces = getAllInterfaces(); 710 for (final JavaClass superInterface : superInterfaces) { 711 if (superInterface.equals(inter)) { 712 return true; 713 } 714 } 715 return false; 716 } 717 718 /** 719 * Equivalent to runtime "instanceof" operator. 720 * 721 * @return true if this JavaClass is derived from the super class 722 * @throws ClassNotFoundException if superclasses or superinterfaces of this object can't be found 723 */ 724 public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException { 725 if (this.equals(superclass)) { 726 return true; 727 } 728 for (final JavaClass clazz : getSuperClasses()) { 729 if (clazz.equals(superclass)) { 730 return true; 731 } 732 } 733 if (superclass.isInterface()) { 734 return implementationOf(superclass); 735 } 736 return false; 737 } 738 739 /** 740 * @since 6.0 741 */ 742 public final boolean isAnonymous() { 743 computeNestedTypeStatus(); 744 return this.isAnonymous; 745 } 746 747 public final boolean isClass() { 748 return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0; 749 } 750 751 /** 752 * @since 6.0 753 */ 754 public final boolean isNested() { 755 computeNestedTypeStatus(); 756 return this.isNested; 757 } 758 759 /** 760 * Tests whether this class was declared as a record 761 * 762 * @return true if a record attribute is present, false otherwise. 763 * @since 6.9.0 764 */ 765 public boolean isRecord() { 766 computeIsRecord(); 767 return this.isRecord; 768 } 769 770 public final boolean isSuper() { 771 return (super.getAccessFlags() & Const.ACC_SUPER) != 0; 772 } 773 774 /** 775 * @param attributes . 776 */ 777 public void setAttributes(final Attribute[] attributes) { 778 this.attributes = attributes != null ? attributes : Attribute.EMPTY_ARRAY; 779 } 780 781 /** 782 * @param className . 783 */ 784 public void setClassName(final String className) { 785 this.className = className; 786 } 787 788 /** 789 * @param classNameIndex . 790 */ 791 public void setClassNameIndex(final int classNameIndex) { 792 this.classNameIndex = classNameIndex; 793 } 794 795 /** 796 * @param constantPool . 797 */ 798 public void setConstantPool(final ConstantPool constantPool) { 799 this.constantPool = constantPool; 800 } 801 802 /** 803 * @param fields . 804 */ 805 public void setFields(final Field[] fields) { 806 this.fields = fields != null ? fields : Field.EMPTY_ARRAY; 807 } 808 809 /** 810 * Sets File name of class, aka SourceFile attribute value 811 */ 812 public void setFileName(final String fileName) { 813 this.fileName = fileName; 814 } 815 816 /** 817 * @param interfaceNames . 818 */ 819 public void setInterfaceNames(final String[] interfaceNames) { 820 this.interfaceNames = ArrayUtils.nullToEmpty(interfaceNames); 821 } 822 823 /** 824 * @param interfaces . 825 */ 826 public void setInterfaces(final int[] interfaces) { 827 this.interfaces = ArrayUtils.nullToEmpty(interfaces); 828 } 829 830 /** 831 * @param major . 832 */ 833 public void setMajor(final int major) { 834 this.major = major; 835 } 836 837 /** 838 * @param methods . 839 */ 840 public void setMethods(final Method[] methods) { 841 this.methods = methods != null ? methods : Method.EMPTY_ARRAY; 842 } 843 844 /** 845 * @param minor . 846 */ 847 public void setMinor(final int minor) { 848 this.minor = minor; 849 } 850 851 /** 852 * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done. 853 */ 854 public void setRepository(final org.apache.bcel.util.Repository repository) { // TODO make protected? 855 this.repository = repository; 856 } 857 858 /** 859 * Sets absolute path to file this class was read from. 860 */ 861 public void setSourceFileName(final String sourceFileName) { 862 this.sourceFileName = sourceFileName; 863 } 864 865 /** 866 * @param superclassName . 867 */ 868 public void setSuperclassName(final String superclassName) { 869 this.superclassName = superclassName; 870 } 871 872 /** 873 * @param superclassNameIndex . 874 */ 875 public void setSuperclassNameIndex(final int superclassNameIndex) { 876 this.superclassNameIndex = superclassNameIndex; 877 } 878 879 /** 880 * @return String representing class contents. 881 */ 882 @Override 883 public String toString() { 884 String access = Utility.accessToString(super.getAccessFlags(), true); 885 access = access.isEmpty() ? "" : access + " "; 886 final StringBuilder buf = new StringBuilder(128); 887 buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ") 888 .append(Utility.compactClassName(superclassName, false)).append('\n'); 889 final int size = interfaces.length; 890 if (size > 0) { 891 buf.append("implements\t\t"); 892 for (int i = 0; i < size; i++) { 893 buf.append(interfaceNames[i]); 894 if (i < size - 1) { 895 buf.append(", "); 896 } 897 } 898 buf.append('\n'); 899 } 900 buf.append("file name\t\t").append(fileName).append('\n'); 901 buf.append("compiled from\t\t").append(sourceFileName).append('\n'); 902 buf.append("compiler version\t").append(major).append(".").append(minor).append('\n'); 903 buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n'); 904 buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n"); 905 buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n"); 906 if (attributes.length > 0) { 907 buf.append("\nAttribute(s):\n"); 908 for (final Attribute attribute : attributes) { 909 buf.append(indent(attribute)); 910 } 911 } 912 final AnnotationEntry[] annotations = getAnnotationEntries(); 913 if (annotations != null && annotations.length > 0) { 914 buf.append("\nAnnotation(s):\n"); 915 for (final AnnotationEntry annotation : annotations) { 916 buf.append(indent(annotation)); 917 } 918 } 919 if (fields.length > 0) { 920 buf.append("\n").append(fields.length).append(" fields:\n"); 921 for (final Field field : fields) { 922 buf.append("\t").append(field).append('\n'); 923 } 924 } 925 if (methods.length > 0) { 926 buf.append("\n").append(methods.length).append(" methods:\n"); 927 for (final Method method : methods) { 928 buf.append("\t").append(method).append('\n'); 929 } 930 } 931 return buf.toString(); 932 } 933}