| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.bcel.classfile; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.DataOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.TreeSet; |
| |
| import org.apache.bcel.Const; |
| import org.apache.bcel.generic.Type; |
| import org.apache.bcel.util.BCELComparator; |
| import org.apache.bcel.util.ClassQueue; |
| import org.apache.bcel.util.SyntheticRepository; |
| import org.apache.commons.lang3.ArrayUtils; |
| |
| /** |
| * Represents a Java class, that is, the data structures, constant pool, fields, methods and commands contained in a Java |
| * .class file. See <a href="https://docs.oracle.com/javase/specs/">JVM specification</a> for details. The intent of |
| * this class is to represent a parsed or otherwise existing class file. Those interested in programmatically generating |
| * classes should see the <a href="../generic/ClassGen.html">ClassGen</a> class. |
| * |
| * @see org.apache.bcel.generic.ClassGen |
| */ |
| public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> { |
| |
| private static final String CLASS_NAME_OBJECT = "java.lang.Object"; |
| |
| /** |
| * The standard class file extension. |
| * |
| * @since 6.7.0 |
| */ |
| public static final String EXTENSION = ".class"; |
| |
| /** |
| * Empty array. |
| * |
| * @since 6.6.0 |
| */ |
| public static final JavaClass[] EMPTY_ARRAY = {}; |
| |
| /** Source was read from heap. */ |
| public static final byte HEAP = 1; |
| |
| /** Source was read from file. */ |
| public static final byte FILE = 2; |
| |
| /** Source was read from ZIP. */ |
| public static final byte ZIP = 3; |
| |
| private static final boolean debug = Boolean.getBoolean("JavaClass.debug"); // Debugging on/off |
| |
| private static BCELComparator<JavaClass> bcelComparator = new BCELComparator<JavaClass>() { |
| |
| @Override |
| public boolean equals(final JavaClass a, final JavaClass b) { |
| return a == b || a != null && b != null && Objects.equals(a.getClassName(), b.getClassName()); |
| } |
| |
| @Override |
| public int hashCode(final JavaClass o) { |
| return o != null ? Objects.hashCode(o.getClassName()) : 0; |
| } |
| }; |
| |
| /* |
| * Print debug information depending on 'JavaClass.debug' |
| */ |
| static void Debug(final String str) { |
| if (debug) { |
| System.out.println(str); |
| } |
| } |
| |
| /** |
| * Gets the comparison strategy object. |
| * |
| * @return Comparison strategy object. |
| */ |
| public static BCELComparator<JavaClass> getComparator() { |
| return bcelComparator; |
| } |
| |
| private static String indent(final Object obj) { |
| final StringTokenizer tokenizer = new StringTokenizer(obj.toString(), "\n"); |
| final StringBuilder buf = new StringBuilder(); |
| while (tokenizer.hasMoreTokens()) { |
| buf.append("\t").append(tokenizer.nextToken()).append("\n"); |
| } |
| return buf.toString(); |
| } |
| |
| /** |
| * Sets the comparison strategy object. |
| * |
| * @param comparator Comparison strategy object. |
| */ |
| public static void setComparator(final BCELComparator<JavaClass> comparator) { |
| bcelComparator = comparator; |
| } |
| |
| private String fileName; |
| private final String packageName; |
| private String sourceFileName = "<Unknown>"; |
| private int classNameIndex; |
| private int superclassNameIndex; |
| private String className; |
| private String superclassName; |
| private int major; |
| private int minor; // Compiler version |
| private ConstantPool constantPool; // Constant pool |
| private int[] interfaces; // implemented interfaces |
| private String[] interfaceNames; |
| private Field[] fields; // Fields, that is, variables of class |
| private Method[] methods; // methods defined in the class |
| private Attribute[] attributes; // attributes defined in the class |
| |
| private AnnotationEntry[] annotations; // annotations defined on the class |
| private byte source = HEAP; // Generated in memory |
| |
| private boolean isAnonymous; |
| |
| private boolean isNested; |
| private boolean isRecord; |
| |
| private boolean computedNestedTypeStatus; |
| private boolean computedRecord; |
| |
| /** |
| * In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any |
| * better. |
| */ |
| private transient org.apache.bcel.util.Repository repository = SyntheticRepository.getInstance(); |
| |
| /** |
| * Constructor gets all contents as arguments. |
| * |
| * @param classNameIndex Class name. |
| * @param superclassNameIndex Superclass name. |
| * @param fileName File name. |
| * @param major Major compiler version. |
| * @param minor Minor compiler version. |
| * @param accessFlags Access rights defined by bit flags. |
| * @param constantPool Array of constants. |
| * @param interfaces Implemented interfaces. |
| * @param fields Class fields. |
| * @param methods Class methods. |
| * @param attributes Class attributes. |
| */ |
| public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags, |
| final ConstantPool constantPool, final int[] interfaces, final Field[] fields, final Method[] methods, final Attribute[] attributes) { |
| this(classNameIndex, superclassNameIndex, fileName, major, minor, accessFlags, constantPool, interfaces, fields, methods, attributes, HEAP); |
| } |
| |
| /** |
| * Constructor gets all contents as arguments. |
| * |
| * @param classNameIndex Index into constant pool referencing a ConstantClass that represents this class. |
| * @param superclassNameIndex Index into constant pool referencing a ConstantClass that represents this class's superclass. |
| * @param fileName File name. |
| * @param major Major compiler version. |
| * @param minor Minor compiler version. |
| * @param accessFlags Access rights defined by bit flags. |
| * @param constantPool Array of constants. |
| * @param interfaces Implemented interfaces. |
| * @param fields Class fields. |
| * @param methods Class methods. |
| * @param attributes Class attributes. |
| * @param source Read from file or generated in memory. |
| */ |
| public JavaClass(final int classNameIndex, final int superclassNameIndex, final String fileName, final int major, final int minor, final int accessFlags, |
| final ConstantPool constantPool, int[] interfaces, Field[] fields, Method[] methods, Attribute[] attributes, final byte source) { |
| super(accessFlags); |
| interfaces = ArrayUtils.nullToEmpty(interfaces); |
| if (attributes == null) { |
| attributes = Attribute.EMPTY_ARRAY; |
| } |
| if (fields == null) { |
| fields = Field.EMPTY_ARRAY; |
| } |
| if (methods == null) { |
| methods = Method.EMPTY_ARRAY; |
| } |
| this.classNameIndex = classNameIndex; |
| this.superclassNameIndex = superclassNameIndex; |
| this.fileName = fileName; |
| this.major = major; |
| this.minor = minor; |
| this.constantPool = constantPool; |
| this.interfaces = interfaces; |
| this.fields = fields; |
| this.methods = methods; |
| this.attributes = attributes; |
| this.source = source; |
| // Get source file name if available |
| for (final Attribute attribute : attributes) { |
| if (attribute instanceof SourceFile) { |
| sourceFileName = ((SourceFile) attribute).getSourceFileName(); |
| break; |
| } |
| } |
| /* |
| * According to the specification the following entries must be of type 'ConstantClass' but we check that anyway via the |
| * 'ConstPool.getConstant' method. |
| */ |
| className = constantPool.getConstantString(classNameIndex, Const.CONSTANT_Class); |
| className = Utility.compactClassName(className, false); |
| final int index = className.lastIndexOf('.'); |
| if (index < 0) { |
| packageName = ""; |
| } else { |
| packageName = className.substring(0, index); |
| } |
| if (superclassNameIndex > 0) { |
| // May be zero -> class is java.lang.Object |
| superclassName = constantPool.getConstantString(superclassNameIndex, Const.CONSTANT_Class); |
| superclassName = Utility.compactClassName(superclassName, false); |
| } else { |
| superclassName = CLASS_NAME_OBJECT; |
| } |
| interfaceNames = new String[interfaces.length]; |
| for (int i = 0; i < interfaces.length; i++) { |
| final String str = constantPool.getConstantString(interfaces[i], Const.CONSTANT_Class); |
| interfaceNames[i] = Utility.compactClassName(str, false); |
| } |
| } |
| |
| /** |
| * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. |
| * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. |
| * |
| * @param v Visitor object. |
| */ |
| @Override |
| public void accept(final Visitor v) { |
| v.visitJavaClass(this); |
| } |
| |
| /** |
| * Return the natural ordering of two JavaClasses. This ordering is based on the class name |
| * |
| * @since 6.0 |
| */ |
| @Override |
| public int compareTo(final JavaClass obj) { |
| return getClassName().compareTo(obj.getClassName()); |
| } |
| |
| private void computeIsRecord() { |
| if (computedRecord) { |
| return; |
| } |
| for (final Attribute attribute : this.attributes) { |
| if (attribute instanceof Record) { |
| isRecord = true; |
| break; |
| } |
| } |
| this.computedRecord = true; |
| } |
| |
| private void computeNestedTypeStatus() { |
| if (computedNestedTypeStatus) { |
| return; |
| } |
| for (final Attribute attribute : this.attributes) { |
| if (attribute instanceof InnerClasses) { |
| ((InnerClasses) attribute).forEach(innerClass -> { |
| boolean innerClassAttributeRefersToMe = false; |
| String innerClassName = constantPool.getConstantString(innerClass.getInnerClassIndex(), Const.CONSTANT_Class); |
| innerClassName = Utility.compactClassName(innerClassName, false); |
| if (innerClassName.equals(getClassName())) { |
| innerClassAttributeRefersToMe = true; |
| } |
| if (innerClassAttributeRefersToMe) { |
| this.isNested = true; |
| if (innerClass.getInnerNameIndex() == 0) { |
| this.isAnonymous = true; |
| } |
| } |
| }); |
| } |
| } |
| this.computedNestedTypeStatus = true; |
| } |
| |
| /** |
| * Creates a deep copy of this class. |
| * |
| * @return deep copy of this class. |
| */ |
| public JavaClass copy() { |
| try { |
| final JavaClass c = (JavaClass) clone(); |
| c.constantPool = constantPool.copy(); |
| c.interfaces = interfaces.clone(); |
| c.interfaceNames = interfaceNames.clone(); |
| c.fields = new Field[fields.length]; |
| Arrays.setAll(c.fields, i -> fields[i].copy(c.constantPool)); |
| c.methods = new Method[methods.length]; |
| Arrays.setAll(c.methods, i -> methods[i].copy(c.constantPool)); |
| c.attributes = new Attribute[attributes.length]; |
| Arrays.setAll(c.attributes, i -> attributes[i].copy(c.constantPool)); |
| return c; |
| } catch (final CloneNotSupportedException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Dumps Java class to output stream in binary format. |
| * |
| * @param file Output stream. |
| * @throws IOException if an I/O error occurs. |
| */ |
| public void dump(final DataOutputStream file) throws IOException { |
| file.writeInt(Const.JVM_CLASSFILE_MAGIC); |
| file.writeShort(minor); |
| file.writeShort(major); |
| constantPool.dump(file); |
| file.writeShort(super.getAccessFlags()); |
| file.writeShort(classNameIndex); |
| file.writeShort(superclassNameIndex); |
| file.writeShort(interfaces.length); |
| for (final int interface1 : interfaces) { |
| file.writeShort(interface1); |
| } |
| file.writeShort(fields.length); |
| for (final Field field : fields) { |
| field.dump(file); |
| } |
| file.writeShort(methods.length); |
| for (final Method method : methods) { |
| method.dump(file); |
| } |
| if (attributes != null) { |
| file.writeShort(attributes.length); |
| for (final Attribute attribute : attributes) { |
| attribute.dump(file); |
| } |
| } else { |
| file.writeShort(0); |
| } |
| file.flush(); |
| } |
| |
| /** |
| * Dumps class to a file. |
| * |
| * @param file Output file. |
| * @throws IOException if an I/O error occurs. |
| */ |
| public void dump(final File file) throws IOException { |
| final String parent = file.getParent(); |
| if (parent != null) { |
| final File dir = new File(parent); |
| if (!dir.mkdirs() && !dir.isDirectory()) { |
| throw new IOException("Could not create the directory " + dir); |
| } |
| } |
| try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) { |
| dump(dos); |
| } |
| } |
| |
| /** |
| * Dumps Java class to output stream in binary format. |
| * |
| * @param file Output stream. |
| * @throws IOException if an I/O error occurs. |
| */ |
| public void dump(final OutputStream file) throws IOException { |
| dump(new DataOutputStream(file)); |
| } |
| |
| /** |
| * Dumps class to a file named fileName. |
| * |
| * @param fileName Output file name. |
| * @throws IOException if an I/O error occurs. |
| */ |
| public void dump(final String fileName) throws IOException { |
| dump(new File(fileName)); |
| } |
| |
| /** |
| * Return value as defined by given BCELComparator strategy. By default two JavaClass objects are said to be equal when |
| * their class names are equal. |
| * |
| * @see Object#equals(Object) |
| */ |
| @Override |
| public boolean equals(final Object obj) { |
| return obj instanceof JavaClass && bcelComparator.equals(this, (JavaClass) obj); |
| } |
| |
| /** |
| * Finds a visible field by name and type in this class and its super classes. |
| * |
| * @param fieldName the field name to find. |
| * @param fieldType the field type to find. |
| * @return field matching given name and type, null if field is not found or not accessible from this class. |
| * @throws ClassNotFoundException if the class cannot be found. |
| * @since 6.8.0 |
| */ |
| public Field findField(final String fieldName, final Type fieldType) throws ClassNotFoundException { |
| return findFieldVisit(fieldName, fieldType, new HashSet<>()); |
| } |
| |
| private Field findFieldVisit(final String fieldName, final Type fieldType, final Set<JavaClass> visiting) throws ClassNotFoundException { |
| if (!visiting.add(this)) { |
| throw new ClassFormatException(getClassName()); |
| } |
| try { |
| for (final Field field : fields) { |
| if (field.getName().equals(fieldName)) { |
| final Type fType = Type.getType(field.getSignature()); |
| // TODO: Check if assignment compatibility is sufficient. What does Sun do? |
| if (fType.equals(fieldType)) { |
| return field; |
| } |
| } |
| } |
| final JavaClass superclass = getSuperClass(); |
| if (superclass != null && !CLASS_NAME_OBJECT.equals(superclass.getClassName())) { |
| final Field f = superclass.findFieldVisit(fieldName, fieldType, visiting); |
| if (f != null && (f.isPublic() || f.isProtected() || !f.isPrivate() && packageName.equals(superclass.getPackageName()))) { |
| return f; |
| } |
| } |
| final JavaClass[] implementedInterfaces = getInterfaces(); |
| if (implementedInterfaces != null) { |
| for (final JavaClass implementedInterface : implementedInterfaces) { |
| final Field f = implementedInterface.findFieldVisit(fieldName, fieldType, visiting); |
| if (f != null) { |
| return f; |
| } |
| } |
| } |
| return null; |
| } finally { |
| visiting.remove(this); |
| } |
| } |
| |
| /** |
| * Gets all interfaces implemented by this JavaClass (transitively). |
| * |
| * @return all interfaces. |
| * @throws ClassNotFoundException if any of the class's superclasses or interfaces can't be found. |
| */ |
| public JavaClass[] getAllInterfaces() throws ClassNotFoundException { |
| final ClassQueue queue = new ClassQueue(); |
| final Set<JavaClass> allInterfaces = new TreeSet<>(); |
| final Set<JavaClass> visited = new HashSet<>(); |
| queue.enqueue(this); |
| while (!queue.empty()) { |
| final JavaClass clazz = queue.dequeue(); |
| if (!visited.add(clazz)) { |
| continue; |
| } |
| final JavaClass souper = clazz.getSuperClass(); |
| final JavaClass[] interfaces = clazz.getInterfaces(); |
| if (clazz.isInterface()) { |
| allInterfaces.add(clazz); |
| } else if (souper != null) { |
| queue.enqueue(souper); |
| } |
| for (final JavaClass iface : interfaces) { |
| queue.enqueue(iface); |
| } |
| } |
| return allInterfaces.toArray(EMPTY_ARRAY); |
| } |
| |
| /** |
| * Gets annotations on the class. |
| * |
| * @return Annotations on the class. |
| * @since 6.0 |
| */ |
| public AnnotationEntry[] getAnnotationEntries() { |
| if (annotations == null) { |
| annotations = AnnotationEntry.createAnnotationEntries(getAttributes()); |
| } |
| |
| return annotations; |
| } |
| |
| /** |
| * Gets attribute for given tag. |
| * |
| * @param <T> the attribute type. |
| * @param tag the attribute tag. |
| * @return Attribute for given tag, null if not found. |
| * Refer to {@link org.apache.bcel.Const#ATTR_UNKNOWN} constants named ATTR_* for possible values. |
| * @since 6.10.0 |
| */ |
| @SuppressWarnings("unchecked") |
| public final <T extends Attribute> T getAttribute(final byte tag) { |
| for (final Attribute attribute : getAttributes()) { |
| if (attribute.getTag() == tag) { |
| return (T) attribute; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Gets attributes of the class. |
| * |
| * @return Attributes of the class. |
| */ |
| public Attribute[] getAttributes() { |
| return attributes; |
| } |
| |
| /** |
| * Gets class in binary format. |
| * |
| * @return class in binary format. |
| */ |
| public byte[] getBytes() { |
| final ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| try (DataOutputStream dos = new DataOutputStream(baos)) { |
| dump(dos); |
| } catch (final IOException e) { |
| e.printStackTrace(); |
| } |
| return baos.toByteArray(); |
| } |
| |
| /** |
| * Gets the class name. |
| * |
| * @return Class name. |
| */ |
| public String getClassName() { |
| return className; |
| } |
| |
| /** |
| * Gets the class name index. |
| * |
| * @return Class name index. |
| */ |
| public int getClassNameIndex() { |
| return classNameIndex; |
| } |
| |
| /** |
| * Gets the constant pool. |
| * |
| * @return Constant pool. |
| */ |
| public ConstantPool getConstantPool() { |
| return constantPool; |
| } |
| |
| /** |
| * Gets the fields. |
| * |
| * @return Fields, that is, variables of the class. Like the JVM spec mandates for the classfile format, these fields are |
| * those specific to this class, and not those of the superclass or superinterfaces. |
| */ |
| public Field[] getFields() { |
| return fields; |
| } |
| |
| /** |
| * Gets the file name of class. |
| * |
| * @return File name of class, aka SourceFile attribute value. |
| */ |
| public String getFileName() { |
| return fileName; |
| } |
| |
| /** |
| * Gets indices in constant pool of implemented interfaces. |
| * |
| * @return Indices in constant pool of implemented interfaces. |
| */ |
| public int[] getInterfaceIndices() { |
| return interfaces; |
| } |
| |
| /** |
| * Gets names of implemented interfaces. |
| * |
| * @return Names of implemented interfaces. |
| */ |
| public String[] getInterfaceNames() { |
| return interfaceNames; |
| } |
| |
| /** |
| * Gets interfaces directly implemented by this JavaClass. |
| * |
| * @return the interfaces. |
| * @throws ClassNotFoundException if any of the class's interfaces can't be found. |
| */ |
| public JavaClass[] getInterfaces() throws ClassNotFoundException { |
| final String[] interfaces = getInterfaceNames(); |
| final JavaClass[] classes = new JavaClass[interfaces.length]; |
| for (int i = 0; i < interfaces.length; i++) { |
| classes[i] = repository.loadClass(interfaces[i]); |
| } |
| return classes; |
| } |
| |
| /** |
| * Gets the major number of class file version. |
| * |
| * @return Major number of class file version. |
| */ |
| public int getMajor() { |
| return major; |
| } |
| |
| /** |
| * Gets a Method corresponding to java.lang.reflect.Method if any. |
| * |
| * @param m the method to find. |
| * @return A {@link Method} corresponding to java.lang.reflect.Method if any. |
| */ |
| public Method getMethod(final java.lang.reflect.Method m) { |
| for (final Method method : methods) { |
| if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) { |
| return method; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Gets the methods of the class. |
| * |
| * @return Methods of the class. |
| */ |
| public Method[] getMethods() { |
| return methods; |
| } |
| |
| /** |
| * Gets the minor number of class file version. |
| * |
| * @return Minor number of class file version. |
| */ |
| public int getMinor() { |
| return minor; |
| } |
| |
| /** |
| * Gets the package name. |
| * |
| * @return Package name. |
| */ |
| public String getPackageName() { |
| return packageName; |
| } |
| |
| /** |
| * Gets the ClassRepository which holds its definition. By default this is the same as |
| * SyntheticRepository.getInstance(). |
| * |
| * @return the repository. |
| */ |
| public org.apache.bcel.util.Repository getRepository() { |
| return repository; |
| } |
| |
| /** |
| * Gets the source. |
| * |
| * @return either HEAP (generated), FILE, or ZIP. |
| */ |
| public final byte getSource() { |
| return source; |
| } |
| |
| /** |
| * Gets the file name where this class was read from. |
| * |
| * @return file name where this class was read from. |
| */ |
| public String getSourceFileName() { |
| return sourceFileName; |
| } |
| |
| /** |
| * Gets the source file path including the package path. |
| * |
| * @return path to original source file of parsed class, relative to original source directory. |
| * @since 6.7.0 |
| */ |
| public String getSourceFilePath() { |
| final StringBuilder outFileName = new StringBuilder(); |
| if (!packageName.isEmpty()) { |
| outFileName.append(Utility.packageToPath(packageName)); |
| outFileName.append('/'); |
| } |
| outFileName.append(sourceFileName); |
| return outFileName.toString(); |
| } |
| |
| /** |
| * Gets the superclass for this JavaClass object, or null if this is {@link Object}. |
| * |
| * @return the superclass for this JavaClass object, or null if this is {@link Object}. |
| * @throws ClassNotFoundException if the superclass can't be found. |
| */ |
| public JavaClass getSuperClass() throws ClassNotFoundException { |
| if (CLASS_NAME_OBJECT.equals(getClassName())) { |
| return null; |
| } |
| return repository.loadClass(getSuperclassName()); |
| } |
| |
| /** |
| * Gets list of super classes of this class in ascending order. |
| * |
| * @return list of super classes of this class in ascending order, that is, {@link Object} is always the last element. |
| * @throws ClassNotFoundException if any of the superclasses can't be found. |
| */ |
| public JavaClass[] getSuperClasses() throws ClassNotFoundException { |
| JavaClass clazz = this; |
| final List<JavaClass> allSuperClasses = new ArrayList<>(); |
| final Set<JavaClass> visited = new HashSet<>(); |
| visited.add(this); |
| for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) { |
| if (!visited.add(clazz)) { |
| throw new ClassFormatException(clazz.getClassName()); |
| } |
| allSuperClasses.add(clazz); |
| } |
| return allSuperClasses.toArray(EMPTY_ARRAY); |
| } |
| |
| /** |
| * returns the super class name of this class. In the case that this class is {@link Object}, it will return itself |
| * ({@link Object}). This is probably incorrect but isn't fixed at this time to not break existing clients. |
| * |
| * @return Superclass name. |
| */ |
| public String getSuperclassName() { |
| return superclassName; |
| } |
| |
| /** |
| * Gets the class name index. |
| * |
| * @return Class name index. |
| */ |
| public int getSuperclassNameIndex() { |
| return superclassNameIndex; |
| } |
| |
| /** |
| * Return value as defined by given BCELComparator strategy. By default return the hash code of the class name. |
| * |
| * @see Object#hashCode() |
| */ |
| @Override |
| public int hashCode() { |
| return bcelComparator.hashCode(this); |
| } |
| |
| /** |
| * Checks if this class is an implementation of interface inter. |
| * |
| * @param inter the interface to check. |
| * @return true, if this class is an implementation of interface inter. |
| * @throws ClassNotFoundException if superclasses or superinterfaces of this class can't be found. |
| */ |
| public boolean implementationOf(final JavaClass inter) throws ClassNotFoundException { |
| if (!inter.isInterface()) { |
| throw new IllegalArgumentException(inter.getClassName() + " is no interface"); |
| } |
| if (equals(inter)) { |
| return true; |
| } |
| final JavaClass[] superInterfaces = getAllInterfaces(); |
| for (final JavaClass superInterface : superInterfaces) { |
| if (superInterface.equals(inter)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Equivalent to runtime "instanceof" operator. |
| * |
| * @param superclass the superclass to check. |
| * @return true if this JavaClass is derived from the super class. |
| * @throws ClassNotFoundException if superclasses or superinterfaces of this object can't be found. |
| */ |
| public final boolean instanceOf(final JavaClass superclass) throws ClassNotFoundException { |
| if (equals(superclass)) { |
| return true; |
| } |
| for (final JavaClass clazz : getSuperClasses()) { |
| if (clazz.equals(superclass)) { |
| return true; |
| } |
| } |
| if (superclass.isInterface()) { |
| return implementationOf(superclass); |
| } |
| return false; |
| } |
| |
| /** |
| * Checks if this class is anonymous. |
| * |
| * @return true if anonymous. |
| * @since 6.0 |
| */ |
| public final boolean isAnonymous() { |
| computeNestedTypeStatus(); |
| return this.isAnonymous; |
| } |
| |
| /** |
| * Checks if this is a class. |
| * |
| * @return true if this is a class. |
| */ |
| public final boolean isClass() { |
| return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0; |
| } |
| |
| /** |
| * Checks if this class is nested. |
| * |
| * @return true if nested. |
| * @since 6.0 |
| */ |
| public final boolean isNested() { |
| computeNestedTypeStatus(); |
| return this.isNested; |
| } |
| |
| /** |
| * Tests whether this class was declared as a record |
| * |
| * @return true if a record attribute is present, false otherwise. |
| * @since 6.9.0 |
| */ |
| public boolean isRecord() { |
| computeIsRecord(); |
| return this.isRecord; |
| } |
| |
| /** |
| * Checks if this is a super class. |
| * |
| * @return true if this is a super class. |
| */ |
| public final boolean isSuper() { |
| return (super.getAccessFlags() & Const.ACC_SUPER) != 0; |
| } |
| |
| /** |
| * Sets the attributes. |
| * |
| * @param attributes the attributes. |
| */ |
| public void setAttributes(final Attribute[] attributes) { |
| this.attributes = attributes != null ? attributes : Attribute.EMPTY_ARRAY; |
| } |
| |
| /** |
| * Sets the class name. |
| * |
| * @param className the class name. |
| */ |
| public void setClassName(final String className) { |
| this.className = className; |
| } |
| |
| /** |
| * Sets the class name index. |
| * |
| * @param classNameIndex the class name index. |
| */ |
| public void setClassNameIndex(final int classNameIndex) { |
| this.classNameIndex = classNameIndex; |
| } |
| |
| /** |
| * Sets the constant pool. |
| * |
| * @param constantPool the constant pool. |
| */ |
| public void setConstantPool(final ConstantPool constantPool) { |
| this.constantPool = constantPool; |
| } |
| |
| /** |
| * Sets the fields. |
| * |
| * @param fields the fields. |
| */ |
| public void setFields(final Field[] fields) { |
| this.fields = fields != null ? fields : Field.EMPTY_ARRAY; |
| } |
| |
| /** |
| * Sets File name of class, aka SourceFile attribute value. |
| * |
| * @param fileName the file name. |
| */ |
| public void setFileName(final String fileName) { |
| this.fileName = fileName; |
| } |
| |
| /** |
| * Sets the interface names. |
| * |
| * @param interfaceNames the interface names. |
| */ |
| public void setInterfaceNames(final String[] interfaceNames) { |
| this.interfaceNames = ArrayUtils.nullToEmpty(interfaceNames); |
| } |
| |
| /** |
| * Sets the interfaces. |
| * |
| * @param interfaces the interfaces. |
| */ |
| public void setInterfaces(final int[] interfaces) { |
| this.interfaces = ArrayUtils.nullToEmpty(interfaces); |
| } |
| |
| /** |
| * Sets the major version. |
| * |
| * @param major the major version. |
| */ |
| public void setMajor(final int major) { |
| this.major = major; |
| } |
| |
| /** |
| * Sets the methods. |
| * |
| * @param methods the methods. |
| */ |
| public void setMethods(final Method[] methods) { |
| this.methods = methods != null ? methods : Method.EMPTY_ARRAY; |
| } |
| |
| /** |
| * Sets the minor version. |
| * |
| * @param minor the minor version. |
| */ |
| public void setMinor(final int minor) { |
| this.minor = minor; |
| } |
| |
| /** |
| * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done. |
| * |
| * @param repository the repository. |
| */ |
| public void setRepository(final org.apache.bcel.util.Repository repository) { // TODO make protected? |
| this.repository = repository; |
| } |
| |
| /** |
| * Sets absolute path to file this class was read from. |
| * |
| * @param sourceFileName the source file name. |
| */ |
| public void setSourceFileName(final String sourceFileName) { |
| this.sourceFileName = sourceFileName; |
| } |
| |
| /** |
| * Sets the superclass name. |
| * |
| * @param superclassName the superclass name. |
| */ |
| public void setSuperclassName(final String superclassName) { |
| this.superclassName = superclassName; |
| } |
| |
| /** |
| * Sets the superclass name index. |
| * |
| * @param superclassNameIndex the superclass name index. |
| */ |
| public void setSuperclassNameIndex(final int superclassNameIndex) { |
| this.superclassNameIndex = superclassNameIndex; |
| } |
| |
| /** |
| * @return String representing class contents. |
| */ |
| @Override |
| public String toString() { |
| String access = Utility.accessToString(super.getAccessFlags(), true); |
| access = access.isEmpty() ? "" : access + " "; |
| final StringBuilder buf = new StringBuilder(128); |
| buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(className).append(" extends ") |
| .append(Utility.compactClassName(superclassName, false)).append('\n'); |
| final int size = interfaces.length; |
| if (size > 0) { |
| buf.append("implements\t\t"); |
| for (int i = 0; i < size; i++) { |
| buf.append(interfaceNames[i]); |
| if (i < size - 1) { |
| buf.append(", "); |
| } |
| } |
| buf.append('\n'); |
| } |
| buf.append("file name\t\t").append(fileName).append('\n'); |
| buf.append("compiled from\t\t").append(sourceFileName).append('\n'); |
| buf.append("compiler version\t").append(major).append(".").append(minor).append('\n'); |
| buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n'); |
| buf.append("constant pool\t\t").append(constantPool.getLength()).append(" entries\n"); |
| buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n"); |
| if (attributes.length > 0) { |
| buf.append("\nAttribute(s):\n"); |
| for (final Attribute attribute : attributes) { |
| buf.append(indent(attribute)); |
| } |
| } |
| final AnnotationEntry[] annotations = getAnnotationEntries(); |
| if (annotations != null && annotations.length > 0) { |
| buf.append("\nAnnotation(s):\n"); |
| for (final AnnotationEntry annotation : annotations) { |
| buf.append(indent(annotation)); |
| } |
| } |
| if (fields.length > 0) { |
| buf.append("\n").append(fields.length).append(" fields:\n"); |
| for (final Field field : fields) { |
| buf.append("\t").append(field).append('\n'); |
| } |
| } |
| if (methods.length > 0) { |
| buf.append("\n").append(methods.length).append(" methods:\n"); |
| for (final Method method : methods) { |
| buf.append("\t").append(method).append('\n'); |
| } |
| } |
| return buf.toString(); |
| } |
| } |
| |