alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 * ClassFile.java
 *
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 2000-2001 Sun
 * Microsystems, Inc. All Rights Reserved.
 *
 * Contributor(s): Thomas Ball
 *
 * Version: $Revision: 1.19 $
 */

package org.netbeans.modules.classfile;

import java.io.*;
import java.util.*;

/**
 * Class representing a Java class file.
 *
 * @author Thomas Ball
 */
public class ClassFile {

    ConstantPool constantPool; 
    int classAccess;
    CPClassInfo classInfo;
    CPClassInfo superClassInfo;
    CPClassInfo[] interfaces;
    Variable[] variables;
    Method[] methods;
    String sourceFileName;
    InnerClass[] innerClasses;
    private AttributeMap attributes;
    private Map annotations;
    short majorVersion;
    short minorVersion;
    String typeSignature;
    EnclosingMethod enclosingMethod;
    private boolean includeCode = false;
    private byte[] laterBytes;
    
    /** size of buffer in buffered input streams */
    private static final int BUFFER_SIZE = 4096;
    
    /**
     * Create a new ClassFile object.
     * @param classData   an InputStream from which the defining bytes of this
     *                    class or interface are read.
     * @throws IOException if InputStream can't be read, or if the class data
     *         is malformed.
     */
    public ClassFile(InputStream classData) throws IOException {
	this(classData, true);
    }
    
    /**
     * Create a new ClassFile object.
     * @param classFileName the path of a class file.
     * @throws IOException if file cannot be opened or read.
     **/
    public ClassFile(String classFileName) throws IOException {
	this(classFileName, true);
    }
    
    /**
     * Create a new ClassFile object.
     * @param file a File instance of a class file.
     * @param includeCode true if this classfile should support operations
     *                    at the bytecode level.  Specify false to conserve
     *                    memory if code access isn't needed.
     * @throws IOException if file cannot be opened or read.
     **/
    public ClassFile(File file, boolean  includeCode) throws IOException {
	InputStream is = null;
	this.includeCode = includeCode;
        if( file == null || !file.exists() )
            throw new IOException("File name is invalid or file not exists");
        try {
            is = new BufferedInputStream( new FileInputStream( file ), BUFFER_SIZE);
            load(is);
        } catch (IOException e) {
            throw new IOException("Invalid classfile format in " + file.getName());
        } finally {
            if (is != null)
                is.close();
        }                
    }

    /**
     * Create a new ClassFile object.
     * @param classData  an InputStream from which the defining bytes of this
     * class or interface are read.
     * @param includeCode true if this classfile should support operations
     *                    at the bytecode level.  Specify false to conserve
     *                    memory if code access isn't needed.
     * @throws IOException if InputStream can't be read, or if the class data
     * is malformed.
     */
    public ClassFile(InputStream classData, boolean includeCode) throws IOException {
        if (classData == null)
            throw new IOException("input stream not specified");
	this.includeCode = includeCode;
        load(classData);
    }
    
    /**
     * Create a new ClassFile object.
     * @param classFileName the path of a class file.
     * @param includeCode true if this classfile should support operations
     *                    at the bytecode level.  Specify false to conserve
     *                    memory if code access isn't needed.
     * @throws IOException if file cannot be opened or read.
     **/
    public ClassFile(String classFileName, boolean includeCode) throws IOException {
        InputStream in = null;
	this.includeCode = includeCode;
        try {
            if (classFileName == null)
                throw new IOException("input stream not specified");
            in = new BufferedInputStream(new FileInputStream(classFileName), BUFFER_SIZE);
            load(in);
        } catch (IOException e) {
            throw new IOException("Invalid classfile format in " + classFileName);
        } finally {
            if (in != null)
                in.close();
        }
    }
    
    
    /** Returns the ConstantPool object associated with this ClassFile.
     * @return the constant pool object
     */    
    public final ConstantPool getConstantPool() {
        return constantPool;
    }

    private void load(InputStream classData) throws IOException {
        try {
            DataInputStream in = new DataInputStream(classData);
            if (in == null)
                throw new IOException("invalid class format");
            constantPool = loadClassHeader(in);
            interfaces = getCPClassList(in, constantPool);

	    // Cache remaining bytes to delay loading.
	    ByteArrayOutputStream baos = new ByteArrayOutputStream();
	    byte[] buf = new byte[2048];
	    int c;
	    while ((c = in.read(buf)) > 0)
		baos.write(buf, 0, c);
	    laterBytes = baos.toByteArray();
        } catch (IOException ioe) {
	    throw new IOException(makeErrorMsg(ioe));
        }
    }

