* 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.util.ArrayList;
import org.fife.rsta.ac.java.classreader.attributes.*;
import org.fife.rsta.ac.java.classreader.constantpool.*;
* Class representing a <code>ClassFile</code> structure.
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
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 <code>ConstantClassInfo</code>
* structure representing the class or interface defined in this class file.
private int thisClass; // u2
* Index into {@link #constantPool} for a <code>ConstantClassInfo</code>
* structure representing the superclass of this class or interface. If
* this value is <code>0</code> then this class must be class
* <code>java.lang.Object</code>. If this is an interface, then this index
* must point to information about class <code>java.lang.Object</code>.
private int superClass; // u2
* Indices into {@link #constantPool} for <code>ConstantClassInfo</code>s
* representing the implemented interfaces of this class or interface.
int[] interfaces; // u2[]
* Structures giving complete descriptions of the fields in this class
private FieldInfo[] fields;
* Structures giving complete descriptions of the methods in this class or
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 <code>List<String></code>.
* 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
* <code>ClassFile</code> doesn't use this field itself; rather, it's there
* for consumers (such as the Java code completion API) to use.
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";
* The 4-byte class file header, "<code>CAFEBABE</code>".
private static final byte[] HEADER = { (byte)0xCA, (byte)0xFE,
(byte)0xBA, (byte)0xBE };
public ClassFile(File classFile) throws IOException {
DataInputStream in = new DataInputStream(new BufferedInputStream(
new FileInputStream(classFile)));
public ClassFile(DataInputStream in) throws IOException {
private void debugPrint(String text) {
System.out.println(text);
* Returns the access flags for this class or interface.
* @return The access flags, as a bit field.
public int getAccessFlags() {
* Returns the specified attribute of this class file.
* @param index The index of 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 <code>1</code>-based.
* @param fullyQualified Whether the returned class name should be fully
* @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);
className = className.replace('/', '.');
className = className.substring(className.lastIndexOf('/')+1);
return className.replace('$', '.');
throw new InternalError("Expected ConstantClassInfo, found " +
cpi.getClass().toString());
* Returns the size of the constant pool, plus <code>1</code>.
* @return The size of the constant pool, plus <code>1</code>.
* @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 <code>1</code>-based (that is, valid indices
* are <code>1 - getConstantPoolCount()-1</code>).
* @param index The index into the constant pool to retrieve.
* @return The constant pool entry, or <code>null</code> if
* <code>index</code> is <code>0</code> (e.g. this
* <code>ClassFile</code> object represents
* <code>java.lang.Object</code>).
* @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 #getFieldInfoByName(String)
public FieldInfo getFieldInfo(int index) {
* Returns a field's information by name.
* @param name The name of the field.
* @return The field's information.
* @see #getFieldInfo(int)
public FieldInfo getFieldInfoByName(String name) {
for (int i=0; i<getFieldCount(); i++) {
if (name.equals(fields[i].getName())) {
* Returns the number of interfaces this class or interface implements.
* @return The number of implemented interfaces.
* @see #getImplementedInterfaceName(int, boolean)
public int getImplementedInterfaceCount() {
return interfaces==null ? 0 : interfaces.length;
* Returns the specified interface implemented by this class or
* @param index The index of the interface.
* @param fullyQualified Whether the returned interface name should be
* @return The interface name.
* @see #getImplementedInterfaceCount()
public String getImplementedInterfaceName(int index,
boolean fullyQualified) {
return getClassNameFromConstantPool(interfaces[index], fullyQualified);
* Returns the number of methods defined/declared in this class or
* @return The number of methods.
* @see #getMethodInfo(int)
public int getMethodCount() {
return methods==null ? 0 : methods.length;
* Returns information about the specified method defined/declared in
* @param index The index of the method.
* @return Information about the method.
public MethodInfo getMethodInfo(int index) {
* Returns all method overloads with the specified name.
* @param name The method name.
* @return Any method overloads with the given name, or <code>null</code>
* 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
* @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
* <code>null</code> if none. This is a list of
* @see #getMethodInfoByName(String)
public List getMethodInfoByName(String name, int argCount) {
for (int i=0; i<getMethodCount(); i++) {
MethodInfo info = this.methods[i];
if (name.equals(info.getName())) {
if (argCount<0 || argCount==info.getParameterCount()) {
methods = new ArrayList(1); // Usually just 1
* Returns the package for this class or interface.
* @return The package, or <code>null</code> if this class or interface
* @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() {
* Returns the fully-qualified name of the superclass of this class or
* @param fullyQualified Whether the returned value should be fully
* @return The name of the superclass of this class or interface. If this
* is an interface, then "<code>java.lang.Object</code>" is
* returned. If this class file represents
* <code>java.lang.Object</code>, then <code>null</code> is
* @see #getClassName(boolean)
public String getSuperClassName(boolean fullyQualified) {
if (superClass==0) { // This is java.lang.Object
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 "<code>Object</code>" if no type
* parameters have been set. This is because, if the user types,
* say, "<code>java.util.List list;</code>" in Java 5+, the
* type defaults to <code>Object</code>. The code completion API
* may set the type argument mapping to <code>null</code> if no
* type arguments are scanned, thus we need to return
* <code>Object</code> 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
return typeMap==null ? "Object" : (String)typeMap.get(typeParam);
* Returns the string value represented by a <code>ConstantUtf8Info</code>
* entry in the constant pool.
* @param index The index into the constant pool of a
* <code>ConstantUtf8Info</code> structure. This should be
* @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
* <code>major.minor</code>.
public String getVersionString() {
return majorVersion + "." + minorVersion;
* Parses the class file from a given input stream.
* @throws IOException If an error occurs reading the class file.
private void init(DataInputStream in) throws IOException {
readConstantPoolInfos(in);
* Returns whether this class is deprecated.
* @return Whether this class is deprecated.
public boolean isDeprecated() {
* Reads this class or interface's access flags.
* @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.
* @throws IOException If an IO error occurs.
private AttributeInfo readAttribute(DataInputStream in) throws IOException {
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();
SourceFile sf = new SourceFile(this, sourceFileIndex);
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);
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
else if (DEPRECATED.equals(attrName)) { // 4.7.10
// No need to read anything else, attributeLength==0
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);
// TODO: Handle other useful Attribute types, if any.
else { // An unknown/unsupported attribute.
System.out.println("Unsupported class attribute: "+ attrName);
ai = AttributeInfo.readUnsupportedAttribute(this, in, attrName,
* 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();
attributes = new AttributeInfo[attributeCount];
for (int i=0; i<attributeCount; i++) {
attributes[i] = readAttribute(in);
* Reads the constant pool.
* @throws IOException If an IO error occurs.
private void readConstantPoolInfos(DataInputStream in) throws IOException {
// NOTE: constant_pool_count is 1-based, so there are
// constant_pool_count-1 infos!
int constantPoolCount = in.readUnsignedShort() - 1;
debugPrint("Constant pool count: " + constantPoolCount);
constantPool = new ConstantPoolInfo[constantPoolCount];
for (int i=0; i<constantPoolCount; i++) {
ConstantPoolInfo cpi = ConstantPoolInfoFactory.readConstantPoolInfo(this, in);
// longs and doubles take up 2 entries in the constant pool,
// although the second entry is not used
if (cpi instanceof ConstantLongInfo ||
cpi instanceof ConstantDoubleInfo) {
* Reads the "fields" information.
* @throws IOException If an IO error occurs.
private void readFields(DataInputStream in) throws IOException {
int fieldCount = in.readUnsignedShort();
fields = new FieldInfo[fieldCount];
for (int i=0; i<fieldCount; i++) {
fields[i] = FieldInfo.read(this, in);
debugPrint("fieldCount: " + fieldCount);
* Reads the <code>0xCAFEBABE</code> class file header.
* @throws IOException If the header is invalid.
private void readHeader(DataInputStream in) throws IOException {
for (int i=0; i<HEADER.length; i++) {
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.
* @throws IOException If an IO error occurs reading the input stream.
private void readInterfaces(DataInputStream in) throws IOException {
int interfaceCount = in.readUnsignedShort();
interfaces = new int[interfaceCount];
for (int i=0; i<interfaceCount; i++) {
interfaces[i] = in.readUnsignedShort();
debugPrint("interfaceCount: " + interfaceCount);
private void readMethods(DataInputStream in) throws IOException {
int methodCount = in.readUnsignedShort();
methods = new MethodInfo[methodCount];
for (int i=0; i<methodCount; i++) {
methods[i] = MethodInfo.read(this, in);
private void readSuperClass(DataInputStream in) throws IOException {
superClass = in.readUnsignedShort();
ConstantPoolInfo cpi = getConstantPoolInfo(superClass);
debugPrint("superClass: " + cpi);
private void readThisClass(DataInputStream in) throws IOException {
thisClass = in.readUnsignedShort();
ConstantPoolInfo cpi = getConstantPoolInfo(thisClass);
debugPrint("thisClass: " + cpi);
* Reads the class file's major and minor version numbers.
* @throws IOException If the version numbers are invalid.
private void readVersion(DataInputStream in) throws IOException {
minorVersion = in.readUnsignedShort();
majorVersion = in.readUnsignedShort();
debugPrint("Class file version: " + getVersionString());
* Sets a mapping of type parameters of this class to type arguments for
* a particular instance of this class. Note that <code>ClassFile</code>
* 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
* @see #getTypeArgument(String)
public void setTypeParamsToTypeArgs(Map typeMap) {
for (int i=0; i<getMethodCount(); i++) {
getMethodInfo(i).clearParamTypeInfo();
public String toString() {
"accessFlags=" + accessFlags +