/* * Copyright 2004-2010 Brian S O'Neill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.cojen.classfile; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.ObjectStreamException; import java.io.Serializable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.cojen.util.WeakCanonicalSet; /** * This class is used to build method descriptor strings as * defined in The Java Virtual Machine Specification, section 4.3.3. * MethodDesc instances are canonicalized and therefore "==" comparable. * * @author Brian S O'Neill */ public class MethodDesc extends Descriptor implements Serializable { private static final TypeDesc[] EMPTY_PARAMS = new TypeDesc[0]; // MethodDesc and TypeDesc can share the same instance cache. private final static WeakCanonicalSet cInstances = TypeDesc.cInstances; static MethodDesc intern(MethodDesc desc) { return cInstances.put(desc); } /** * Acquire a MethodDesc from a set of arguments. * @param ret return type of method; null implies void * @param params parameters to method; null implies none */ public static MethodDesc forArguments(TypeDesc ret, TypeDesc[] params) { if (ret == null) { ret = TypeDesc.VOID; } if (params == null || params.length == 0) { params = EMPTY_PARAMS; } return intern(new MethodDesc(ret, params)); } /** * Acquire a MethodDesc from a type descriptor. This syntax is described in * section 4.3.3, Method Descriptors. */ public static MethodDesc forDescriptor(String desc) throws IllegalArgumentException { try { int cursor = 0; char c; if ((c = desc.charAt(cursor++)) != '(') { throw invalidDescriptor(desc); } StringBuffer buf = new StringBuffer(); List list = new ArrayList(); while ((c = desc.charAt(cursor++)) != ')') { switch (c) { case 'V': case 'I': case 'C': case 'Z': case 'D': case 'F': case 'J': case 'B': case 'S': buf.append(c); break; case '[': buf.append(c); continue; case 'L': while (true) { buf.append(c); if (c == ';') { break; } c = desc.charAt(cursor++); } break; default: throw invalidDescriptor(desc); } list.add(TypeDesc.forDescriptor(buf.toString())); buf.setLength(0); } TypeDesc ret = TypeDesc.forDescriptor(desc.substring(cursor)); TypeDesc[] tds = list.toArray(new TypeDesc[list.size()]); return intern(new MethodDesc(desc, ret, tds)); } catch (NullPointerException e) { throw invalidDescriptor(desc); } catch (IndexOutOfBoundsException e) { throw invalidDescriptor(desc); } } public static MethodDesc forMethod(Method method) { Class[] paramClasses = method.getParameterTypes(); TypeDesc[] paramTypes; if (paramClasses == null || paramClasses.length == 0) { paramTypes = EMPTY_PARAMS; } else { paramTypes = new TypeDesc[paramClasses.length]; for (int i=paramClasses.length; --i>=0; ) { paramTypes[i] = TypeDesc.forClass(paramClasses[i]); } } return forArguments(TypeDesc.forClass(method.getReturnType()), paramTypes); } private static IllegalArgumentException invalidDescriptor(String desc) { return new IllegalArgumentException("Invalid descriptor: " + desc); } private transient final String mDescriptor; private transient final TypeDesc mRetType; private transient final TypeDesc[] mParams; private MethodDesc(TypeDesc ret, TypeDesc[] params) { mDescriptor = generateDescriptor(ret, params); mRetType = ret; mParams = params; } private MethodDesc(String desc, TypeDesc ret, TypeDesc[] params) { mDescriptor = desc; mRetType = ret; mParams = params; } /** * Returns a method descriptor string, excluding generics. */ public String getDescriptor() { return mDescriptor; } /** * Returns a method descriptor string, including any generics. */ //public abstract String getGenericDescriptor(); /** * Returns the described return type, which is TypeDesc.VOID if void. */ public TypeDesc getReturnType() { return mRetType; } public int getParameterCount() { return mParams.length; } public TypeDesc[] getParameterTypes() { TypeDesc[] params = mParams; return (params != EMPTY_PARAMS) ? (TypeDesc[])params.clone() : params; } /** * Returns this in Java method signature syntax. * * @param name method name */ public String toMethodSignature(String name) { return toMethodSignature(name, false); } /** * Returns this in Java method signature syntax. * * @param name method name * @param varargs request that the last argument, if it is an array, to * be formatted in varargs syntax. */ public String toMethodSignature(String name, boolean varargs) { StringBuffer buf = new StringBuffer(); buf.append(mRetType.getFullName()); buf.append(' '); buf.append(name); buf.append('('); TypeDesc[] params = mParams; for (int i=0; i 0) { buf.append(", "); } TypeDesc param = params[i]; if (varargs && param.isArray() && i == (params.length - 1)) { buf.append(param.getComponentType().getFullName()); buf.append("..."); } else { buf.append(param.getFullName()); } } return buf.append(')').toString(); } public String toString() { // TODO: Return generic descriptor return mDescriptor; } public int hashCode() { return mDescriptor.hashCode(); } public boolean equals(Object other) { if (this == other) { return true; } if (other instanceof MethodDesc) { return ((MethodDesc)other).mDescriptor.equals(mDescriptor); } return false; } Object writeReplace() throws ObjectStreamException { return new External(mDescriptor); } private static String generateDescriptor(TypeDesc ret, TypeDesc[] params) { int length = ret.getDescriptor().length() + 2; int paramsLength = params.length; for (int i=paramsLength; --i >=0; ) { length += params[i].getDescriptor().length(); } char[] buf = new char[length]; buf[0] = '('; int index = 1; String paramDesc; for (int i=0; i