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

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

Go to download

A library adding code completion and other advanced features for Java, JavaScript, Perl, and other languages to RSyntaxTextArea.

There is a newer version: 3.3.0
Show newest version
/*
 * 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.*;

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


/**
 * Implementation of the "method_info" structure as defined in
 * the JVM specification.
 *
 * @author Robert Futrell
 * @version 1.0
 */
public class MethodInfo extends MemberInfo implements AccessFlags {

	/**
	 * An index into the constant pool of a {@link ConstantUtf8Info} structure
	 * representing either one of the special method names (
	 * "<init>" or "<clinit>") or a
	 * valid method name in the Java programming language, stored as a simple
	 * name.
	 */
	private int nameIndex; // u2

	/**
	 * An index into the constant pool of a {@link ConstantUtf8Info} structure
	 * representing a valid method descriptor.
	 */
	private int descriptorIndex; // u2

	/**
	 * The Signature attribute, or null if there
	 * isn't one for this method.
	 */
	private Signature signatureAttr;

	/**
	 * The Code attribute, or null if this method
	 * is abstract or native.
	 */
	private Code codeAttr;

	/**
	 * All attributes of this method that aren't explicitly covered by the
	 * private members {@link #signatureAttr} and {@link #codeAttr}.
	 */
	private List attributes;

	/**
	 * The type of all parameters to this method.  Note that this cache will
	 * be short-lived, as classes that take type parameters will pass their
	 * type arguments down to individual MethodInfos when doing
	 * completions, to ensure types are as correct as possible. 
	 */
	private String[] paramTypes;

	/**
	 * Cached return type.
	 */
	private String returnType;

	/**
	 * Cached string representing the name and parameters for this method.
	 */
	private String nameAndParameters;

	/**
	 * Used in class files to denote constructors.
	 */
	private static final String SPECIAL_NAME_CONSTRUCTOR	= "";

	public static final String CODE					= "Code";
	public static final String EXCEPTIONS			= "Exceptions";


	/**
	 * Constructor.
	 *
	 * @param cf The class file defining this method.
	 */
	public MethodInfo(ClassFile cf, int accessFlags, int nameIndex,
						int descriptorIndex) {
		super(cf, accessFlags);
		this.nameIndex = nameIndex;
		this.descriptorIndex = descriptorIndex;
		attributes = new ArrayList(1); // Usually only 0 or 1?
	}


	/**
	 * Adds the specified attribute to this field.
	 *
	 * @param info Information about the attribute.
	 */
	private void addAttribute(AttributeInfo info) {
		attributes.add(info);
	}