    private void finishLoading() {
	if (laterBytes == null)
	    return;
        try {
	    DataInputStream in = 
		new DataInputStream(new ByteArrayInputStream(laterBytes));
            variables = Variable.loadFields(in, constantPool, this);
            methods = Method.loadMethods(in, constantPool, this, includeCode);
            attributes = AttributeMap.load(in, constantPool);
        } catch (IOException ioe) {
	    throw new InternalError(makeErrorMsg(ioe));
        } finally {
	    laterBytes = null;
	}
    }

    private String makeErrorMsg(IOException ioe) {
	StringBuffer sb = new StringBuffer("invalid class format");
	if (sourceFileName != null) {
	    sb.append(": ");
	    sb.append(sourceFileName);
	}
	sb.append('(');
	sb.append(ioe.getMessage());
	sb.append(')');
	return sb.toString();
    }

    private ConstantPool loadClassHeader(DataInputStream in) throws IOException {
        int magic = in.readInt();
        if (magic != 0xCAFEBABE) {
            throw new IOException("invalid class format");
        }
            
        minorVersion = in.readShort();
        majorVersion = in.readShort();
        int count = in.readUnsignedShort();
        ConstantPool pool = new ConstantPool(count, in);
        classAccess = in.readUnsignedShort();
        classInfo = pool.getClass(in.readUnsignedShort());
        if (classInfo == null)
            throw new IOException("invalid class format");
        int index = in.readUnsignedShort();
        if (index != 0) // true for java.lang.Object
            superClassInfo = pool.getClass(index);
        return pool;
    }

    static CPClassInfo[] getCPClassList(DataInputStream in, ConstantPool pool)
      throws IOException {
        int count = in.readUnsignedShort();
        CPClassInfo[] classes = new CPClassInfo[count];
        for (int i = 0; i < count; i++) {
            classes[i] = pool.getClass(in.readUnsignedShort());
        }
        return classes;
    }
    
    /**
     * Returns the access permissions of this class or interface.
     * @return a mask of access flags.
     * @see org.netbeans.modules.classfile.Access
     */
    public final int getAccess() {
        return classAccess;
    }
    
    /** Returns the name of this class.
     * @return the name of this class.
     */
    public final ClassName getName() {
        return classInfo.getClassName();
    }

    /** Returns the name of this class's superclass.  A string is returned
     * instead of a ClassFile object to reduce object creation.
     * @return the name of the superclass of this class.
     */    
    public final ClassName getSuperClass() {
        if (superClassInfo == null)
            return null;
	return superClassInfo.getClassName();
    }
    
    /**
     * @return a collection of Strings describing this class's interfaces.
     */    
    public final Collection getInterfaces() {
        List l = new ArrayList();
        int n = interfaces.length;
        for (int i = 0; i < n; i++)
            l.add(interfaces[i].getClassName());
        return l;
    }
    
    /**
     * Looks up a variable by its name.
     *
     * NOTE: this method only looks up variables defined by this class,
     * and not inherited from its superclass.
     *
     * @param name the name of the variable
     * @return the variable,or null if no such variable in this class.
     */
    public final Variable getVariable(String name) {
	finishLoading();
        int n = variables.length;
        for (int i = 0; i < n; i++) {
            Variable v = variables[i];
            if (v.getName().equals(name))
                return v;
        }
        return null;
    }
    
    /**
     * @return a Collection of Variable objects representing the fields 
     *         defined by this class.
     */    
    public final Collection getVariables() {
	finishLoading();
        return Arrays.asList(variables);
    }

    /**
     * @return the number of variables defined by this class.
     */    
    public final int getVariableCount() {
	finishLoading();
        return variables.length;
    }
    
    /**
     * Looks up a method by its name and type signature, as defined
     * by the Java Virtual Machine Specification, section 4.3.3.
     *
     * NOTE: this method only looks up methods defined by this class,
     * and not methods inherited from its superclass.
     *
     * @param name the name of the method
     * @param signature the method's type signature
     * @return the method, or null if no such method in this class.
     */
    public final Method getMethod(String name, String signature) {
	finishLoading();
        int n = methods.length;
        for (int i = 0; i < n; i++) {
            Method m = methods[i];
            if (m.getName().equals(name) && m.getDescriptor().equals(signature))
                return m;
        }
        return null;
    }
    
    /**
     * @return a Collection of Method objects representing the methods 
     *         defined by this class.
     */    
    public final Collection getMethods() {
	finishLoading();
        return Arrays.asList(methods);
    }
    
