001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.bcel.classfile; 020 021import java.io.ByteArrayOutputStream; 022import java.io.DataOutputStream; 023import java.io.File; 024import java.io.FileOutputStream; 025import java.io.IOException; 026import java.io.OutputStream; 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.List; 030import java.util.Objects; 031import java.util.Set; 032import java.util.StringTokenizer; 033import java.util.TreeSet; 034 035import org.apache.bcel.Const; 036import org.apache.bcel.generic.Type; 037import org.apache.bcel.util.BCELComparator; 038import org.apache.bcel.util.ClassQueue; 039import org.apache.bcel.util.SyntheticRepository; 040import org.apache.commons.lang3.ArrayUtils; 041 042/** 043 * Represents a Java class, i.e., the data structures, constant pool, fields, methods and commands contained in a Java 044 * .class file. See <a href="https://docs.oracle.com/javase/specs/">JVM specification</a> for details. The intent of 045 * this class is to represent a parsed or otherwise existing class file. Those interested in programmatically generating 046 * classes should see the <a href="../generic/ClassGen.html">ClassGen</a> class. 047 * 048 * @see org.apache.bcel.generic.ClassGen 049 */ 050public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> { 051 052 /** 053 * The standard class file extension. 054 * 055 * @since 6.7.0 056 */ 057 public static final String EXTENSION = ".class"; 058 059 /** 060 * Empty array. 061 * 062 * @since 6.6.0 063 */ 064 public static final JavaClass[] EMPTY_ARRAY = {}; 065 066 public static final byte HEAP = 1; 067 public static final byte FILE = 2; 068 public static final byte ZIP = 3; 069 private static final boolean debug = Boolean.getBoolean("JavaClass.debug"); // Debugging on/off 070 071 private static BCELComparator<JavaClass> bcelComparator = new BCELComparator<JavaClass>() { 072 073 @Override 074 public boolean equals(final JavaClass a, final JavaClass b) { 075 return a == b || a != null && b != null && Objects.equals(a.getClassName(), b.getClassName()); 076 } 077 078 @Override 079 public int hashCode(final JavaClass o) { 080 return o != null ? Objects.hashCode(o.getClassName()) : 0; 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<JavaClass> 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<JavaClass> 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 private boolean isRecord; 139 140 private boolean computedNestedTypeStatus; 141 private boolean computedRecord; 142 143 /** 144 * In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any 145 * better. 146 */ 147 private transient org.apache.bcel.util.Repository repository = SyntheticRepository.getInstance(); 148 149 /** 150 * Constructor gets all contents as arguments. 151 * 152 * @param classNameIndex Class name 153 * @param superclassNameIndex Superclass name 154 * @param fileName File name 155 * @param major Major compiler version 156 * @param minor Minor compiler version 157 * @param accessFlags Access rights defined by bit flags 158 * @param constantPool Array of constants 159 * @param interfaces Implemented interfaces 160 * @param fields Class fields 161 * @param methods Class methods 162 * @param attributes Class attributes 163 */ 164 public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags, 165 final ConstantPool constantPool, final int[] interfaces, final Field[] fields, final Method[] methods, final Attribute[] attributes) { 166 this(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes, HEAP); 167 } 168 169 /** 170 * Constructor gets all contents as arguments. 171 * 172 * @param classNameIndex Index into constant pool referencing a ConstantClass that represents this class. 173 * @param superclassNameIndex Index into constant pool referencing a ConstantClass that represents this class's 174 * superclass. 175 * @param fileName File name 176 * @param major Major compiler version 177 * @param minor Minor compiler version 178 * @param accessFlags Access rights defined by bit flags 179 * @param constantPool Array of constants 180 * @param interfaces Implemented interfaces 181 * @param fields Class fields 182 * @param methods Class methods 183 * @param attributes Class attributes 184 * @param source Read from file or generated in memory? 185 */ 186 public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags, 187 final ConstantPool constantPool, int[] interfaces, Field[] fields, Method[] methods, Attribute[] attributes, final byte source) { 188 super(accessFlags); 189 interfaces = ArrayUtils.nullToEmpty(interfaces); 190 if (attributes == null) { 191 attributes = Attribute.EMPTY_ARRAY; 192 } 193 if (fields == null) { 194 fields = Field.EMPTY_ARRAY; 195 } 196 if (methods == null) { 197 methods = Method.EMPTY_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 computeIsRecord() { 265 if (computedRecord) { 266 return; 267 } 268 for (final Attribute attribute : this.attributes) { 269 if (attribute instanceof Record) { 270 isRecord = true; 271 break; 272 } 273 } 274 this.computedRecord = true; 275 } 276 277 private void computeNestedTypeStatus() { 278 if (computedNestedTypeStatus) { 279 return; 280 } 281 for (final Attribute attribute : this.attributes) { 282 if (attribute instanceof InnerClasses) { 283 ((InnerClasses) attribute).forEach(innerClass -> { 284 boolean innerClassAttributeRefersToMe = false; 285 String innerClassName = constantPool.getConstantString(innerClass.getInnerClassIndex(), Const.CONSTANT_Class); 286 innerClassName = Utility.compactClassName(innerClassName, false); 287 if (innerClassName.equals(getClassName())) { 288 innerClassAttributeRefersToMe = true; 289 } 290 if (innerClassAttributeRefersToMe) { 291 this.isNested = true; 292 if (innerClass.getInnerNameIndex() == 0) { 293 this.isAnonymous = true; 294 } 295 } 296 }); 297 } 298 } 299 this.computedNestedTypeStatus = true; 300 } 301 302 /** 303 * @return deep copy of this class 304 */ 305 public JavaClass copy() { 306 try { 307 final JavaClass c = (JavaClass) clone(); 308 c.constantPool = constantPool.copy(); 309 c.interfaces = interfaces.clone(); 310 c.interfaceNames = interfaceNames.clone(); 311 c.fields = new Field[fields.length]; 312 Arrays.setAll(c.fields, i -> fields[i].copy(c.constantPool)); 313 c.methods = new Method[methods.length]; 314 Arrays.setAll(c.methods, i -> methods[i].copy(c.constantPool)); 315 c.attributes = new Attribute[attributes.length]; 316 Arrays.setAll(c.attributes, i -> attributes[i].copy(c.constantPool)); 317 return c; 318 } catch (final CloneNotSupportedException e) { 319 return null; 320 } 321 } 322 323 /** 324 * Dump Java class to output stream in binary format. 325 * 326 * @param file Output stream 327 * @throws IOException if an I/O error occurs. 328 */ 329 public void dump(final DataOutputStream file) throws IOException { 330 file.writeInt(Const.JVM_CLASSFILE_MAGIC); 331 file.writeShort(minor); 332 file.writeShort(major); 333 constantPool.dump(file); 334 file.writeShort(super.getAccessFlags()); 335 file.writeShort(classNameIndex); 336 file.writeShort(superclassNameIndex); 337 file.writeShort(interfaces.length); 338 for (final int interface1 : interfaces) { 339 file.writeShort(interface1); 340 } 341 file.writeShort(fields.length); 342 for (final Field field : fields) { 343 field.dump(file); 344 } 345 file.writeShort(methods.length); 346 for (final Method method : methods) { 347 method.dump(file); 348 } 349 if (attributes != null) { 350 file.writeShort(attributes.length); 351 for (final Attribute attribute : attributes) { 352 attribute.dump(file); 353 } 354 } else { 355 file.writeShort(0); 356 } 357 file.flush(); 358 } 359 360 /** 361 * Dump class to a file. 362 * 363 * @param file Output file 364 * @throws IOException if an I/O error occurs. 365 */ 366 public void dump(final File file) throws IOException { 367 final String parent = file.getParent(); 368 if (parent != null) { 369 final File dir = new File(parent); 370 if (!dir.mkdirs() && !dir.isDirectory()) { 371 throw new IOException("Could not create the directory " + dir); 372 } 373 } 374 try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) { 375 dump(dos); 376 } 377 } 378 379 /** 380 * Dump Java class to output stream in binary format. 381 * 382 * @param file Output stream 383 * @throws IOException if an I/O error occurs. 384 */ 385 public void dump(final OutputStream file) throws IOException { 386 dump(new DataOutputStream(file)); 387 } 388 389 /** 390 * Dump class to a file named fileName. 391 * 392 * @param fileName Output file name 393 * @throws IOException if an I/O error occurs. 394 */ 395 public void dump(final String fileName) throws IOException { 396 dump(new File(fileName)); 397 } 398 399 /** 400 * Return value as defined by given BCELComparator strategy. By default two JavaClass objects are said to be equal when 401 * their class names are equal. 402 * 403 * @see Object#equals(Object) 404 */ 405 @Override 406 public boolean equals(final Object obj) { 407 return obj instanceof JavaClass && bcelComparator.equals(this, (JavaClass) obj); 408 } 409 410 /** 411 * Finds a visible field by name and type in this class and its super classes. 412 * @param fieldName the field name to find 413 * @param fieldType the field type to find 414 * @return field matching given name and type, null if field is not found or not accessible from this class. 415 * @throws ClassNotFoundException 416 * @since 6.8.0 417 */ 418 public Field findField(final String fieldName, final Type fieldType) throws ClassNotFoundException { 419 for (final Field field : fields) { 420 if (field.getName().equals(fieldName)) { 421 final Type fType = Type.getType(field.getSignature()); 422 /* 423 * TODO: Check if assignment compatibility is sufficient. What does Sun do? 424 */ 425 if (fType.equals(fieldType)) { 426 return field; 427 } 428 } 429 } 430 431 final JavaClass superclass = getSuperClass(); 432 if (superclass != null && !"java.lang.Object".equals(superclass.getClassName())) { 433 final Field f = superclass.findField(fieldName, fieldType); 434 if (f != null && (f.isPublic() || f.isProtected() || !f.isPrivate() && packageName.equals(superclass.getPackageName()))) { 435 return f; 436 } 437 } 438 final JavaClass[] implementedInterfaces = getInterfaces(); 439 if (implementedInterfaces != null) { 440 for (final JavaClass implementedInterface : implementedInterfaces) { 441 final Field f = implementedInterface.findField(fieldName, fieldType); 442 if (f != null) { 443 return f; 444 } 445 } 446 } 447 return null; 448 } 449 450 /** 451 * Gets all interfaces implemented by this JavaClass (transitively). 452 * 453 * @throws ClassNotFoundException if any of the class's superclasses or interfaces can't be found. 454 */ 455 public JavaClass[] getAllInterfaces() throws ClassNotFoundException { 456 final ClassQueue queue = new ClassQueue(); 457 final Set<JavaClass> allInterfaces = new TreeSet<>(); 458 queue.enqueue(this); 459 while (!queue.empty()) { 460 final JavaClass clazz = queue.dequeue(); 461 final JavaClass souper = clazz.getSuperClass(); 462 final JavaClass[] interfaces = clazz.getInterfaces(); 463 if (clazz.isInterface()) { 464 allInterfaces.add(clazz); 465 } else if (souper != null) { 466 queue.enqueue(souper); 467 } 468 for (final JavaClass iface : interfaces) { 469 queue.enqueue(iface); 470 } 471 } 472 return allInterfaces.toArray(EMPTY_ARRAY); 473 } 474 475 /** 476 * @return Annotations on the class 477 * @since 6.0 478 */ 479 public AnnotationEntry[] getAnnotationEntries() { 480 if (annotations == null) { 481 annotations = AnnotationEntry.createAnnotationEntries(getAttributes()); 482 } 483 484 return annotations; 485 } 486 487 /** 488 * Gets attribute for given tag. 489 * @return Attribute for given tag, null if not found. 490 * Refer to {@link org.apache.bcel.Const#ATTR_UNKNOWN} constants named ATTR_* for possible values. 491 * @since 6.10.0 492 */ 493 @SuppressWarnings("unchecked") 494 public final <T extends Attribute> T getAttribute(final byte tag) { 495 for (final Attribute attribute : getAttributes()) { 496 if (attribute.getTag() == tag) { 497 return (T) attribute; 498 } 499 } 500 return null; 501 } 502 503 /** 504 * @return Attributes of the class. 505 */ 506 public Attribute[] getAttributes() { 507 return attributes; 508 } 509 510 /** 511 * @return class in binary format 512 */ 513 public byte[] getBytes() { 514 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 515 try (DataOutputStream dos = new DataOutputStream(baos)) { 516 dump(dos); 517 } catch (final IOException e) { 518 e.printStackTrace(); 519 } 520 return baos.toByteArray(); 521 } 522 523 /** 524 * @return Class name. 525 */ 526 public String getClassName() { 527 return className; 528 } 529 530 /** 531 * @return Class name index. 532 */ 533 public int getClassNameIndex() { 534 return classNameIndex; 535 } 536 537 /** 538 * @return Constant pool. 539 */ 540 public ConstantPool getConstantPool() { 541 return constantPool; 542 } 543 544 /** 545 * @return Fields, i.e., variables of the class. Like the JVM spec mandates for the classfile format, these fields are 546 * those specific to this class, and not those of the superclass or superinterfaces. 547 */ 548 public Field[] getFields() { 549 return fields; 550 } 551 552 /** 553 * @return File name of class, aka SourceFile attribute value 554 */ 555 public String getFileName() { 556 return fileName; 557 } 558 559 /** 560 * @return Indices in constant pool of implemented interfaces. 561 */ 562 public int[] getInterfaceIndices() { 563 return interfaces; 564 } 565 566 /** 567 * @return Names of implemented interfaces. 568 */ 569 public String[] getInterfaceNames() { 570 return interfaceNames; 571 } 572 573 /** 574 * Gets interfaces directly implemented by this JavaClass. 575 * 576 * @throws ClassNotFoundException if any of the class's interfaces can't be found. 577 */ 578 public JavaClass[] getInterfaces() throws ClassNotFoundException { 579 final String[] interfaces = getInterfaceNames(); 580 final JavaClass[] classes = new JavaClass[interfaces.length]; 581 for (int i = 0; i < interfaces.length; i++) { 582 classes[i] = repository.loadClass(interfaces[i]); 583 } 584 return classes; 585 } 586 587 /** 588 * @return Major number of class file version. 589 */ 590 public int getMajor() { 591 return major; 592 } 593 594 /** 595 * @return A {@link Method} corresponding to java.lang.reflect.Method if any 596 */ 597 public Method getMethod(final java.lang.reflect.Method m) { 598 for (final Method method : methods) { 599 if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) { 600 return method; 601 } 602 } 603 return null; 604 } 605 606 /** 607 * @return Methods of the class. 608 */ 609 public Method[] getMethods() { 610 return methods; 611 } 612 613 /** 614 * @return Minor number of class file version. 615 */ 616 public int getMinor() { 617 return minor; 618 } 619 620 /** 621 * @return Package name. 622 */ 623 public String getPackageName() { 624 return packageName; 625 } 626 627 /** 628 * Gets the ClassRepository which holds its definition. By default this is the same as 629 * SyntheticRepository.getInstance(); 630 */ 631 public org.apache.bcel.util.Repository getRepository() { 632 return repository; 633 } 634 635 /** 636 * @return either HEAP (generated), FILE, or ZIP 637 */ 638 public final byte getSource() { 639 return source; 640 } 641 642 /** 643 * @return file name where this class was read from 644 */ 645 public String getSourceFileName() { 646 return sourceFileName; 647 } 648 649 /** 650 * Gets the source file path including the package path. 651 * 652 * @return path to original source file of parsed class, relative to original source directory. 653 * @since 6.7.0 654 */ 655 public String getSourceFilePath() { 656 final StringBuilder outFileName = new StringBuilder(); 657 if (!packageName.isEmpty()) { 658 outFileName.append(Utility.packageToPath(packageName)); 659 outFileName.append('/'); 660 } 661 outFileName.append(sourceFileName); 662 return outFileName.toString(); 663 } 664 665 /** 666 * @return the superclass for this JavaClass object, or null if this is {@link Object} 667 * @throws ClassNotFoundException if the superclass can't be found 668 */ 669 public JavaClass getSuperClass() throws ClassNotFoundException { 670 if ("java.lang.Object".equals(getClassName())) { 671 return null; 672 } 673 return repository.loadClass(getSuperclassName()); 674 } 675 676 /** 677 * @return list of super classes of this class in ascending order, i.e., {@link Object} is always the last element. 678 * @throws ClassNotFoundException if any of the superclasses can't be found 679 */ 680 public JavaClass[] getSuperClasses() throws ClassNotFoundException { 681 JavaClass clazz = this; 682 final List<JavaClass> allSuperClasses = new ArrayList<>(); 683 for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) { 684 allSuperClasses.add(clazz); 685 } 686 return allSuperClasses.toArray(EMPTY_ARRAY); 687 } 688 689 /** 690 * returns the super class name of this class. In the case that this class is {@link Object}, it will return itself 691 * ({@link Object}). This is probably incorrect but isn't fixed at this time to not break existing clients. 692 * 693 * @return Superclass name. 694 */ 695 public String getSuperclassName() { 696 return superclassName; 697 } 698 699 /** 700 * @return Class name index. 701 */ 702 public int getSuperclassNameIndex() { 703 return superclassNameIndex; 704 } 705 706 /** 707 * Return value as defined by given BCELComparator strategy. By default return the hash code of the class name. 708 * 709 * @see Object#hashCode() 710 */ 711 @Override 712 public int hashCode() { 713 return bcelComparator.hashCode(this); 714 } 715 716 /** 717 * @return true, if this class is an implementation of interface inter 718 * @throws ClassNotFoundException if superclasses or superinterfaces of this class can't be found 719 */ 720 public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException { 721 if (!inter.isInterface()) { 722 throw new IllegalArgumentException(inter.getClassName() + " is no interface"); 723 } 724 if (equals(inter)) { 725 return true; 726 } 727 final JavaClass[] superInterfaces = getAllInterfaces(); 728 for (final JavaClass superInterface : superInterfaces) { 729 if (superInterface.equals(inter)) { 730 return true; 731 } 732 } 733 return false; 734 } 735 736 /** 737 * Equivalent to runtime "instanceof" operator. 738 * 739 * @return true if this JavaClass is derived from the super class 740 * @throws ClassNotFoundException if superclasses or superinterfaces of this object can't be found 741 */ 742 public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException { 743 if (equals(superclass)) { 744 return true; 745 } 746 for (final JavaClass clazz : getSuperClasses()) { 747 if (clazz.equals(superclass)) { 748 return true; 749 } 750 } 751 if (superclass.isInterface()) { 752 return implementationOf(superclass); 753 } 754 return false; 755 } 756 757 /** 758 * @since 6.0 759 */ 760 public final boolean isAnonymous() { 761 computeNestedTypeStatus(); 762 return this.isAnonymous; 763 } 764 765 public final boolean isClass() { 766 return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0; 767 } 768 769 /** 770 * @since 6.0 771 */ 772 public final boolean isNested() { 773 computeNestedTypeStatus(); 774 return this.isNested; 775 } 776 777 /** 778 * Tests whether this class was declared as a record 779 * 780 * @return true if a record attribute is present, false otherwise. 781 * @since 6.9.0 782 */ 783 public boolean isRecord() { 784 computeIsRecord(); 785 return this.isRecord; 786 } 787 788 public final boolean isSuper() { 789 return (super.getAccessFlags() & Const.ACC_SUPER) != 0; 790 } 791 792 /** 793 * @param attributes . 794 */ 795 public void setAttributes(final Attribute[] attributes) { 796 this.attributes = attributes != null ? attributes : Attribute.EMPTY_ARRAY; 797 } 798 799 /** 800 * @param className . 801 */ 802 public void setClassName(final String className) { 803 this.className = className; 804 } 805 806 /** 807 * @param classNameIndex . 808 */ 809 public void setClassNameIndex(final int classNameIndex) { 810 this.classNameIndex = classNameIndex; 811 } 812 813 /** 814 * @param constantPool . 815 */ 816 public void setConstantPool(final ConstantPool constantPool) { 817 this.constantPool = constantPool; 818 } 819 820 /** 821 * @param fields . 822 */ 823 public void setFields(final Field[] fields) { 824 this.fields = fields != null ? fields : Field.EMPTY_ARRAY; 825 } 826 827 /** 828 * Sets File name of class, aka SourceFile attribute value 829 */ 830 public void setFileName(final String fileName) { 831 this.fileName = fileName; 832 } 833 834 /** 835 * @param interfaceNames . 836 */ 837 public void setInterfaceNames(final String[] interfaceNames) { 838 this.interfaceNames = ArrayUtils.nullToEmpty(interfaceNames); 839 } 840 841 /** 842 * @param interfaces . 843 */ 844 public void setInterfaces(final int[] interfaces) { 845 this.interfaces = ArrayUtils.nullToEmpty(interfaces); 846 } 847 848 /** 849 * @param major . 850 */ 851 public void setMajor(final int major) { 852 this.major = major; 853 } 854 855 /** 856 * @param methods . 857 */ 858 public void setMethods(final Method[] methods) { 859 this.methods = methods != null ? methods : Method.EMPTY_ARRAY; 860 } 861 862 /** 863 * @param minor . 864 */ 865 public void setMinor(final int minor) { 866 this.minor = minor; 867 } 868 869 /** 870 * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done. 871 */ 872 public void setRepository(final org.apache.bcel.util.Repository repository) { // TODO make protected? 873 this.repository = repository; 874 } 875 876 /** 877 * Sets absolute path to file this class was read from. 878 */ 879 public void setSourceFileName(final String sourceFileName) { 880 this.sourceFileName = sourceFileName; 881 } 882 883 /** 884 * @param superclassName . 885 */ 886 public void setSuperclassName(final String superclassName) { 887 this.superclassName = superclassName; 888 } 889 890 /** 891 * @param superclassNameIndex . 892 */ 893 public void setSuperclassNameIndex(final int superclassNameIndex) { 894 this.superclassNameIndex = superclassNameIndex; 895 } 896 897 /** 898 * @return String representing class contents. 899 */ 900 @Override 901 public String toString() { 902 String access = Utility.accessToString(super.getAccessFlags(), true); 903 access = access.isEmpty() ? "" : access + " "; 904 final StringBuilder buf = new StringBuilder(128); 905 buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ") 906 .append(Utility.compactClassName(superclassName, false)).append('\n'); 907 final int size = interfaces.length; 908 if (size > 0) { 909 buf.append("implements\t\t"); 910 for (int i = 0; i < size; i++) { 911 buf.append(interfaceNames[i]); 912 if (i < size - 1) { 913 buf.append(", "); 914 } 915 } 916 buf.append('\n'); 917 } 918 buf.append("file name\t\t").append(fileName).append('\n'); 919 buf.append("compiled from\t\t").append(sourceFileName).append('\n'); 920 buf.append("compiler version\t").append(major).append(".").append(minor).append('\n'); 921 buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n'); 922 buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n"); 923 buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n"); 924 if (attributes.length > 0) { 925 buf.append("\nAttribute(s):\n"); 926 for (final Attribute attribute : attributes) { 927 buf.append(indent(attribute)); 928 } 929 } 930 final AnnotationEntry[] annotations = getAnnotationEntries(); 931 if (annotations != null && annotations.length > 0) { 932 buf.append("\nAnnotation(s):\n"); 933 for (final AnnotationEntry annotation : annotations) { 934 buf.append(indent(annotation)); 935 } 936 } 937 if (fields.length > 0) { 938 buf.append("\n").append(fields.length).append(" fields:\n"); 939 for (final Field field : fields) { 940 buf.append("\t").append(field).append('\n'); 941 } 942 } 943 if (methods.length > 0) { 944 buf.append("\n").append(methods.length).append(" methods:\n"); 945 for (final Method method : methods) { 946 buf.append("\t").append(method).append('\n'); 947 } 948 } 949 return buf.toString(); 950 } 951}