package org.aspectj.apache.bcel.classfile;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache BCEL" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache BCEL", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

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.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.StringTokenizer;

import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnos;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.apache.bcel.util.SyntheticRepository;

Represents a Java class, i.e., the data structures, constant pool, fields, methods and commands contained in a Java .class file. See JVM specification for details. The intent of this class is to represent a parsed or otherwise existing class file. Those interested in programatically generating classes should see the ClassGen class.
Author:M. Dahm
See Also:
Version:$Id: JavaClass.java,v 1.22 2009/09/15 19:40:14 aclement Exp $
/** * Represents a Java class, i.e., the data structures, constant pool, fields, methods and commands contained in a Java .class file. * See <a href="ftp://java.sun.com/docs/specs/">JVM specification</a> for details. * * The intent of this class is to represent a parsed or otherwise existing class file. Those interested in programatically * generating classes should see the <a href="../generic/ClassGen.html">ClassGen</a> class. * * @version $Id: JavaClass.java,v 1.22 2009/09/15 19:40:14 aclement Exp $ * @see org.aspectj.apache.bcel.generic.ClassGen * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> */
public class JavaClass extends Modifiers implements Cloneable, Node { private static final String[] NoInterfaceNames = new String[0]; private static final Field[] NoFields = new Field[0]; private static final Method[] NoMethod = new Method[0]; private static final int[] NoInterfaceIndices = new int[0]; private static final Attribute[] NoAttributes = new Attribute[0]; private String fileName; private String packageName; private String sourcefileName; private int classnameIdx; private int superclassnameIdx; private String classname; private String superclassname; private int major, minor; private ConstantPool cpool; private int[] interfaces; private String[] interfacenames; private Field[] fields; private Method[] methods; private Attribute[] attributes; private AnnotationGen[] annotations; private boolean isGeneric = false; private boolean isAnonymous = false; private boolean isNested = false; private boolean computedNestedTypeStatus = false; // Annotations are collected from certain attributes, don't do it more than necessary! private boolean annotationsOutOfDate = true; // state for dealing with generic signature string private String signatureAttributeString = null; private Signature signatureAttribute = null; private boolean searchedForSignatureAttribute = false;
In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any better.
/** * In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any better. */
private transient org.aspectj.apache.bcel.util.Repository repository = null; public JavaClass(int classnameIndex, int superclassnameIndex, String filename, int major, int minor, int access_flags, ConstantPool cpool, int[] interfaces, Field[] fields, Method[] methods, Attribute[] attributes) { if (interfaces == null) { interfaces = NoInterfaceIndices; } this.classnameIdx = classnameIndex; this.superclassnameIdx = superclassnameIndex; this.fileName = filename; this.major = major; this.minor = minor; this.modifiers = access_flags; this.cpool = cpool; this.interfaces = interfaces; this.fields = (fields == null ? NoFields : fields); this.methods = (methods == null ? NoMethod : methods); this.attributes = (attributes == null ? NoAttributes : attributes); annotationsOutOfDate = true; // Get source file name if available SourceFile sfAttribute = AttributeUtils.getSourceFileAttribute(attributes); sourcefileName = sfAttribute == null ? "<Unknown>" : sfAttribute.getSourceFileName(); /* * According to the specification the following entries must be of type `ConstantClass' but we check that anyway via the * `ConstPool.getConstant' method. */ classname = cpool.getConstantString(classnameIndex, Constants.CONSTANT_Class); classname = Utility.compactClassName(classname, false); 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 = cpool.getConstantString(superclassnameIndex, Constants.CONSTANT_Class); superclassname = Utility.compactClassName(superclassname, false); } else { superclassname = "java.lang.Object"; } if (interfaces.length == 0) { interfacenames = NoInterfaceNames; } else { interfacenames = new String[interfaces.length]; for (int i = 0; i < interfaces.length; i++) { String str = cpool.getConstantString(interfaces[i], Constants.CONSTANT_Class); interfacenames[i] = Utility.compactClassName(str, false); } } }
Called by objects that are traversing the nodes of the tree implicitely defined by the contents of a Java class. I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
Params:
  • v – Visitor object
/** * Called by objects that are traversing the nodes of the tree implicitely 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 */
public void accept(ClassVisitor v) { v.visitJavaClass(this); }
Dump class to a file.
Params:
  • file – Output file
Throws:
/** * Dump class to a file. * * @param file Output file * @throws IOException */
public void dump(File file) throws IOException { String parent = file.getParent(); if (parent != null) { File dir = new File(parent); dir.mkdirs(); } dump(new DataOutputStream(new FileOutputStream(file))); }
Dump class to a file named file_name.
Params:
  • file_name – Output file name
Throws:
/** * Dump class to a file named file_name. * * @param file_name Output file name * @exception IOException */
public void dump(String file_name) throws IOException { dump(new File(file_name)); }
Returns:class in binary format
/** * @return class in binary format */
public byte[] getBytes() { ByteArrayOutputStream s = new ByteArrayOutputStream(); DataOutputStream ds = new DataOutputStream(s); try { dump(ds); } catch (IOException e) { e.printStackTrace(); } finally { try { ds.close(); } catch (IOException e2) { e2.printStackTrace(); } } return s.toByteArray(); }
Dump Java class to output stream in binary format.
/** * Dump Java class to output stream in binary format. */
public void dump(OutputStream file) throws IOException { dump(new DataOutputStream(file)); }
Dump Java class to output stream in binary format.
/** * Dump Java class to output stream in binary format. */
public void dump(DataOutputStream file) throws IOException { file.writeInt(0xcafebabe); file.writeShort(minor); file.writeShort(major); cpool.dump(file); file.writeShort(modifiers); file.writeShort(classnameIdx); file.writeShort(superclassnameIdx); file.writeShort(interfaces.length); for (int i = 0; i < interfaces.length; i++) { file.writeShort(interfaces[i]); } file.writeShort(fields.length); for (int i = 0; i < fields.length; i++) { fields[i].dump(file); } file.writeShort(methods.length); for (int i = 0; i < methods.length; i++) { methods[i].dump(file); } AttributeUtils.writeAttributes(attributes, file); file.close(); } public Attribute[] getAttributes() { return attributes; } public AnnotationGen[] getAnnotations() { if (annotationsOutOfDate) { // Find attributes that contain annotation data List<AnnotationGen> accumulatedAnnotations = new ArrayList<AnnotationGen>(); for (int i = 0; i < attributes.length; i++) { Attribute attribute = attributes[i]; if (attribute instanceof RuntimeAnnos) { RuntimeAnnos runtimeAnnotations = (RuntimeAnnos) attribute; accumulatedAnnotations.addAll(runtimeAnnotations.getAnnotations()); } } annotations = accumulatedAnnotations.toArray(new AnnotationGen[] {}); annotationsOutOfDate = false; } return annotations; }
Returns:Class name.
/** * @return Class name. */
public String getClassName() { return classname; }
Returns:Package name.
/** * @return Package name. */
public String getPackageName() { return packageName; } public int getClassNameIndex() { return classnameIdx; } public ConstantPool getConstantPool() { return cpool; }
Returns:Fields, i.e., 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.
/** * @return Fields, i.e., 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; }
Returns:File name of class, aka SourceFile attribute value
/** * @return File name of class, aka SourceFile attribute value */
public String getFileName() { return fileName; }
Returns:Names of implemented interfaces.
/** * @return Names of implemented interfaces. */
public String[] getInterfaceNames() { return interfacenames; }
Returns:Indices in constant pool of implemented interfaces.
/** * @return Indices in constant pool of implemented interfaces. */
public int[] getInterfaceIndices() { return interfaces; } public int getMajor() { return major; }
Returns:Methods of the class.
/** * @return Methods of the class. */
public Method[] getMethods() { return methods; }
Returns:A org.aspectj.apache.bcel.classfile.Method corresponding to java.lang.reflect.Method if any
/** * @return A org.aspectj.apache.bcel.classfile.Method corresponding to java.lang.reflect.Method if any */
public Method getMethod(java.lang.reflect.Method m) { for (int i = 0; i < methods.length; i++) { Method method = methods[i]; if (m.getName().equals(method.getName()) && m.getModifiers() == method.getModifiers() && Type.getSignature(m).equals(method.getSignature())) { return method; } } return null; } public Method getMethod(java.lang.reflect.Constructor<?> c) { for (int i = 0; i < methods.length; i++) { Method method = methods[i]; if (method.getName().equals("<init>") && c.getModifiers() == method.getModifiers() && Type.getSignature(c).equals(method.getSignature())) { return method; } } return null; } public Field getField(java.lang.reflect.Field field) { String fieldName = field.getName(); for (Field f : fields) { if (f.getName().equals(fieldName)) { return f; } } return null; }
Returns:Minor number of class file version.
/** * @return Minor number of class file version. */
public int getMinor() { return minor; }
Returns:sbsolute path to file where this class was read from
/** * @return sbsolute path to file where this class was read from */
public String getSourceFileName() { return sourcefileName; }
Returns:Superclass name.
/** * @return Superclass name. */
public String getSuperclassName() { return superclassname; }
Returns:Class name index.
/** * @return Class name index. */
public int getSuperclassNameIndex() { return superclassnameIdx; }
Params:
  • attributes – .
/** * @param attributes . */
public void setAttributes(Attribute[] attributes) { this.attributes = attributes; annotationsOutOfDate = true; }
Params:
  • class_name – .
/** * @param class_name . */
public void setClassName(String class_name) { this.classname = class_name; }
Params:
  • class_name_index – .
/** * @param class_name_index . */
public void setClassNameIndex(int class_name_index) { this.classnameIdx = class_name_index; }
Params:
  • constant_pool – .
/** * @param constant_pool . */
public void setConstantPool(ConstantPool constant_pool) { this.cpool = constant_pool; }
Params:
  • fields – .
/** * @param fields . */
public void setFields(Field[] fields) { this.fields = fields; }
Set File name of class, aka SourceFile attribute value
/** * Set File name of class, aka SourceFile attribute value */
public void setFileName(String file_name) { this.fileName = file_name; }
Params:
  • interface_names – .
/** * @param interface_names . */
public void setInterfaceNames(String[] interface_names) { this.interfacenames = interface_names; }
Params:
  • interfaces – .
/** * @param interfaces . */
public void setInterfaces(int[] interfaces) { this.interfaces = interfaces; } public void setMajor(int major) { this.major = major; } public void setMethods(Method[] methods) { this.methods = methods; } public void setMinor(int minor) { this.minor = minor; }
Set absolute path to file this class was read from.
/** * Set absolute path to file this class was read from. */
public void setSourceFileName(String source_file_name) { this.sourcefileName = source_file_name; }
Params:
  • superclass_name – .
/** * @param superclass_name . */
public void setSuperclassName(String superclass_name) { this.superclassname = superclass_name; }
Params:
  • superclass_name_index – .
/** * @param superclass_name_index . */
public void setSuperclassNameIndex(int superclass_name_index) { this.superclassnameIdx = superclass_name_index; }
Returns:String representing class contents.
/** * @return String representing class contents. */
@Override public String toString() { String access = Utility.accessToString(modifiers, true); access = access.equals("") ? "" : access + " "; StringBuffer buf = new StringBuffer(access + Utility.classOrInterface(modifiers) + " " + classname + " extends " + Utility.compactClassName(superclassname, false) + '\n'); 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("filename\t\t" + fileName + '\n'); buf.append("compiled from\t\t" + sourcefileName + '\n'); buf.append("compiler version\t" + major + "." + minor + '\n'); buf.append("access flags\t\t" + modifiers + '\n'); buf.append("constant pool\t\t" + cpool.getLength() + " entries\n"); buf.append("ACC_SUPER flag\t\t" + isSuper() + "\n"); if (attributes.length > 0) { buf.append("\nAttribute(s):\n"); for (int i = 0; i < attributes.length; i++) { buf.append(indent(attributes[i])); } } if (annotations != null && annotations.length > 0) { buf.append("\nAnnotation(s):\n"); for (int i = 0; i < annotations.length; i++) { buf.append(indent(annotations[i])); } } if (fields.length > 0) { buf.append("\n" + fields.length + " fields:\n"); for (int i = 0; i < fields.length; i++) { buf.append("\t" + fields[i] + '\n'); } } if (methods.length > 0) { buf.append("\n" + methods.length + " methods:\n"); for (int i = 0; i < methods.length; i++) { buf.append("\t" + methods[i] + '\n'); } } return buf.toString(); } private static final String indent(Object obj) { StringTokenizer tok = new StringTokenizer(obj.toString(), "\n"); StringBuffer buf = new StringBuffer(); while (tok.hasMoreTokens()) { buf.append("\t" + tok.nextToken() + "\n"); } return buf.toString(); } public final boolean isSuper() { return (modifiers & Constants.ACC_SUPER) != 0; } public final boolean isClass() { return (modifiers & Constants.ACC_INTERFACE) == 0; } public final boolean isAnonymous() { computeNestedTypeStatus(); return this.isAnonymous; } public final boolean isNested() { computeNestedTypeStatus(); return this.isNested; } private final void computeNestedTypeStatus() { if (computedNestedTypeStatus) { return; } // Attribute[] attrs = attributes.getAttributes(); for (int i = 0; i < attributes.length; i++) { if (attributes[i] instanceof InnerClasses) { InnerClass[] innerClasses = ((InnerClasses) attributes[i]).getInnerClasses(); for (int j = 0; j < innerClasses.length; j++) { boolean innerClassAttributeRefersToMe = false; String inner_class_name = cpool.getConstantString(innerClasses[j].getInnerClassIndex(), Constants.CONSTANT_Class); inner_class_name = Utility.compactClassName(inner_class_name); if (inner_class_name.equals(getClassName())) { innerClassAttributeRefersToMe = true; } if (innerClassAttributeRefersToMe) { this.isNested = true; if (innerClasses[j].getInnerNameIndex() == 0) { this.isAnonymous = true; } } } } } this.computedNestedTypeStatus = true; } // J5SUPPORT:
Returns true if this class represents an annotation, i.e. it was a 'public @interface blahblah' declaration
/** * Returns true if this class represents an annotation, i.e. it was a 'public @interface blahblah' declaration */
public final boolean isAnnotation() { return (modifiers & Constants.ACC_ANNOTATION) != 0; }
Returns true if this class represents an enum type
/** * Returns true if this class represents an enum type */
public final boolean isEnum() { return (modifiers & Constants.ACC_ENUM) != 0; } /********************* New repository functionality *********************/
Gets the ClassRepository which holds its definition. By default this is the same as SyntheticRepository.getInstance();
/** * Gets the ClassRepository which holds its definition. By default this is the same as SyntheticRepository.getInstance(); */
public org.aspectj.apache.bcel.util.Repository getRepository() { if (repository == null) { repository = SyntheticRepository.getInstance(); } return repository; }
Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done.
/** * Sets the ClassRepository which loaded the JavaClass. Should be called immediately after parsing is done. */
public void setRepository(org.aspectj.apache.bcel.util.Repository repository) { this.repository = repository; }
Equivalent to runtime "instanceof" operator.
Returns:true if this JavaClass is derived from teh super class
/** * Equivalent to runtime "instanceof" operator. * * @return true if this JavaClass is derived from teh super class */
public final boolean instanceOf(JavaClass super_class) { if (this.equals(super_class)) { return true; } JavaClass[] super_classes = getSuperClasses(); for (int i = 0; i < super_classes.length; i++) { if (super_classes[i].equals(super_class)) { return true; } } if (super_class.isInterface()) { return implementationOf(super_class); } return false; }
Returns:true, if clazz is an implementation of interface inter
/** * @return true, if clazz is an implementation of interface inter */
public boolean implementationOf(JavaClass inter) { if (!inter.isInterface()) { throw new IllegalArgumentException(inter.getClassName() + " is no interface"); } if (this.equals(inter)) { return true; } Collection<JavaClass> superInterfaces = getAllInterfaces(); for (JavaClass superInterface : superInterfaces) { if (superInterface.equals(inter)) { return true; } } // for (int i = 0; i < super_interfaces.length; i++) { // if (super_interfaces[i].equals(inter)) { // return true; // } // } return false; }
Returns:the superclass for this JavaClass object, or null if this is java.lang.Object
/** * @return the superclass for this JavaClass object, or null if this is java.lang.Object */
public JavaClass getSuperClass() { if ("java.lang.Object".equals(getClassName())) { return null; } try { return getRepository().loadClass(getSuperclassName()); } catch (ClassNotFoundException e) { System.err.println(e); return null; } }
Returns:list of super classes of this class in ascending order, i.e., java.lang.Object is always the last element
/** * @return list of super classes of this class in ascending order, i.e., java.lang.Object is always the last element */
public JavaClass[] getSuperClasses() { JavaClass clazz = this; List<JavaClass> vec = new ArrayList<JavaClass>(); for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) { vec.add(clazz); } return vec.toArray(new JavaClass[vec.size()]); }
Get interfaces directly implemented by this JavaClass.
/** * Get interfaces directly implemented by this JavaClass. */
public JavaClass[] getInterfaces() { String[] interfaces = getInterfaceNames(); JavaClass[] classes = new JavaClass[interfaces.length]; try { for (int i = 0; i < interfaces.length; i++) { classes[i] = getRepository().loadClass(interfaces[i]); } } catch (ClassNotFoundException e) { System.err.println(e); return null; } return classes; }
Get all interfaces implemented by this JavaClass (transitively).
/** * Get all interfaces implemented by this JavaClass (transitively). */
public Collection<JavaClass> getAllInterfaces() { Queue<JavaClass> queue = new LinkedList<JavaClass>(); List<JavaClass> interfaceList = new ArrayList<JavaClass>(); queue.add(this); while (!queue.isEmpty()) { JavaClass clazz = queue.remove(); JavaClass souper = clazz.getSuperClass(); JavaClass[] interfaces = clazz.getInterfaces(); if (clazz.isInterface()) { interfaceList.add(clazz); } else { if (souper != null) { queue.add(souper); } } for (int i = 0; i < interfaces.length; i++) { queue.add(interfaces[i]); } } return interfaceList; // return interfaceList.toArray(new JavaClass[interfaceList.size()]); }
Hunts for a signature attribute on the member and returns its contents. So where the 'regular' signature may be Ljava/util/Vector; the signature attribute will tell us e.g. "Ljava/lang/Object". We can learn the type variable names, their bounds, and the true superclass and superinterface types (including any parameterizations) Coded for performance - searches for the attribute only when requested - only searches for it once.
/** * Hunts for a signature attribute on the member and returns its contents. So where the 'regular' signature may be * Ljava/util/Vector; the signature attribute will tell us e.g. "<E:>Ljava/lang/Object". We can learn the type variable names, * their bounds, and the true superclass and superinterface types (including any parameterizations) Coded for performance - * searches for the attribute only when requested - only searches for it once. */
public final String getGenericSignature() { loadGenericSignatureInfoIfNecessary(); return signatureAttributeString; } public boolean isGeneric() { loadGenericSignatureInfoIfNecessary(); return isGeneric; } private void loadGenericSignatureInfoIfNecessary() { if (!searchedForSignatureAttribute) { signatureAttribute = AttributeUtils.getSignatureAttribute(attributes); signatureAttributeString = signatureAttribute == null ? null : signatureAttribute.getSignature(); isGeneric = signatureAttribute != null && signatureAttributeString.charAt(0) == '<'; searchedForSignatureAttribute = true; } } public final Signature getSignatureAttribute() { loadGenericSignatureInfoIfNecessary(); return signatureAttribute; } }