    /**
     * @return the number of methods defined by this class.
     */    
    public final int getMethodCount() {
	finishLoading();
        return methods.length;
    }
    
    /**
     * @return the name of the source file the compiler used to create this class.
     */    
    public final String getSourceFileName() {
	finishLoading();
	if (sourceFileName == null) {
	    DataInputStream in = attributes.getStream("SourceFile"); // NOI18N
	    if (in != null) {
		try {
		    int ipool = in.readUnsignedShort();
		    CPUTF8Info entry = (CPUTF8Info)constantPool.get(ipool);
		    sourceFileName = entry.getName();
		    in.close();
		} catch (IOException e) {
		    System.err.println("invalid SourceFile attribute");
		}
	    }
	}
        return sourceFileName;
    }
    
    public final boolean isDeprecated() {
	finishLoading();
	return attributes.get("Deprecated") != null;
    }

    public final boolean isSynthetic() {
	finishLoading();
        return (classAccess & Access.SYNTHETIC) == Access.SYNTHETIC ||
	    attributes.get("Synthetic") != null;
    }


    /**
     * Returns true if this class is an annotation type.
     */
    public final boolean isAnnotation() {
	return (classAccess & Access.ANNOTATION) == Access.ANNOTATION;
    }
            
    /**
     * Returns true if this class defines an enum type.
     */
    public final boolean isEnum() {
	return (classAccess & Access.ENUM) == Access.ENUM;
    }

    /**
     * Returns a map of the raw attributes for this classfile.  
     * Field attributes are
     * not returned in this map.
     *
     * @see org.netbeans.modules.classfile.Field#getAttributes
     */
    public final AttributeMap getAttributes(){
	finishLoading();
        return attributes;
    }
    
    public final Collection getInnerClasses(){
	finishLoading();
	if (innerClasses == null) {
	    DataInputStream in = attributes.getStream("InnerClasses"); // NOI18N
	    if (in != null) {
		try {
		    innerClasses = 
			InnerClass.loadInnerClasses(in, constantPool);
		    in.close();
		} catch (IOException e) {
		    System.err.println("invalid InnerClasses attribute");
		}
	    } else
		innerClasses = new InnerClass[0];
	}
        return Arrays.asList(innerClasses);
    }

    /**
     * Returns the major version number of this classfile.
     */
    public int getMajorVersion() {
	return majorVersion;
    }

    /**
     * Returns the minor version number of this classfile.
     */
    public int getMinorVersion() {
	return minorVersion;
    }

    /**
     * Returns the generic type information associated with this class.  
     * If this class does not have generic type information, then null 
     * is returned.
     */
    public String getTypeSignature() {
	finishLoading();
	if (typeSignature == null) {
	    DataInputStream in = attributes.getStream("Signature"); // NOI18N
	    if (in != null) {
		try {
		    CPUTF8Info entry = 
			(CPUTF8Info)constantPool.get(in.readUnsignedShort());
		    typeSignature = entry.getName();
		    in.close();
		} catch (IOException e) {
		    System.err.println("invalid Signature attribute");
		}
	    }
	}
	return typeSignature;
    }

    /**
     * Returns the enclosing method for this class.  A class will have an
     * enclosing class if and only if it is a local class or an anonymous
     * class, and has been compiled with a compiler target level of 1.5 
     * or above.  If no such attribute is present in the classfile, then
     * null is returned.
     */
    public EnclosingMethod getEnclosingMethod() {
	finishLoading();
	if (enclosingMethod == null) {
	    DataInputStream in = 
		attributes.getStream("EnclosingMethod"); // NOI18N
	    if (in != null) {
		try {
		    int classIndex = in.readUnsignedShort();
		    int natIndex = in.readUnsignedShort();
		    CPEntry classInfo = constantPool.get(classIndex);
		    if (classInfo.getTag() == ConstantPool.CONSTANT_Class)
			enclosingMethod = 
			    new EnclosingMethod(constantPool, 
						(CPClassInfo)classInfo, 
						natIndex);
		    else
			; // JDK 1.5 beta1 bug
		    in.close();
		} catch (IOException e) {
		    System.err.println("invalid EnclosingMethod attribute");
		}
	    }
	}
	return enclosingMethod;
    }

    private void loadAnnotations() {
	finishLoading();
	if (annotations == null)
	    annotations = buildAnnotationMap(constantPool, attributes);
    }

