org.aspectj.apache.bcel.classfile.JavaClass.java Source code

Java tutorial

Introduction

Here is the source code for org.aspectj.apache.bcel.classfile.JavaClass.java

Source

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 <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.
     */
    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.
     * 
     * @param v Visitor object
     */
    public void accept(ClassVisitor v) {
        v.visitJavaClass(this);
    }

    /**
     * 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.
     * 
     * @param file_name Output file name
     * @exception IOException
     */
    public void dump(String file_name) throws IOException {
        dump(new File(file_name));
    }

    /**
     * @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.
     */
    public void dump(OutputStream file) throws IOException {
        dump(new DataOutputStream(file));
    }

    /**
     * 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;
    }

    /**
     * @return Class name.
     */
    public String getClassName() {
        return classname;
    }

    /**
     * @return Package name.
     */
    public String getPackageName() {
        return packageName;
    }

    public int getClassNameIndex() {
        return classnameIdx;
    }

    public ConstantPool getConstantPool() {
        return cpool;
    }

    /**
     * @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;
    }

    /**
     * @return File name of class, aka SourceFile attribute value
     */
    public String getFileName() {
        return fileName;
    }

    /**
     * @return Names of implemented interfaces.
     */
    public String[] getInterfaceNames() {
        return interfacenames;
    }

    /**
     * @return Indices in constant pool of implemented interfaces.
     */
    public int[] getInterfaceIndices() {
        return interfaces;
    }

    public int getMajor() {
        return major;
    }

    /**
     * @return Methods of the class.
     */
    public Method[] getMethods() {
        return methods;
    }

    /**
     * @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;
    }

    /**
     * @return Minor number of class file version.
     */
    public int getMinor() {
        return minor;
    }

    /**
     * @return sbsolute path to file where this class was read from
     */
    public String getSourceFileName() {
        return sourcefileName;
    }

    /**
     * @return Superclass name.
     */
    public String getSuperclassName() {
        return superclassname;
    }

    /**
     * @return Class name index.
     */
    public int getSuperclassNameIndex() {
        return superclassnameIdx;
    }

    /**
     * @param attributes .
     */
    public void setAttributes(Attribute[] attributes) {
        this.attributes = attributes;
        annotationsOutOfDate = true;
    }

    /**
     * @param class_name .
     */
    public void setClassName(String class_name) {
        this.classname = class_name;
    }

    /**
     * @param class_name_index .
     */
    public void setClassNameIndex(int class_name_index) {
        this.classnameIdx = class_name_index;
    }

    /**
     * @param constant_pool .
     */
    public void setConstantPool(ConstantPool constant_pool) {
        this.cpool = constant_pool;
    }

    /**
     * @param fields .
     */
    public void setFields(Field[] fields) {
        this.fields = fields;
    }

    /**
     * Set File name of class, aka SourceFile attribute value
     */
    public void setFileName(String file_name) {
        this.fileName = file_name;
    }

    /**
     * @param interface_names .
     */
    public void setInterfaceNames(String[] interface_names) {
        this.interfacenames = interface_names;
    }

    /**
     * @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.
     */
    public void setSourceFileName(String source_file_name) {
        this.sourcefileName = source_file_name;
    }

    /**
     * @param superclass_name .
     */
    public void setSuperclassName(String superclass_name) {
        this.superclassname = superclass_name;
    }

    /**
     * @param superclass_name_index .
     */
    public void setSuperclassNameIndex(int superclass_name_index) {
        this.superclassnameIdx = superclass_name_index;
    }

    /**
     * @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
     */
    public final boolean isAnnotation() {
        return (modifiers & Constants.ACC_ANNOTATION) != 0;
    }

    /**
     * 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();
     */
    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.
     */
    public void setRepository(org.aspectj.apache.bcel.util.Repository repository) {
        this.repository = repository;
    }

    /**
     * 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;
    }

    /**
     * @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;
    }

    /**
     * @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;
        }
    }

    /**
     * @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.
     */
    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).
     */
    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. "<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;
    }

}