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(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 * Gets attribute for given tag. 487 * @return Attribute for given tag, null if not found. 488 * Refer to {@link org.apache.bcel.Const#ATTR_UNKNOWN} constants named ATTR_* for possible values. 489 * @since 6.10.0 490 */ 491 @SuppressWarnings("unchecked") 492 public final <T extends Attribute> T getAttribute(final byte tag) { 493 for (final Attribute attribute : getAttributes()) { 494 if (attribute.getTag() == tag) { 495 return (T) attribute; 496 } 497 } 498 return null; 499 } 500 501 /** 502 * @return Attributes of the class. 503 */ 504 public Attribute[] getAttributes() { 505 return attributes; 506 } 507 508 /** 509 * @return class in binary format 510 */ 511 public byte[] getBytes() { 512 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 513 try (DataOutputStream dos = new DataOutputStream(baos)) { 514 dump(dos); 515 } catch (final IOException e) { 516 e.printStackTrace(); 517 } 518 return baos.toByteArray(); 519 } 520 521 /** 522 * @return Class name. 523 */ 524 public String getClassName() { 525 return className; 526 } 527 528 /** 529 * @return Class name index. 530 */ 531 public int getClassNameIndex() { 532 return classNameIndex; 533 } 534 535 /** 536 * @return Constant pool. 537 */ 538 public ConstantPool getConstantPool() { 539 return constantPool; 540 } 541 542 /** 543 * @return Fields, i.e., variables of the class. Like the JVM spec mandates for the classfile format, these fields are 544 * those specific to this class, and not those of the superclass or superinterfaces. 545 */ 546 public Field[] getFields() { 547 return fields; 548 } 549 550 /** 551 * @return File name of class, aka SourceFile attribute value 552 */ 553 public String getFileName() { 554 return fileName; 555 } 556 557 /** 558 * @return Indices in constant pool of implemented interfaces. 559 */ 560 public int[] getInterfaceIndices() { 561 return interfaces; 562 } 563 564 /** 565 * @return Names of implemented interfaces. 566 */ 567 public String[] getInterfaceNames() { 568 return interfaceNames; 569 } 570 571 /** 572 * Gets interfaces directly implemented by this JavaClass. 573 * 574 * @throws ClassNotFoundException if any of the class's interfaces can't be found. 575 */ 576 public JavaClass[] getInterfaces() throws ClassNotFoundException { 577 final String[] interfaces = getInterfaceNames(); 578 final JavaClass[] classes = new JavaClass[interfaces.length]; 579 for (int i = 0; i < interfaces.length; i++) { 580 classes[i] = repository.loadClass(interfaces[i]); 581 } 582 return classes; 583 } 584 585 /** 586 * @return Major number of class file version. 587 */ 588 public int getMajor() { 589 return major; 590 } 591 592 /** 593 * @return A {@link Method} corresponding to java.lang.reflect.Method if any 594 */ 595 public Method getMethod(final java.lang.reflect.Method m) { 596 for (final Method method : methods) { 597 if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) { 598 return method; 599 } 600 } 601 return null; 602 } 603 604 /** 605 * @return Methods of the class. 606 */ 607 public Method[] getMethods() { 608 return methods; 609 } 610 611 /** 612 * @return Minor number of class file version. 613 */ 614 public int getMinor() { 615 return minor; 616 } 617 618 /** 619 * @return Package name. 620 */ 621 public String getPackageName() { 622 return packageName; 623 } 624 625 /** 626 * Gets the ClassRepository which holds its definition. By default this is the same as 627 * SyntheticRepository.getInstance(); 628 */ 629 public org.apache.bcel.util.Repository getRepository() { 630 return repository; 631 } 632 633 /** 634 * @return returns either HEAP (generated), FILE, or ZIP 635 */ 636 public final byte getSource() { 637 return source; 638 } 639 640 /** 641 * @return file name where this class was read from 642 */ 643 public String getSourceFileName() { 644 return sourceFileName; 645 } 646 647 /** 648 * Gets the source file path including the package path. 649 * 650 * @return path to original source file of parsed class, relative to original source directory. 651 * @since 6.7.0 652 */ 653 public String getSourceFilePath() { 654 final StringBuilder outFileName = new StringBuilder(); 655 if (!packageName.isEmpty()) { 656 outFileName.append(Utility.packageToPath(packageName)); 657 outFileName.append('/'); 658 } 659 outFileName.append(sourceFileName); 660 return outFileName.toString(); 661 } 662 663 /** 664 * @return the superclass for this JavaClass object, or null if this is {@link Object} 665 * @throws ClassNotFoundException if the superclass can't be found 666 */ 667 public JavaClass getSuperClass() throws ClassNotFoundException { 668 if ("java.lang.Object".equals(getClassName())) { 669 return null; 670 } 671 return repository.loadClass(getSuperclassName()); 672 } 673 674 /** 675 * @return list of super classes of this class in ascending order, i.e., java.lang.Object is always the last element 676 * @throws ClassNotFoundException if any of the superclasses can't be found 677 */ 678 public JavaClass[] getSuperClasses() throws ClassNotFoundException { 679 JavaClass clazz = this; 680 final List<JavaClass> allSuperClasses = new ArrayList<>(); 681 for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) { 682 allSuperClasses.add(clazz); 683 } 684 return allSuperClasses.toArray(EMPTY_ARRAY); 685 } 686 687 /** 688 * returns the super class name of this class. In the case that this class is {@link Object}, it will return itself 689 * ({@link Object}). This is probably incorrect but isn't fixed at this time to not break existing clients. 690 * 691 * @return Superclass name. 692 */ 693 public String getSuperclassName() { 694 return superclassName; 695 } 696 697 /** 698 * @return Class name index. 699 */ 700 public int getSuperclassNameIndex() { 701 return superclassNameIndex; 702 } 703 704 /** 705 * Return value as defined by given BCELComparator strategy. By default return the hash code of the class name. 706 * 707 * @see Object#hashCode() 708 */ 709 @Override 710 public int hashCode() { 711 return bcelComparator.hashCode(this); 712 } 713 714 /** 715 * @return true, if this class is an implementation of interface inter 716 * @throws ClassNotFoundException if superclasses or superinterfaces of this class can't be found 717 */ 718 public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException { 719 if (!inter.isInterface()) { 720 throw new IllegalArgumentException(inter.getClassName() + " is no interface"); 721 } 722 if (equals(inter)) { 723 return true; 724 } 725 final JavaClass[] superInterfaces = getAllInterfaces(); 726 for (final JavaClass superInterface : superInterfaces) { 727 if (superInterface.equals(inter)) { 728 return true; 729 } 730 } 731 return false; 732 } 733 734 /** 735 * Equivalent to runtime "instanceof" operator. 736 * 737 * @return true if this JavaClass is derived from the super class 738 * @throws ClassNotFoundException if superclasses or superinterfaces of this object can't be found 739 */ 740 public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException { 741 if (equals(superclass)) { 742 return true; 743 } 744 for (final JavaClass clazz : getSuperClasses()) { 745 if (clazz.equals(superclass)) { 746 return true; 747 } 748 } 749 if (superclass.isInterface()) { 750 return implementationOf(superclass); 751 } 752 return false; 753 } 754 755 /** 756 * @since 6.0 757 */ 758 public final boolean isAnonymous() { 759 computeNestedTypeStatus(); 760 return this.isAnonymous; 761 } 762 763 public final boolean isClass() { 764 return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0; 765 } 766 767 /** 768 * @since 6.0 769 */ 770 public final boolean isNested() { 771 computeNestedTypeStatus(); 772 return this.isNested; 773 } 774 775 /** 776 * Tests whether this class was declared as a record 777 * 778 * @return true if a record attribute is present, false otherwise. 779 * @since 6.9.0 780 */ 781 public boolean isRecord() { 782 computeIsRecord(); 783 return this.isRecord; 784 } 785 786 public final boolean isSuper() { 787 return (super.getAccessFlags() & Const.ACC_SUPER) != 0; 788 } 789 790 /** 791 * @param attributes . 792 */ 793 public void setAttributes(final Attribute[] attributes) { 794 this.attributes = attributes != null ? attributes : Attribute.EMPTY_ARRAY; 795 } 796 797 /** 798 * @param className . 799 */ 800 public void setClassName(final String className) { 801 this.className = className; 802 } 803 804 /** 805 * @param classNameIndex . 806 */ 807 public void setClassNameIndex(final int classNameIndex) { 808 this.classNameIndex = classNameIndex; 809 } 810 811 /** 812 * @param constantPool . 813 */ 814 public void setConstantPool(final ConstantPool constantPool) { 815 this.constantPool = constantPool; 816 } 817 818 /** 819 * @param fields . 820 */ 821 public void setFields(final Field[] fields) { 822 this.fields = fields != null ? fields : Field.EMPTY_ARRAY; 823 } 824 825 /** 826 * Sets File name of class, aka SourceFile attribute value 827 */ 828 public void setFileName(final String fileName) { 829 this.fileName = fileName; 830 } 831 832 /** 833 * @param interfaceNames . 834 */ 835 public void setInterfaceNames(final String[] interfaceNames) { 836 this.interfaceNames = ArrayUtils.nullToEmpty(interfaceNames); 837 } 838 839 /** 840 * @param interfaces . 841 */ 842 public void setInterfaces(final int[] interfaces) { 843 this.interfaces = ArrayUtils.nullToEmpty(interfaces); 844 } 845 846 /** 847 * @param major . 848 */ 849 public void setMajor(final int major) { 850 this.major = major; 851 } 852 853 /** 854 * @param methods . 855 */ 856 public void setMethods(final Method[] methods) { 857 this.methods = methods != null ? methods : Method.EMPTY_ARRAY; 858 } 859 860 /** 861 * @param minor . 862 */ 863 public void setMinor(final int minor) { 864 this.minor = minor; 865 } 866 867 /** 868 * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done. 869 */ 870 public void setRepository(final org.apache.bcel.util.Repository repository) { // TODO make protected? 871 this.repository = repository; 872 } 873 874 /** 875 * Sets absolute path to file this class was read from. 876 */ 877 public void setSourceFileName(final String sourceFileName) { 878 this.sourceFileName = sourceFileName; 879 } 880 881 /** 882 * @param superclassName . 883 */ 884 public void setSuperclassName(final String superclassName) { 885 this.superclassName = superclassName; 886 } 887 888 /** 889 * @param superclassNameIndex . 890 */ 891 public void setSuperclassNameIndex(final int superclassNameIndex) { 892 this.superclassNameIndex = superclassNameIndex; 893 } 894 895 /** 896 * @return String representing class contents. 897 */ 898 @Override 899 public String toString() { 900 String access = Utility.accessToString(super.getAccessFlags(), true); 901 access = access.isEmpty() ? "" : access + " "; 902 final StringBuilder buf = new StringBuilder(128); 903 buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ") 904 .append(Utility.compactClassName(superclassName, false)).append('\n'); 905 final int size = interfaces.length; 906 if (size > 0) { 907 buf.append("implements\t\t"); 908 for (int i = 0; i < size; i++) { 909 buf.append(interfaceNames[i]); 910 if (i < size - 1) { 911 buf.append(", "); 912 } 913 } 914 buf.append('\n'); 915 } 916 buf.append("file name\t\t").append(fileName).append('\n'); 917 buf.append("compiled from\t\t").append(sourceFileName).append('\n'); 918 buf.append("compiler version\t").append(major).append(".").append(minor).append('\n'); 919 buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n'); 920 buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n"); 921 buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n"); 922 if (attributes.length > 0) { 923 buf.append("\nAttribute(s):\n"); 924 for (final Attribute attribute : attributes) { 925 buf.append(indent(attribute)); 926 } 927 } 928 final AnnotationEntry[] annotations = getAnnotationEntries(); 929 if (annotations != null && annotations.length > 0) { 930 buf.append("\nAnnotation(s):\n"); 931 for (final AnnotationEntry annotation : annotations) { 932 buf.append(indent(annotation)); 933 } 934 } 935 if (fields.length > 0) { 936 buf.append("\n").append(fields.length).append(" fields:\n"); 937 for (final Field field : fields) { 938 buf.append("\t").append(field).append('\n'); 939 } 940 } 941 if (methods.length > 0) { 942 buf.append("\n").append(methods.length).append(" methods:\n"); 943 for (final Method method : methods) { 944 buf.append("\t").append(method).append('\n'); 945 } 946 } 947 return buf.toString(); 948 } 949}