    static Map buildAnnotationMap(ConstantPool pool, AttributeMap attrs) {
	Map annotations = new HashMap(2);
	DataInputStream in = 
	    attrs.getStream("RuntimeVisibleAnnotations"); //NOI18N
	if (in != null) {
	    try {
		Annotation.load(in, pool, true, annotations);
		in.close();
	    } catch (IOException e) {
		System.err.println("invalid RuntimeVisibleAnnotations attribute");
	    }
	}
	in = attrs.getStream("RuntimeInvisibleAnnotations"); //NOI18N
	if (in != null) {
	    try {
		Annotation.load(in, pool, false, annotations);
		in.close();
	    } catch (IOException e) {
		System.err.println("invalid RuntimeInvisibleAnnotations attribute");
	    }
	}
	return annotations;
    }

    /**
     * Returns all runtime annotations defined for this class.  Inherited
     * annotations are not included in this collection.
     */
    public final Collection getAnnotations() {
	loadAnnotations();
	return annotations.values();
    }

    /**
     * Returns the annotation for a specified annotation type, or null if
     * no annotation of that type exists for this class.
     */
    public final Annotation getAnnotation(final ClassName annotationClass) {
	loadAnnotations();
	return (Annotation)annotations.get(annotationClass);
    }
    
    /**
     * Returns true if an annotation of the specified type is defined for
     * this class.
     */
    public final boolean isAnnotationPresent(final ClassName annotationClass) {
	loadAnnotations();
	return annotations.get(annotationClass) != null;
    }
    
    /* Return the collection of all unique class references in this class.
     *
     * @return a Set of ClassNames specifying the referenced classnames.
     */
    public final Set getAllClassNames() {
        Set set = new HashSet();

        // include all class name constants from constant pool
        Collection c = constantPool.getAllConstants(CPClassInfo.class);
        for (Iterator i = c.iterator(); i.hasNext();) {
            CPClassInfo ci = (CPClassInfo)i.next();
            set.add(ci.getClassName());
        }

	// scan variables and methods for other class references
	// (inner classes will caught above)
	for (int i = 0; i < variables.length; i++)
	    addClassNames(set, variables[i].getDescriptor());
	for (int i = 0; i < methods.length; i++)
	    addClassNames(set, methods[i].getDescriptor());

        return Collections.unmodifiableSet(set);
    }

    private void addClassNames(Set set, String type) {
        int i = 0;
        while ((i = type.indexOf('L', i)) != -1) {
            int j = type.indexOf(';', i);
            if (j > i) {
		// get name, minus leading 'L' and trailing ';'
                String classType = type.substring(i + 1, j);
		set.add(ClassName.getClassName(classType));
                i = j + 1;
            } else
		break;
        }
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("ClassFile: "); //NOI18N
        sb.append(Access.toString(classAccess));
        sb.append(' ');
        sb.append(classInfo);
        if (isSynthetic())
            sb.append(" (synthetic)"); //NOI18N
        if (isDeprecated())
            sb.append(" (deprecated)"); //NOI18N
        sb.append("\n   source: "); //NOI18N
        sb.append(getSourceFileName());
        sb.append("\n   super: "); //NOI18N
        sb.append(superClassInfo);
	if (getTypeSignature() != null) {
	    sb.append("\n   signature: "); //NOI18N
	    sb.append(typeSignature);
	}
	if (getEnclosingMethod() != null) {
	    sb.append("\n   enclosing method: "); //NOI18N
	    sb.append(enclosingMethod);
	}
        sb.append("\n   ");
	loadAnnotations();
	if (annotations.size() > 0) {
	    Iterator iter = annotations.values().iterator();
	    sb.append("annotations: ");
	    while (iter.hasNext()) {
                sb.append("\n      ");
		sb.append(iter.next().toString());
	    }
	    sb.append("\n   ");
	}
        if (interfaces.length > 0) {
            sb.append(arrayToString("interfaces", interfaces)); //NOI18N
            sb.append("\n   ");
        }
        if (getInnerClasses().size() > 0) {
            sb.append(arrayToString("innerclasses", innerClasses)); //NOI18N
            sb.append("\n   ");
        }
        if (variables.length > 0) {
            sb.append(arrayToString("variables", variables)); //NOI18N
            sb.append("\n   ");
        }
        if (methods.length > 0)
            sb.append(arrayToString("methods", methods)); //NOI18N
        return sb.toString();
    }

    private String arrayToString(String name, Object[] array) {
        StringBuffer sb = new StringBuffer();
        sb.append(name);
        sb.append(": ");
        int n = array.length;
        if (n > 0) {
            int i = 0;
            do {
                sb.append("\n      ");
                sb.append(array[i++].toString());
            } while (i < n);
        } else
            sb.append("none"); //NOI18N
        return sb.toString();
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.