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