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