JavaClass.java

  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. import java.io.ByteArrayOutputStream;
  19. import java.io.DataOutputStream;
  20. import java.io.File;
  21. import java.io.FileOutputStream;
  22. import java.io.IOException;
  23. import java.io.OutputStream;
  24. import java.util.ArrayList;
  25. import java.util.Arrays;
  26. import java.util.List;
  27. import java.util.Objects;
  28. import java.util.Set;
  29. import java.util.StringTokenizer;
  30. import java.util.TreeSet;

  31. import org.apache.bcel.Const;
  32. import org.apache.bcel.generic.Type;
  33. import org.apache.bcel.util.BCELComparator;
  34. import org.apache.bcel.util.ClassQueue;
  35. import org.apache.bcel.util.SyntheticRepository;
  36. import org.apache.commons.lang3.ArrayUtils;

  37. /**
  38.  * Represents a Java class, i.e., the data structures, constant pool, fields, methods and commands contained in a Java
  39.  * .class file. See <a href="https://docs.oracle.com/javase/specs/">JVM specification</a> for details. The intent of
  40.  * this class is to represent a parsed or otherwise existing class file. Those interested in programmatically generating
  41.  * classes should see the <a href="../generic/ClassGen.html">ClassGen</a> class.
  42.  *
  43.  * @see org.apache.bcel.generic.ClassGen
  44.  */
  45. public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> {

  46.     /**
  47.      * The standard class file extension.
  48.      *
  49.      * @since 6.7.0
  50.      */
  51.     public static final String EXTENSION = ".class";

  52.     /**
  53.      * Empty array.
  54.      *
  55.      * @since 6.6.0
  56.      */
  57.     public static final JavaClass[] EMPTY_ARRAY = {};

  58.     public static final byte HEAP = 1;
  59.     public static final byte FILE = 2;
  60.     public static final byte ZIP = 3;
  61.     private static final boolean debug = Boolean.getBoolean("JavaClass.debug"); // Debugging on/off

  62.     private static BCELComparator<JavaClass> bcelComparator = new BCELComparator<JavaClass>() {

  63.         @Override
  64.         public boolean equals(final JavaClass a, final JavaClass b) {
  65.             return a == b || a != null && b != null && Objects.equals(a.getClassName(), b.getClassName());
  66.         }

  67.         @Override
  68.         public int hashCode(final JavaClass o) {
  69.             return o != null ? Objects.hashCode(o.getClassName()) : 0;
  70.         }
  71.     };

  72.     /*
  73.      * Print debug information depending on 'JavaClass.debug'
  74.      */
  75.     static void Debug(final String str) {
  76.         if (debug) {
  77.             System.out.println(str);
  78.         }
  79.     }

  80.     /**
  81.      * @return Comparison strategy object.
  82.      */
  83.     public static BCELComparator<JavaClass> getComparator() {
  84.         return bcelComparator;
  85.     }

  86.     private static String indent(final Object obj) {
  87.         final StringTokenizer tokenizer = new StringTokenizer(obj.toString(), "\n");
  88.         final StringBuilder buf = new StringBuilder();
  89.         while (tokenizer.hasMoreTokens()) {
  90.             buf.append("\t").append(tokenizer.nextToken()).append("\n");
  91.         }
  92.         return buf.toString();
  93.     }

  94.     /**
  95.      * @param comparator Comparison strategy object.
  96.      */
  97.     public static void setComparator(final BCELComparator<JavaClass> comparator) {
  98.         bcelComparator = comparator;
  99.     }

  100.     private String fileName;
  101.     private final String packageName;
  102.     private String sourceFileName = "<Unknown>";
  103.     private int classNameIndex;
  104.     private int superclassNameIndex;
  105.     private String className;
  106.     private String superclassName;
  107.     private int major;
  108.     private int minor; // Compiler version
  109.     private ConstantPool constantPool; // Constant pool
  110.     private int[] interfaces; // implemented interfaces
  111.     private String[] interfaceNames;
  112.     private Field[] fields; // Fields, i.e., variables of class
  113.     private Method[] methods; // methods defined in the class
  114.     private Attribute[] attributes; // attributes defined in the class

  115.     private AnnotationEntry[] annotations; // annotations defined on the class
  116.     private byte source = HEAP; // Generated in memory

  117.     private boolean isAnonymous;

  118.     private boolean isNested;
  119.     private boolean isRecord;

  120.     private boolean computedNestedTypeStatus;
  121.     private boolean computedRecord;

  122.     /**
  123.      * In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any
  124.      * better.
  125.      */
  126.     private transient org.apache.bcel.util.Repository repository = SyntheticRepository.getInstance();

  127.     /**
  128.      * Constructor gets all contents as arguments.
  129.      *
  130.      * @param classNameIndex Class name
  131.      * @param superclassNameIndex Superclass name
  132.      * @param fileName File name
  133.      * @param major Major compiler version
  134.      * @param minor Minor compiler version
  135.      * @param accessFlags Access rights defined by bit flags
  136.      * @param constantPool Array of constants
  137.      * @param interfaces Implemented interfaces
  138.      * @param fields Class fields
  139.      * @param methods Class methods
  140.      * @param attributes Class attributes
  141.      */
  142.     public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
  143.         final ConstantPool constantPool, final int[] interfaces, final Field[] fields, final Method[] methods, final Attribute[] attributes) {
  144.         this(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes, HEAP);
  145.     }

  146.     /**
  147.      * Constructor gets all contents as arguments.
  148.      *
  149.      * @param classNameIndex Index into constant pool referencing a ConstantClass that represents this class.
  150.      * @param superclassNameIndex Index into constant pool referencing a ConstantClass that represents this class's
  151.      *        superclass.
  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.      * @param source Read from file or generated in memory?
  162.      */
  163.     public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags,
  164.         final ConstantPool constantPool, int[] interfaces, Field[] fields, Method[] methods, Attribute[] attributes, final byte source) {
  165.         super(accessFlags);
  166.         interfaces = ArrayUtils.nullToEmpty(interfaces);
  167.         if (attributes == null) {
  168.             attributes = Attribute.EMPTY_ARRAY;
  169.         }
  170.         if (fields == null) {
  171.             fields = Field.EMPTY_ARRAY;
  172.         }
  173.         if (methods == null) {
  174.             methods = Method.EMPTY_ARRAY;
  175.         }
  176.         this.classNameIndex = classNameIndex;
  177.         this.superclassNameIndex = superclassNameIndex;
  178.         this.fileName = fileName;
  179.         this.major = major;
  180.         this.minor = minor;
  181.         this.constantPool = constantPool;
  182.         this.interfaces = interfaces;
  183.         this.fields = fields;
  184.         this.methods = methods;
  185.         this.attributes = attributes;
  186.         this.source = source;
  187.         // Get source file name if available
  188.         for (final Attribute attribute : attributes) {
  189.             if (attribute instanceof SourceFile) {
  190.                 sourceFileName = ((SourceFile) attribute).getSourceFileName();
  191.                 break;
  192.             }
  193.         }
  194.         /*
  195.          * According to the specification the following entries must be of type 'ConstantClass' but we check that anyway via the
  196.          * 'ConstPool.getConstant' method.
  197.          */
  198.         className = constantPool.getConstantString(classNameIndex, Const.CONSTANT_Class);
  199.         className = Utility.compactClassName(className, false);
  200.         final int index = className.lastIndexOf('.');
  201.         if (index < 0) {
  202.             packageName = "";
  203.         } else {
  204.             packageName = className.substring(0, index);
  205.         }
  206.         if (superclassNameIndex > 0) {
  207.             // May be zero -> class is java.lang.Object
  208.             superclassName = constantPool.getConstantString(superclassNameIndex, Const.CONSTANT_Class);
  209.             superclassName = Utility.compactClassName(superclassName, false);
  210.         } else {
  211.             superclassName = "java.lang.Object";
  212.         }
  213.         interfaceNames = new String[interfaces.length];
  214.         for (int i = 0; i < interfaces.length; i++) {
  215.             final String str = constantPool.getConstantString(interfaces[i], Const.CONSTANT_Class);
  216.             interfaceNames[i] = Utility.compactClassName(str, false);
  217.         }
  218.     }

  219.     /**
  220.      * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
  221.      * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
  222.      *
  223.      * @param v Visitor object
  224.      */
  225.     @Override
  226.     public void accept(final Visitor v) {
  227.         v.visitJavaClass(this);
  228.     }

  229.     /**
  230.      * Return the natural ordering of two JavaClasses. This ordering is based on the class name
  231.      *
  232.      * @since 6.0
  233.      */
  234.     @Override
  235.     public int compareTo(final JavaClass obj) {
  236.         return getClassName().compareTo(obj.getClassName());
  237.     }

  238.     private void computeIsRecord() {
  239.         if (computedRecord) {
  240.             return;
  241.         }
  242.         for (final Attribute attribute : this.attributes) {
  243.             if (attribute instanceof Record) {
  244.                 isRecord = true;
  245.                 break;
  246.             }
  247.         }
  248.         this.computedRecord = true;
  249.     }

  250.     private void computeNestedTypeStatus() {
  251.         if (computedNestedTypeStatus) {
  252.             return;
  253.         }
  254.         for (final Attribute attribute : this.attributes) {
  255.             if (attribute instanceof InnerClasses) {
  256.                 ((InnerClasses) attribute).forEach(innerClass ->  {
  257.                     boolean innerClassAttributeRefersToMe = false;
  258.                     String innerClassName = constantPool.getConstantString(innerClass.getInnerClassIndex(), Const.CONSTANT_Class);
  259.                     innerClassName = Utility.compactClassName(innerClassName, false);
  260.                     if (innerClassName.equals(getClassName())) {
  261.                         innerClassAttributeRefersToMe = true;
  262.                     }
  263.                     if (innerClassAttributeRefersToMe) {
  264.                         this.isNested = true;
  265.                         if (innerClass.getInnerNameIndex() == 0) {
  266.                             this.isAnonymous = true;
  267.                         }
  268.                     }
  269.                 });
  270.             }
  271.         }
  272.         this.computedNestedTypeStatus = true;
  273.     }

  274.     /**
  275.      * @return deep copy of this class
  276.      */
  277.     public JavaClass copy() {
  278.         try {
  279.             final JavaClass c = (JavaClass) clone();
  280.             c.constantPool = constantPool.copy();
  281.             c.interfaces = interfaces.clone();
  282.             c.interfaceNames = interfaceNames.clone();
  283.             c.fields = new Field[fields.length];
  284.             Arrays.setAll(c.fields, i -> fields[i].copy(c.constantPool));
  285.             c.methods = new Method[methods.length];
  286.             Arrays.setAll(c.methods, i -> methods[i].copy(c.constantPool));
  287.             c.attributes = new Attribute[attributes.length];
  288.             Arrays.setAll(c.attributes, i -> attributes[i].copy(c.constantPool));
  289.             return c;
  290.         } catch (final CloneNotSupportedException e) {
  291.             return null;
  292.         }
  293.     }

  294.     /**
  295.      * Dump Java class to output stream in binary format.
  296.      *
  297.      * @param file Output stream
  298.      * @throws IOException if an I/O error occurs.
  299.      */
  300.     public void dump(final DataOutputStream file) throws IOException {
  301.         file.writeInt(Const.JVM_CLASSFILE_MAGIC);
  302.         file.writeShort(minor);
  303.         file.writeShort(major);
  304.         constantPool.dump(file);
  305.         file.writeShort(super.getAccessFlags());
  306.         file.writeShort(classNameIndex);
  307.         file.writeShort(superclassNameIndex);
  308.         file.writeShort(interfaces.length);
  309.         for (final int interface1 : interfaces) {
  310.             file.writeShort(interface1);
  311.         }
  312.         file.writeShort(fields.length);
  313.         for (final Field field : fields) {
  314.             field.dump(file);
  315.         }
  316.         file.writeShort(methods.length);
  317.         for (final Method method : methods) {
  318.             method.dump(file);
  319.         }
  320.         if (attributes != null) {
  321.             file.writeShort(attributes.length);
  322.             for (final Attribute attribute : attributes) {
  323.                 attribute.dump(file);
  324.             }
  325.         } else {
  326.             file.writeShort(0);
  327.         }
  328.         file.flush();
  329.     }

  330.     /**
  331.      * Dump class to a file.
  332.      *
  333.      * @param file Output file
  334.      * @throws IOException if an I/O error occurs.
  335.      */
  336.     public void dump(final File file) throws IOException {
  337.         final String parent = file.getParent();
  338.         if (parent != null) {
  339.             final File dir = new File(parent);
  340.             if (!dir.mkdirs() && !dir.isDirectory()) {
  341.                 throw new IOException("Could not create the directory " + dir);
  342.             }
  343.         }
  344.         try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
  345.             dump(dos);
  346.         }
  347.     }

  348.     /**
  349.      * Dump Java class to output stream in binary format.
  350.      *
  351.      * @param file Output stream
  352.      * @throws IOException if an I/O error occurs.
  353.      */
  354.     public void dump(final OutputStream file) throws IOException {
  355.         dump(new DataOutputStream(file));
  356.     }

  357.     /**
  358.      * Dump class to a file named fileName.
  359.      *
  360.      * @param fileName Output file name
  361.      * @throws IOException if an I/O error occurs.
  362.      */
  363.     public void dump(final String fileName) throws IOException {
  364.         dump(new File(fileName));
  365.     }

  366.     /**
  367.      * Return value as defined by given BCELComparator strategy. By default two JavaClass objects are said to be equal when
  368.      * their class names are equal.
  369.      *
  370.      * @see Object#equals(Object)
  371.      */
  372.     @Override
  373.     public boolean equals(final Object obj) {
  374.         return obj instanceof JavaClass && bcelComparator.equals(this, (JavaClass) obj);
  375.     }

  376.     /**
  377.      * Finds a visible field by name and type in this class and its super classes.
  378.      * @param fieldName the field name to find
  379.      * @param fieldType the field type to find
  380.      * @return field matching given name and type, null if field is not found or not accessible from this class.
  381.      * @throws ClassNotFoundException
  382.      * @since 6.8.0
  383.      */
  384.     public Field findField(final String fieldName, final Type fieldType) throws ClassNotFoundException {
  385.         for (final Field field : fields) {
  386.             if (field.getName().equals(fieldName)) {
  387.                 final Type fType = Type.getType(field.getSignature());
  388.                 /*
  389.                  * TODO: Check if assignment compatibility is sufficient. What does Sun do?
  390.                  */
  391.                 if (fType.equals(fieldType)) {
  392.                     return field;
  393.                 }
  394.             }
  395.         }

  396.         final JavaClass superclass = getSuperClass();
  397.         if (superclass != null && !"java.lang.Object".equals(superclass.getClassName())) {
  398.             final Field f = superclass.findField(fieldName, fieldType);
  399.             if (f != null && (f.isPublic() || f.isProtected() || !f.isPrivate() && packageName.equals(superclass.getPackageName()))) {
  400.                 return f;
  401.             }
  402.         }
  403.         final JavaClass[] implementedInterfaces = getInterfaces();
  404.         if (implementedInterfaces != null) {
  405.             for (final JavaClass implementedInterface : implementedInterfaces) {
  406.                 final Field f = implementedInterface.findField(fieldName, fieldType);
  407.                 if (f != null) {
  408.                     return f;
  409.                 }
  410.             }
  411.         }
  412.         return null;
  413.     }

  414.     /**
  415.      * Gets all interfaces implemented by this JavaClass (transitively).
  416.      *
  417.      * @throws ClassNotFoundException if any of the class's superclasses or interfaces can't be found.
  418.      */
  419.     public JavaClass[] getAllInterfaces() throws ClassNotFoundException {
  420.         final ClassQueue queue = new ClassQueue();
  421.         final Set<JavaClass> allInterfaces = new TreeSet<>();
  422.         queue.enqueue(this);
  423.         while (!queue.empty()) {
  424.             final JavaClass clazz = queue.dequeue();
  425.             final JavaClass souper = clazz.getSuperClass();
  426.             final JavaClass[] interfaces = clazz.getInterfaces();
  427.             if (clazz.isInterface()) {
  428.                 allInterfaces.add(clazz);
  429.             } else if (souper != null) {
  430.                 queue.enqueue(souper);
  431.             }
  432.             for (final JavaClass iface : interfaces) {
  433.                 queue.enqueue(iface);
  434.             }
  435.         }
  436.         return allInterfaces.toArray(EMPTY_ARRAY);
  437.     }

  438.     /**
  439.      * @return Annotations on the class
  440.      * @since 6.0
  441.      */
  442.     public AnnotationEntry[] getAnnotationEntries() {
  443.         if (annotations == null) {
  444.             annotations = AnnotationEntry.createAnnotationEntries(getAttributes());
  445.         }

  446.         return annotations;
  447.     }

  448.     /**
  449.      * Gets attribute for given tag.
  450.      * @return Attribute for given tag, null if not found.
  451.      * Refer to {@link org.apache.bcel.Const#ATTR_UNKNOWN} constants named ATTR_* for possible values.
  452.      * @since 6.10.0
  453.      */
  454.     @SuppressWarnings("unchecked")
  455.     public final <T extends Attribute> T getAttribute(final byte tag) {
  456.         for (final Attribute attribute : getAttributes()) {
  457.             if (attribute.getTag() == tag) {
  458.                 return (T) attribute;
  459.             }
  460.         }
  461.         return null;
  462.     }

  463.     /**
  464.      * @return Attributes of the class.
  465.      */
  466.     public Attribute[] getAttributes() {
  467.         return attributes;
  468.     }

  469.     /**
  470.      * @return class in binary format
  471.      */
  472.     public byte[] getBytes() {
  473.         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
  474.         try (DataOutputStream dos = new DataOutputStream(baos)) {
  475.             dump(dos);
  476.         } catch (final IOException e) {
  477.             e.printStackTrace();
  478.         }
  479.         return baos.toByteArray();
  480.     }

  481.     /**
  482.      * @return Class name.
  483.      */
  484.     public String getClassName() {
  485.         return className;
  486.     }

  487.     /**
  488.      * @return Class name index.
  489.      */
  490.     public int getClassNameIndex() {
  491.         return classNameIndex;
  492.     }

  493.     /**
  494.      * @return Constant pool.
  495.      */
  496.     public ConstantPool getConstantPool() {
  497.         return constantPool;
  498.     }

  499.     /**
  500.      * @return Fields, i.e., variables of the class. Like the JVM spec mandates for the classfile format, these fields are
  501.      *         those specific to this class, and not those of the superclass or superinterfaces.
  502.      */
  503.     public Field[] getFields() {
  504.         return fields;
  505.     }

  506.     /**
  507.      * @return File name of class, aka SourceFile attribute value
  508.      */
  509.     public String getFileName() {
  510.         return fileName;
  511.     }

  512.     /**
  513.      * @return Indices in constant pool of implemented interfaces.
  514.      */
  515.     public int[] getInterfaceIndices() {
  516.         return interfaces;
  517.     }

  518.     /**
  519.      * @return Names of implemented interfaces.
  520.      */
  521.     public String[] getInterfaceNames() {
  522.         return interfaceNames;
  523.     }

  524.     /**
  525.      * Gets interfaces directly implemented by this JavaClass.
  526.      *
  527.      * @throws ClassNotFoundException if any of the class's interfaces can't be found.
  528.      */
  529.     public JavaClass[] getInterfaces() throws ClassNotFoundException {
  530.         final String[] interfaces = getInterfaceNames();
  531.         final JavaClass[] classes = new JavaClass[interfaces.length];
  532.         for (int i = 0; i < interfaces.length; i++) {
  533.             classes[i] = repository.loadClass(interfaces[i]);
  534.         }
  535.         return classes;
  536.     }

  537.     /**
  538.      * @return Major number of class file version.
  539.      */
  540.     public int getMajor() {
  541.         return major;
  542.     }

  543.     /**
  544.      * @return A {@link Method} corresponding to java.lang.reflect.Method if any
  545.      */
  546.     public Method getMethod(final java.lang.reflect.Method m) {
  547.         for (final Method method : methods) {
  548.             if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) {
  549.                 return method;
  550.             }
  551.         }
  552.         return null;
  553.     }

  554.     /**
  555.      * @return Methods of the class.
  556.      */
  557.     public Method[] getMethods() {
  558.         return methods;
  559.     }

  560.     /**
  561.      * @return Minor number of class file version.
  562.      */
  563.     public int getMinor() {
  564.         return minor;
  565.     }

  566.     /**
  567.      * @return Package name.
  568.      */
  569.     public String getPackageName() {
  570.         return packageName;
  571.     }

  572.     /**
  573.      * Gets the ClassRepository which holds its definition. By default this is the same as
  574.      * SyntheticRepository.getInstance();
  575.      */
  576.     public org.apache.bcel.util.Repository getRepository() {
  577.         return repository;
  578.     }

  579.     /**
  580.      * @return returns either HEAP (generated), FILE, or ZIP
  581.      */
  582.     public final byte getSource() {
  583.         return source;
  584.     }

  585.     /**
  586.      * @return file name where this class was read from
  587.      */
  588.     public String getSourceFileName() {
  589.         return sourceFileName;
  590.     }

  591.     /**
  592.      * Gets the source file path including the package path.
  593.      *
  594.      * @return path to original source file of parsed class, relative to original source directory.
  595.      * @since 6.7.0
  596.      */
  597.     public String getSourceFilePath() {
  598.         final StringBuilder outFileName = new StringBuilder();
  599.         if (!packageName.isEmpty()) {
  600.             outFileName.append(Utility.packageToPath(packageName));
  601.             outFileName.append('/');
  602.         }
  603.         outFileName.append(sourceFileName);
  604.         return outFileName.toString();
  605.     }

  606.     /**
  607.      * @return the superclass for this JavaClass object, or null if this is {@link Object}
  608.      * @throws ClassNotFoundException if the superclass can't be found
  609.      */
  610.     public JavaClass getSuperClass() throws ClassNotFoundException {
  611.         if ("java.lang.Object".equals(getClassName())) {
  612.             return null;
  613.         }
  614.         return repository.loadClass(getSuperclassName());
  615.     }

  616.     /**
  617.      * @return list of super classes of this class in ascending order, i.e., java.lang.Object is always the last element
  618.      * @throws ClassNotFoundException if any of the superclasses can't be found
  619.      */
  620.     public JavaClass[] getSuperClasses() throws ClassNotFoundException {
  621.         JavaClass clazz = this;
  622.         final List<JavaClass> allSuperClasses = new ArrayList<>();
  623.         for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) {
  624.             allSuperClasses.add(clazz);
  625.         }
  626.         return allSuperClasses.toArray(EMPTY_ARRAY);
  627.     }

  628.     /**
  629.      * returns the super class name of this class. In the case that this class is {@link Object}, it will return itself
  630.      * ({@link Object}). This is probably incorrect but isn't fixed at this time to not break existing clients.
  631.      *
  632.      * @return Superclass name.
  633.      */
  634.     public String getSuperclassName() {
  635.         return superclassName;
  636.     }

  637.     /**
  638.      * @return Class name index.
  639.      */
  640.     public int getSuperclassNameIndex() {
  641.         return superclassNameIndex;
  642.     }

  643.     /**
  644.      * Return value as defined by given BCELComparator strategy. By default return the hash code of the class name.
  645.      *
  646.      * @see Object#hashCode()
  647.      */
  648.     @Override
  649.     public int hashCode() {
  650.         return bcelComparator.hashCode(this);
  651.     }

  652.     /**
  653.      * @return true, if this class is an implementation of interface inter
  654.      * @throws ClassNotFoundException if superclasses or superinterfaces of this class can't be found
  655.      */
  656.     public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException {
  657.         if (!inter.isInterface()) {
  658.             throw new IllegalArgumentException(inter.getClassName() + " is no interface");
  659.         }
  660.         if (equals(inter)) {
  661.             return true;
  662.         }
  663.         final JavaClass[] superInterfaces = getAllInterfaces();
  664.         for (final JavaClass superInterface : superInterfaces) {
  665.             if (superInterface.equals(inter)) {
  666.                 return true;
  667.             }
  668.         }
  669.         return false;
  670.     }

  671.     /**
  672.      * Equivalent to runtime "instanceof" operator.
  673.      *
  674.      * @return true if this JavaClass is derived from the super class
  675.      * @throws ClassNotFoundException if superclasses or superinterfaces of this object can't be found
  676.      */
  677.     public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException {
  678.         if (equals(superclass)) {
  679.             return true;
  680.         }
  681.         for (final JavaClass clazz : getSuperClasses()) {
  682.             if (clazz.equals(superclass)) {
  683.                 return true;
  684.             }
  685.         }
  686.         if (superclass.isInterface()) {
  687.             return implementationOf(superclass);
  688.         }
  689.         return false;
  690.     }

  691.     /**
  692.      * @since 6.0
  693.      */
  694.     public final boolean isAnonymous() {
  695.         computeNestedTypeStatus();
  696.         return this.isAnonymous;
  697.     }

  698.     public final boolean isClass() {
  699.         return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0;
  700.     }

  701.     /**
  702.      * @since 6.0
  703.      */
  704.     public final boolean isNested() {
  705.         computeNestedTypeStatus();
  706.         return this.isNested;
  707.     }

  708.     /**
  709.      * Tests whether this class was declared as a record
  710.      *
  711.      * @return true if a record attribute is present, false otherwise.
  712.      * @since 6.9.0
  713.      */
  714.     public boolean isRecord() {
  715.         computeIsRecord();
  716.         return this.isRecord;
  717.     }

  718.     public final boolean isSuper() {
  719.         return (super.getAccessFlags() & Const.ACC_SUPER) != 0;
  720.     }

  721.     /**
  722.      * @param attributes .
  723.      */
  724.     public void setAttributes(final Attribute[] attributes) {
  725.         this.attributes = attributes != null ? attributes : Attribute.EMPTY_ARRAY;
  726.     }

  727.     /**
  728.      * @param className .
  729.      */
  730.     public void setClassName(final String className) {
  731.         this.className = className;
  732.     }

  733.     /**
  734.      * @param classNameIndex .
  735.      */
  736.     public void setClassNameIndex(final int classNameIndex) {
  737.         this.classNameIndex = classNameIndex;
  738.     }

  739.     /**
  740.      * @param constantPool .
  741.      */
  742.     public void setConstantPool(final ConstantPool constantPool) {
  743.         this.constantPool = constantPool;
  744.     }

  745.     /**
  746.      * @param fields .
  747.      */
  748.     public void setFields(final Field[] fields) {
  749.         this.fields = fields != null ? fields : Field.EMPTY_ARRAY;
  750.     }

  751.     /**
  752.      * Sets File name of class, aka SourceFile attribute value
  753.      */
  754.     public void setFileName(final String fileName) {
  755.         this.fileName = fileName;
  756.     }

  757.     /**
  758.      * @param interfaceNames .
  759.      */
  760.     public void setInterfaceNames(final String[] interfaceNames) {
  761.         this.interfaceNames = ArrayUtils.nullToEmpty(interfaceNames);
  762.     }

  763.     /**
  764.      * @param interfaces .
  765.      */
  766.     public void setInterfaces(final int[] interfaces) {
  767.         this.interfaces = ArrayUtils.nullToEmpty(interfaces);
  768.     }

  769.     /**
  770.      * @param major .
  771.      */
  772.     public void setMajor(final int major) {
  773.         this.major = major;
  774.     }

  775.     /**
  776.      * @param methods .
  777.      */
  778.     public void setMethods(final Method[] methods) {
  779.         this.methods = methods != null ? methods : Method.EMPTY_ARRAY;
  780.     }

  781.     /**
  782.      * @param minor .
  783.      */
  784.     public void setMinor(final int minor) {
  785.         this.minor = minor;
  786.     }

  787.     /**
  788.      * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done.
  789.      */
  790.     public void setRepository(final org.apache.bcel.util.Repository repository) { // TODO make protected?
  791.         this.repository = repository;
  792.     }

  793.     /**
  794.      * Sets absolute path to file this class was read from.
  795.      */
  796.     public void setSourceFileName(final String sourceFileName) {
  797.         this.sourceFileName = sourceFileName;
  798.     }

  799.     /**
  800.      * @param superclassName .
  801.      */
  802.     public void setSuperclassName(final String superclassName) {
  803.         this.superclassName = superclassName;
  804.     }

  805.     /**
  806.      * @param superclassNameIndex .
  807.      */
  808.     public void setSuperclassNameIndex(final int superclassNameIndex) {
  809.         this.superclassNameIndex = superclassNameIndex;
  810.     }

  811.     /**
  812.      * @return String representing class contents.
  813.      */
  814.     @Override
  815.     public String toString() {
  816.         String access = Utility.accessToString(super.getAccessFlags(), true);
  817.         access = access.isEmpty() ? "" : access + " ";
  818.         final StringBuilder buf = new StringBuilder(128);
  819.         buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ")
  820.             .append(Utility.compactClassName(superclassName, false)).append('\n');
  821.         final int size = interfaces.length;
  822.         if (size > 0) {
  823.             buf.append("implements\t\t");
  824.             for (int i = 0; i < size; i++) {
  825.                 buf.append(interfaceNames[i]);
  826.                 if (i < size - 1) {
  827.                     buf.append(", ");
  828.                 }
  829.             }
  830.             buf.append('\n');
  831.         }
  832.         buf.append("file name\t\t").append(fileName).append('\n');
  833.         buf.append("compiled from\t\t").append(sourceFileName).append('\n');
  834.         buf.append("compiler version\t").append(major).append(".").append(minor).append('\n');
  835.         buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n');
  836.         buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n");
  837.         buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n");
  838.         if (attributes.length > 0) {
  839.             buf.append("\nAttribute(s):\n");
  840.             for (final Attribute attribute : attributes) {
  841.                 buf.append(indent(attribute));
  842.             }
  843.         }
  844.         final AnnotationEntry[] annotations = getAnnotationEntries();
  845.         if (annotations != null && annotations.length > 0) {
  846.             buf.append("\nAnnotation(s):\n");
  847.             for (final AnnotationEntry annotation : annotations) {
  848.                 buf.append(indent(annotation));
  849.             }
  850.         }
  851.         if (fields.length > 0) {
  852.             buf.append("\n").append(fields.length).append(" fields:\n");
  853.             for (final Field field : fields) {
  854.                 buf.append("\t").append(field).append('\n');
  855.             }
  856.         }
  857.         if (methods.length > 0) {
  858.             buf.append("\n").append(methods.length).append(" methods:\n");
  859.             for (final Method method : methods) {
  860.                 buf.append("\t").append(method).append('\n');
  861.             }
  862.         }
  863.         return buf.toString();
  864.     }
  865. }