View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.bcel.classfile;
18  
19  import java.io.ByteArrayOutputStream;
20  import java.io.DataOutputStream;
21  import java.io.File;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.OutputStream;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.List;
28  import java.util.Objects;
29  import java.util.Set;
30  import java.util.StringTokenizer;
31  import java.util.TreeSet;
32  
33  import org.apache.bcel.Const;
34  import org.apache.bcel.generic.Type;
35  import org.apache.bcel.util.BCELComparator;
36  import org.apache.bcel.util.ClassQueue;
37  import org.apache.bcel.util.SyntheticRepository;
38  import org.apache.commons.lang3.ArrayUtils;
39  
40  /**
41   * Represents a Java class, i.e., the data structures, constant pool, fields, methods and commands contained in a Java
42   * .class file. See <a href="https://docs.oracle.com/javase/specs/">JVM specification</a> for details. The intent of
43   * this class is to represent a parsed or otherwise existing class file. Those interested in programmatically generating
44   * classes should see the <a href="../generic/ClassGen.html">ClassGen</a> class.
45   *
46   * @see org.apache.bcel.generic.ClassGen
47   */
48  public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> {
49  
50      /**
51       * The standard class file extension.
52       *
53       * @since 6.7.0
54       */
55      public static final String EXTENSION = ".class";
56  
57      /**
58       * Empty array.
59       *
60       * @since 6.6.0
61       */
62      public static final JavaClass[] EMPTY_ARRAY = {};
63  
64      public static final byte HEAP = 1;
65      public static final byte FILE = 2;
66      public static final byte ZIP = 3;
67      private static final boolean debug = Boolean.getBoolean("JavaClass.debug"); // Debugging on/off
68  
69      private static BCELComparator<JavaClass> bcelComparator = new BCELComparator<JavaClass>() {
70  
71          @Override
72          public boolean equals(final JavaClass a, final JavaClass b) {
73              return a == b || a != null && b != null && Objects.equals(a.getClassName(), b.getClassName());
74          }
75  
76          @Override
77          public int hashCode(final JavaClass o) {
78              return o != null ? Objects.hashCode(o.getClassName()) : 0;
79          }
80      };
81  
82      /*
83       * Print debug information depending on 'JavaClass.debug'
84       */
85      static void Debug(final String str) {
86          if (debug) {
87              System.out.println(str);
88          }
89      }
90  
91      /**
92       * @return Comparison strategy object.
93       */
94      public static BCELComparator<JavaClass> getComparator() {
95          return bcelComparator;
96      }
97  
98      private static String indent(final Object obj) {
99          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 }