All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.rsta.ac.java.classreader.ClassFile Maven / Gradle / Ivy

/*
 * 03/21/2010
 *
 * Copyright (C) 2010 Robert Futrell
 * robert_futrell at users.sourceforge.net
 * http://fifesoft.com/rsyntaxtextarea
 *
 * This library is distributed under a modified BSD license.  See the included
 * RSTALanguageSupport.License.txt file for details.
 */
package org.fife.rsta.ac.java.classreader;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.fife.rsta.ac.java.classreader.attributes.*;
import org.fife.rsta.ac.java.classreader.constantpool.*;


/**
 * Class representing a ClassFile structure.
 *
 * @author Robert Futrell
 * @version 1.0
 */
public class ClassFile implements AccessFlags {

	private static final boolean DEBUG = false;

	/**
	 * The class file's minor version number.
	 */
	private int minorVersion; // u2

	/**
	 * The class file's major version number.
	 */
	private int majorVersion; // u2

	/**
	 * Constant pool infos.
	 */
	private ConstantPoolInfo[] constantPool; // Length constant_pool_count-1 !!

	/**
	 * Permissions and properties of this class or interface.
	 */
	private int accessFlags; // u2

	/**
	 * Index into {@link #constantPool} for a ConstantClassInfo
	 * structure representing the class or interface defined in this class file.
	 */
	private int thisClass; // u2

	/**
	 * Index into {@link #constantPool} for a ConstantClassInfo
	 * structure representing the superclass of this class or interface.  If
	 * this value is 0 then this class must be class
	 * java.lang.Object.  If this is an interface, then this index
	 * must point to information about class java.lang.Object.
	 */
	private int superClass; // u2

	/**
	 * Indices into {@code constantPool} for ConstantClassInfos
	 * representing the implemented interfaces of this class or interface.
	 */
	int[] interfaces; // u2[]

	/**
	 * Structures giving complete descriptions of the fields in this class
	 * or interface.
	 */
	private FieldInfo[] fields;

	/**
	 * Structures giving complete descriptions of the methods in this class or
	 * interface.
	 */
	private MethodInfo[] methods;

	/**
	 * Whether this class is deprecated.
	 */
	private boolean deprecated;

	/**
	 * Attributes of this class or interface.
	 */
	private AttributeInfo[] attributes;

	/**
	 * Parameter types, such as "String" in List<String>.
	 */
	private List paramTypes;

	/**
	 * A mapping of type parameters to type arguments.  This is set via
	 * {@link #setTypeParamsToTypeArgs(Map)} during code completion of members of an
	 * instance variable whose type is represented by this class file.  This
	 * ClassFile doesn't use this field itself; rather, it's there
	 * for consumers (such as the Java code completion API) to use.
	 */
	private Map typeMap;

	public static final String DEPRECATED			= "Deprecated";
	public static final String ENCLOSING_METHOD		= "EnclosingMethod";
	public static final String INNER_CLASSES		= "InnerClasses";
	public static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
	public static final String SIGNATURE			= "Signature";
	public static final String SOURCE_FILE			= "SourceFile";
	public static final String BOOTSTRAP_METHODS    = "BootstrapMethods";

	/**
	 * The 4-byte class file header, "CAFEBABE".
	 */
	private static final byte[] HEADER	= { (byte)0xCA, (byte)0xFE,
											(byte)0xBA, (byte)0xBE };


	public ClassFile(File classFile) throws IOException {
        try (DataInputStream in = new DataInputStream(new BufferedInputStream(
                new FileInputStream(classFile)))) {
            init(in);
        }
	}


	public ClassFile(DataInputStream in) throws IOException {
		init(in);
	}


	private void debugPrint(String text) {
		if (DEBUG) {
			System.out.println(text);
		}
	}


	/**
	 * Returns the access flags for this class or interface.
	 *
	 * @return The access flags, as a bit field.
	 * @see AccessFlags
	 */
	public int getAccessFlags() {
		return accessFlags;
	}


	/**
	 * Returns the specified attribute of this class file.
	 *
	 * @param index The index of the attribute.
	 * @return The attribute.
	 * @see #getAttributeCount()
	 */
	public AttributeInfo getAttribute(int index) {
		return attributes[index];
	}