	private void appendParamDescriptors(StringBuffer sb) {

		String[] paramTypes = getParameterTypes();
		for (int i=0; iMethodInfo to be used for code completion for instances
	 * of the same class initialized with different type arguments.

* * Note that if this method does not have parameterized arguments or * return type, calling this method won't affect its behavior. */ void clearParamTypeInfo() { paramTypes = null; returnType = null; } /** * Creates and returns an array of types of all parameters of this * method. If this method takes any generic type arguments, these * types are grabbed from the parent ClassFile instance, * whose type argument values should have been initialized via * {@link ClassFile#setTypeParamsToTypeArgs(Map)}. * * @return The array of parameter types. * @see #createParamTypesFromDescriptor() * @see #createParamTypesFromTypeSignature() */ private String[] createParamTypes() { String[] types = createParamTypesFromTypeSignature(); if (types==null) { types = createParamTypesFromDescriptor(true); } return types; } /** * Creates an array of types of each parameter by looking at the method's * descriptor field. This technique should work with Java 1.0+, but won't * pick up on generic types added in Java 5. * * @return The parameter types. * @see #createParamTypesFromTypeSignature() */ private String[] createParamTypesFromDescriptor(boolean qualified) { String descriptor = getDescriptor(); int rparen = descriptor.indexOf(')'); String paramDescriptors = descriptor.substring(1, rparen); //String returnDescriptor = descriptor.substring(rparen+1); List paramTypeList = new ArrayList(); String type = null; while (paramDescriptors.length()>0) { // Can't do lastIndexOf() as there may be > 1 array parameter // in the descriptors. // int braceCount = paramDescriptors.lastIndexOf('[') + 1; int braceCount = -1; while (paramDescriptors.charAt(++braceCount) == '['); int pos = braceCount; switch (paramDescriptors.charAt(pos)) { // BaseType case 'B': type = "byte"; pos++; break; case 'C': type = "char"; pos++; break; case 'D': type = "double"; pos++; break; case 'F': type = "float"; pos++; break; case 'I': type = "int"; pos++; break; case 'J': type = "long"; pos++; break; case 'S': type = "short"; pos++; break; case 'Z': type = "boolean"; pos++; break; // ObjectType case 'L': String clazz = paramDescriptors.substring(pos + 1, paramDescriptors.indexOf(';')); type = qualified ? clazz.replace('/', '.') : clazz.substring(clazz.lastIndexOf('/')+1); pos += clazz.length() + 2; // "+2" for the 'L' & semicolon break; // Invalid method descriptor default: String temp = "INVALID_TYPE_" + paramDescriptors; type = temp; pos += paramDescriptors.length(); break; } for (int i = 0; i < braceCount; i++) { type += "[]"; } paramTypeList.add(type); paramDescriptors = paramDescriptors.substring(pos); } String[] types = new String[paramTypeList.size()]; types = (String[])paramTypeList.toArray(types); return types; } /** * Creates an array of types of each parameter by looking at the method's * Signature attribute, and querying the parent * ClassFile instance for any type argument values. This * attribute was introduced in Java 5, and is the only way to detect * generic parameters. * * @return The parameter types. * @see #createParamTypesFromDescriptor() */ private String[] createParamTypesFromTypeSignature() { String[] params = null; if (signatureAttr!=null) { List paramTypes = signatureAttr.getMethodParamTypes(this, cf, false); if (paramTypes!=null) { params = new String[paramTypes.size()]; params = (String[])paramTypes.toArray(params); } } return params; } /** * Returns the specified attribute. * * @param index The index of the attribute. * @return The attribute. */ public AttributeInfo getAttribute(int index) { return (AttributeInfo)attributes.get(index); } /** * Returns the number of attributes of this field. * * @return The number of attributes. */ public int getAttributeCount() { return attributes.size(); } /** * {@inheritDoc} */ public String getDescriptor() { return cf.getUtf8ValueFromConstantPool(descriptorIndex); } /** * {@inheritDoc} */ public String getName() { String name = cf.getUtf8ValueFromConstantPool(nameIndex); if (SPECIAL_NAME_CONSTRUCTOR.equals(name)) { name = cf.getClassName(false); } return name; } /** * Returns the name and parameters of this method, in the form * performAction(String, int, Runnable). * * @return The name and parameters of this method. */ public String getNameAndParameters() { if (nameAndParameters==null) { StringBuffer sb = new StringBuffer(getName()); sb.append('('); int paramCount = getParameterCount(); for (int i=0; inull * is returned. * * @param index The index of the parameter. * @return The name of the parameter, or null. */ public String getParameterName(int index) { if (index>=0 && index-1) { type = type.substring(dot+1); } } return type; } /** * Returns an array if strings representing the types of all parameters * to this method. If this method takes no parameters, a zero-length * array is returned. * * @return The array. These types will likely be fully qualified. * @see #getParameterCount() * @see #getParameterType(int, boolean) */ public String[] getParameterTypes() { if (paramTypes==null) { paramTypes = createParamTypes(); } return (String[])paramTypes.clone(); } /** * Returns the return type of this method. * * @return The return type of this method. */ public String getReturnTypeString(boolean fullyQualified) { if (returnType==null) { returnType = getReturnTypeStringFromTypeSignature(fullyQualified); if (returnType==null) { returnType = getReturnTypeStringFromDescriptor(fullyQualified); } } if(!fullyQualified) { if(returnType != null && returnType.indexOf(".") > -1) { return returnType.substring(returnType.lastIndexOf(".") +1, returnType.length()); } } return returnType; } /** * Returns the return type of this method, as determined by a snippet * of the method descriptor. This should work with all class files * created in Java 1.0+, but won't discover the generic types added in * Java 5. * * @return The return type of this method. * @see #getReturnTypeStringFromTypeSignature() */ /* * TODO: This is identical to FieldInfo.getTypeString(), except for the * 'V' case. It is also very similar to #getParameterTypes(). Try * to refactor common code from these methods. */ private String getReturnTypeStringFromDescriptor(boolean qualified) { String descriptor = getDescriptor(); int rparen = descriptor.indexOf(')'); descriptor = descriptor.substring(rparen+1); // return type desc. StringBuffer sb = new StringBuffer(); int braceCount = descriptor.lastIndexOf('[') + 1; switch (descriptor.charAt(braceCount)) { // BaseType case 'B': sb.append("byte"); break; case 'C': sb.append("char"); break; case 'D': sb.append("double"); break; case 'F': sb.append("float"); break; case 'I': sb.append("int"); break; case 'J': sb.append("long"); break; case 'S': sb.append("short"); break; case 'Z': sb.append("boolean"); break; case 'V': sb.append("void"); break; // ObjectType case 'L': String clazz = descriptor.substring(braceCount+1, descriptor.length()-1); clazz = qualified ? clazz.replace('/', '.') : clazz.substring(clazz.lastIndexOf('/')+1); sb.append(clazz); break; // Invalid field descriptor default: sb.append("UNSUPPORTED_TYPE_").append(descriptor); break; } for (int i=0; iSignature attribute that was added in Java 5. This allows * us to check for generic types. * * @return The return type of this method. * @see #getReturnTypeStringFromDescriptor() */ private String getReturnTypeStringFromTypeSignature(boolean qualified) { String retType = null; if (signatureAttr!=null) { retType = signatureAttr.getMethodReturnType(this, cf, qualified); } return retType; } /** * Returns the signature of this method, as determined from its method * descriptor. * * @return The signature of this method. */ public String getSignature() { StringBuffer sb = new StringBuffer(); // Return type. if (!isConstructor()) { // Don't print "void" return type. sb.append(getReturnTypeString(false)); sb.append(' '); } // Method name and param list. sb.append(getName()); sb.append('('); appendParamDescriptors(sb); sb.append(')'); // "throws" clause. for (int i=0; i0; } /** * Returns whether this method is a constructor. * * @return Whether this method is a constructor. */ public boolean isConstructor() { String name = cf.getUtf8ValueFromConstantPool(nameIndex); return SPECIAL_NAME_CONSTRUCTOR.equals(name); } /** * Returns whether this method is native. * * @return Whether this method is native. */ public boolean isNative() { return (getAccessFlags()&ACC_NATIVE)>0; } /** * Returns whether this method is static. * * @return Whether this method is static. */ public boolean isStatic() { return (getAccessFlags()&ACC_STATIC)>0; } /** * Reads a MethodInfo from an input stream. * * @param cf The class file defining the method. * @param in The input stream to read from. * @return The method information read. * @throws IOException If an IO error occurs. */ public static MethodInfo read(ClassFile cf, DataInputStream in) throws IOException { int accessFlags = in.readUnsignedShort(); int nameIndex = in.readUnsignedShort(); int descriptorIndex = in.readUnsignedShort(); MethodInfo mi = new MethodInfo(cf, accessFlags, nameIndex, descriptorIndex); int attrCount = in.readUnsignedShort(); for (int j=0; jnull if it was known * to be unimportant for our purposes. * @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 = cf.getUtf8ValueFromConstantPool(attributeNameIndex); if (CODE.equals(attrName)) { // 4.7.3 ai = Code.read(this, in); } else if (EXCEPTIONS.equals(attrName)) { // 4.7.4 int exceptionCount = in.readUnsignedShort(); int[] exceptionIndexTable = null; if (exceptionCount>0) { exceptionIndexTable = new int[exceptionCount]; for (int i=0; i





© 2015 - 2024 Weber Informatics LLC | Privacy Policy