	/**
	 * Returns the number of attributes of this class file.
	 *
	 * @return The number of attributes.
	 * @see #getAttribute(int)
	 */
	public int getAttributeCount() {
		return attributes==null ? 0 : attributes.length;
	}


	/**
	 * Returns the name of this class or interface.
	 *
	 * @param fullyQualified Whether the name should be fully-qualified.
	 * @return The name of this class or interface.
	 * @see #getSuperClassName(boolean)
	 */
	public String getClassName(boolean fullyQualified) {
		return getClassNameFromConstantPool(thisClass, fullyQualified);
	}


	/**
	 * Given an index into the constant pool of a {@link ConstantClassInfo},
	 * this method returns the fully-qualified name of the class it points to.
	 *
	 * @param cpIndex The index into the constant pool.  Note that
	 *        this value is 1-based.
	 * @param fullyQualified Whether the returned class name should be fully
	 *        qualified.
	 * @return The fully-qualified class or interface name.
	 */
	protected String getClassNameFromConstantPool(int cpIndex,
												boolean fullyQualified) {

		ConstantPoolInfo cpi = getConstantPoolInfo(cpIndex);

		if (cpi instanceof ConstantClassInfo) {
			ConstantClassInfo cci = (ConstantClassInfo)cpi;
			int index = cci.getNameIndex();
			ConstantUtf8Info cui = (ConstantUtf8Info)getConstantPoolInfo(index);
			String className = cui.getRepresentedString(false);
			if (fullyQualified) {
				className = className.replace('/', '.');
			}
			else {
				className = className.substring(className.lastIndexOf('/')+1);
			}
			return className.replace('$', '.');
		}

		// cpi is never null
		throw new InternalError("Expected ConstantClassInfo, found " +
								cpi.getClass().toString());

	}


	/**
	 * Returns the size of the constant pool, plus 1.
	 *
	 * @return The size of the constant pool, plus 1.
	 * @see #getConstantPoolInfo(int)
	 */
	public int getConstantPoolCount() {
		return constantPool.length + 1;
	}


	/**
	 * Returns the constant pool entry at the specified index.  Note that
	 * constant pool entries are 1-based (that is, valid indices
	 * are 1 - getConstantPoolCount()-1).
	 *
	 * @param index The index into the constant pool to retrieve.
	 * @return The constant pool entry, or null if
	 *         index is 0 (e.g. this
	 *         ClassFile object represents
	 *         java.lang.Object).
	 * @see #getConstantPoolCount()
	 */
	public ConstantPoolInfo getConstantPoolInfo(int index) {
		return index!=0 ? constantPool[index-1] : null;
	}


	/**
	 * Returns the number of fields declared in this class file.
	 *
	 * @return The number of fields.
	 * @see #getFieldInfo(int)
	 */
	public int getFieldCount() {
		return fields==null ? 0 : fields.length;
	}


	/**
	 * Returns the specified field's information.
	 *
	 * @param index The index of the field info.
	 * @return The field's information.
	 * @see #getFieldCount()
	 * @see #getFieldInfoByName(String)
	 */
	public FieldInfo getFieldInfo(int index) {
		return fields[index];
	}


	/**
	 * Returns a field's information by name.
	 *
	 * @param name The name of the field.
	 * @return The field's information.
	 * @see #getFieldCount()
	 * @see #getFieldInfo(int)
	 */
	public FieldInfo getFieldInfoByName(String name) {
		for (int i=0; inull
	 *         if none.  This is a list of {@link MethodInfo}s.
	 * @see #getMethodInfoByName(String, int)
	 */
	public List getMethodInfoByName(String name) {
		return getMethodInfoByName(name, -1);
	}


	/**
	 * Returns all method overloads with the specified name and number of
	 * arguments.
	 *
	 * @param name The method name.
	 * @param argCount The number of arguments.  If this is less than zero,
	 *        all overloads will be returned, regardless of argument count.
	 * @return Any method overloads with the given name and argument count, or
	 *         null if none.  This is a list of
	 *         {@link MethodInfo}s.
	 * @see #getMethodInfoByName(String)
	 */
	public List getMethodInfoByName(String name, int argCount) {
		List methods = null;
		for (int i=0; i(1); // Usually just 1
					}
					methods.add(info);
				}
			}
		}
		return methods;
	}


	/**
	 * Returns the package for this class or interface.
	 *
	 * @return The package, or null if this class or interface
	 *         is not in a package.
	 * @see #getClassName(boolean)
	 */
	public String getPackageName() {
		String className = getClassName(true);
		int dot = className.lastIndexOf('.');
		return dot==-1 ? null : className.substring(0, dot);
	}


	public List getParamTypes() {
		return paramTypes;
	}


	/**
	 * Returns the fully-qualified name of the superclass of this class or
	 * interface.
	 *
	 * @param fullyQualified Whether the returned value should be fully
	 *        qualified.
	 * @return The name of the superclass of this class or interface.  If this
	 *         is an interface, then "java.lang.Object" is
	 *         returned.  If this class file represents
	 *         java.lang.Object, then null is
	 *         returned.
	 * @see #getClassName(boolean)
	 */
	public String getSuperClassName(boolean fullyQualified) {
		if (superClass==0) { // This is java.lang.Object
			return null;
		}
		return getClassNameFromConstantPool(superClass, fullyQualified);
	}


	/**
	 * Returns the currently set type argument for the specified type parameter.
	 *
	 * @param typeParam The type parameter.
	 * @return The type argument, or "Object" if no type
	 *         parameters have been set.  This is because, if the user types,
	 *         say, "java.util.List list;" in Java 5+, the
	 *         type defaults to Object.  The code completion API
	 *         may set the type argument mapping to null if no
	 *         type arguments are scanned, thus we need to return
	 *         Object in this case.
	 * @see #setTypeParamsToTypeArgs(Map)
	 */
	public String getTypeArgument(String typeParam) {
		// If no type arguments are specified for a class that's supposed to
		// have them (according to calling code), return "Object", as Java
		// assumes this.
		return typeMap==null ? "Object" : typeMap.get(typeParam);
	}


	/**
	 * Returns the string value represented by a ConstantUtf8Info
	 * entry in the constant pool.
	 *
	 * @param index The index into the constant pool of a
	 *        ConstantUtf8Info structure.  This should be
	 *        1-based.
	 * @return The string represented.
	 */
	public String getUtf8ValueFromConstantPool(int index) {
		ConstantPoolInfo cpi = getConstantPoolInfo(index);
		ConstantUtf8Info cui = (ConstantUtf8Info)cpi;
		return cui.getRepresentedString(false);
	}


	/**
	 * Returns the version number of this class, as a string.
	 *
	 * @return The class's version number, in the form
	 *         major.minor.
	 */
	public String getVersionString() {
		return majorVersion + "." + minorVersion;
	}


	/**
	 * Parses the class file from a given input stream.
	 *
	 * @param in The input stream to read from.
	 * @throws IOException If an error occurs reading the class file.
	 */
	private void init(DataInputStream in) throws IOException {
		readHeader(in);
		readVersion(in);
		readConstantPoolInfos(in);
		readAccessFlags(in);
		readThisClass(in);
		readSuperClass(in);
		readInterfaces(in);
		readFields(in);
		readMethods(in);
		readAttributes(in);
	}


	/**
	 * Returns whether this class is deprecated.
	 *
	 * @return Whether this class is deprecated.
	 */
	public boolean isDeprecated() {
		return deprecated;
	}


	/**
	 * Reads this class or interface's access flags.
	 *
	 * @param in The input stream to read from.
	 * @throws IOException If an error occurs reading the access flags.
	 */
	private void readAccessFlags(DataInputStream in) throws IOException {
		accessFlags = in.readUnsignedShort();
		debugPrint("Access flags: " + accessFlags);
	}


	/**
	 * Reads a single attribute of this class file.
	 *
	 * @param in The input stream to read from.
	 * @return The attribute.
	 * @throws IOException If an IO error occurs.
	 */
	private AttributeInfo readAttribute(DataInputStream in) throws IOException {

		AttributeInfo ai = null;

		int attributeNameIndex = in.readUnsignedShort();
		int attributeLength = in.readInt();

		String attrName = getUtf8ValueFromConstantPool(attributeNameIndex);
		debugPrint("Found class attribute: " + attrName);

		if (SOURCE_FILE.equals(attrName)) { // 4.7.7
			int sourceFileIndex = in.readUnsignedShort();
            ai = new SourceFile(this, sourceFileIndex);
		}

		else if (BOOTSTRAP_METHODS.equals(attrName)) { // 4.7.23
            //String name = getClassFile().getClassName(false) + "." + getName();
            //System.out.println(name + ": Attribute " + attrName + " not supported");
            Util.skipBytes(in, attributeLength);
            //ai = null;
        }

		else if (SIGNATURE.equals(attrName)) { // 4.8.8
			int signatureIndex = in.readUnsignedShort();
			String sig = getUtf8ValueFromConstantPool(signatureIndex);
			//System.out.println("... Signature: " + sig);
			ai = new Signature(this, sig);
			paramTypes = ((Signature)ai).getClassParamTypes();
		}

		else if (INNER_CLASSES.equals(attrName)) { // 4.8.5
			//String name = getClassName(false) + "." + getName();
			//System.out.println(name + ": Attribute " + attrName + " not supported");
			Util.skipBytes(in, attributeLength);
			//ai = null;
		}

		else if (ENCLOSING_METHOD.equals(attrName)) { // 4.8.6
			//String name = getClassName(false) + "." + getName();
			//System.out.println(name + ": Attribute " + attrName + " not supported");
			Util.skipBytes(in, attributeLength); // 2 u2's, class_index and method_index
			//ai = null;
		}

		else if (DEPRECATED.equals(attrName)) { // 4.7.10
			// No need to read anything else, attributeLength==0
			deprecated = true;
		}

		else if (RUNTIME_VISIBLE_ANNOTATIONS.equals(attrName)) { // 4.8.15
			//String name = getClassFile().getClassName(false) + "." + getName();
			//System.out.println(name + ": Attribute " + attrName + " not supported");
			Util.skipBytes(in, attributeLength);
			//ai = null;
		}

		// TODO: Handle other useful Attribute types, if any.

		else { // An unknown/unsupported attribute.
			debugPrint("Unsupported class attribute: "+  attrName);
			ai = AttributeInfo.readUnsupportedAttribute(this, in, attrName,
					attributeLength);
		}

		return ai;

	}


	/**
	 * Reads this class file's attributes.
	 *
	 * @param in The input stream to read from.
	 * @throws IOException If an IO error occurs.
	 */
	private void readAttributes(DataInputStream in) throws IOException {
		int attributeCount = in.readUnsignedShort();
		if (attributeCount>0) {
			attributes = new AttributeInfo[attributeCount];
			for (int i=0; i0) {
			fields = new FieldInfo[fieldCount];
			for (int i=0; i0xCAFEBABE class file header.
	 *
	 * @param in The input stream to read from.
	 * @throws IOException If the header is invalid.
	 */
	private void readHeader(DataInputStream in) throws IOException {
        for (byte b1 : HEADER) {
            byte b = in.readByte();
            if (b != b1) {
                throw new IOException("\"CAFEBABE\" header not found");
            }
        }
	}


	/**
	 * Reads the array of indices into the constant pool for the names of the
	 * interfaces implemented by this class or interface.
	 *
	 * @param in The input stream to read from.
	 * @throws IOException If an IO error occurs reading the input stream.
	 */
	private void readInterfaces(DataInputStream in) throws IOException {
		int interfaceCount = in.readUnsignedShort();
		if (interfaceCount>0) {
			interfaces = new int[interfaceCount];
			for (int i=0; i0) {
			methods = new MethodInfo[methodCount];
			for (int i=0; iClassFile
	 * does not directly use this field; it is there for code completion API's
	 * to use to extract the necessary types of arguments, return values, etc.,
	 * of methods (see the {@link MethodInfo} class).
	 *
	 * @param typeMap A mapping of type parameters to type arguments (both
	 *        Strings).
	 * @see #getTypeArgument(String)
	 */
	public void setTypeParamsToTypeArgs(Map typeMap) {
		this.typeMap = typeMap;
		for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy