2 * Copyright 2004-2010 Brian S O'Neill
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.cojen.classfile;
19 import java.io.Externalizable;
20 import java.io.IOException;
21 import java.io.ObjectInput;
22 import java.io.ObjectOutput;
23 import java.io.ObjectStreamException;
24 import java.io.Serializable;
25 import java.lang.reflect.Method;
26 import java.util.ArrayList;
27 import java.util.List;
29 import org.cojen.util.WeakCanonicalSet;
32 * This class is used to build method descriptor strings as
33 * defined in <i>The Java Virtual Machine Specification</i>, section 4.3.3.
34 * MethodDesc instances are canonicalized and therefore "==" comparable.
36 * @author Brian S O'Neill
38 public class MethodDesc extends Descriptor implements Serializable {
39 private static final TypeDesc[] EMPTY_PARAMS = new TypeDesc[0];
41 // MethodDesc and TypeDesc can share the same instance cache.
42 private final static WeakCanonicalSet<Descriptor> cInstances = TypeDesc.cInstances;
44 static MethodDesc intern(MethodDesc desc) {
45 return cInstances.put(desc);
49 * Acquire a MethodDesc from a set of arguments.
50 * @param ret return type of method; null implies void
51 * @param params parameters to method; null implies none
53 public static MethodDesc forArguments(TypeDesc ret, TypeDesc[] params) {
57 if (params == null || params.length == 0) {
58 params = EMPTY_PARAMS;
60 return intern(new MethodDesc(ret, params));
64 * Acquire a MethodDesc from a type descriptor. This syntax is described in
65 * section 4.3.3, Method Descriptors.
67 public static MethodDesc forDescriptor(String desc)
68 throws IllegalArgumentException
74 if ((c = desc.charAt(cursor++)) != '(') {
75 throw invalidDescriptor(desc);
78 StringBuffer buf = new StringBuffer();
79 List<TypeDesc> list = new ArrayList<TypeDesc>();
81 while ((c = desc.charAt(cursor++)) != ')') {
103 c = desc.charAt(cursor++);
107 throw invalidDescriptor(desc);
110 list.add(TypeDesc.forDescriptor(buf.toString()));
114 TypeDesc ret = TypeDesc.forDescriptor(desc.substring(cursor));
116 TypeDesc[] tds = list.toArray(new TypeDesc[list.size()]);
118 return intern(new MethodDesc(desc, ret, tds));
119 } catch (NullPointerException e) {
120 throw invalidDescriptor(desc);
121 } catch (IndexOutOfBoundsException e) {
122 throw invalidDescriptor(desc);
126 public static MethodDesc forMethod(Method method) {
127 Class<?>[] paramClasses = method.getParameterTypes();
128 TypeDesc[] paramTypes;
129 if (paramClasses == null || paramClasses.length == 0) {
130 paramTypes = EMPTY_PARAMS;
132 paramTypes = new TypeDesc[paramClasses.length];
133 for (int i=paramClasses.length; --i>=0; ) {
134 paramTypes[i] = TypeDesc.forClass(paramClasses[i]);
137 return forArguments(TypeDesc.forClass(method.getReturnType()), paramTypes);
140 private static IllegalArgumentException invalidDescriptor(String desc) {
141 return new IllegalArgumentException("Invalid descriptor: " + desc);
144 private transient final String mDescriptor;
145 private transient final TypeDesc mRetType;
146 private transient final TypeDesc[] mParams;
148 private MethodDesc(TypeDesc ret, TypeDesc[] params) {
149 mDescriptor = generateDescriptor(ret, params);
154 private MethodDesc(String desc, TypeDesc ret, TypeDesc[] params) {
161 * Returns a method descriptor string, excluding generics.
163 public String getDescriptor() {
168 * Returns a method descriptor string, including any generics.
170 //public abstract String getGenericDescriptor();
173 * Returns the described return type, which is TypeDesc.VOID if void.
175 public TypeDesc getReturnType() {
179 public int getParameterCount() {
180 return mParams.length;
183 public TypeDesc[] getParameterTypes() {
184 TypeDesc[] params = mParams;
185 return (params != EMPTY_PARAMS) ? (TypeDesc[])params.clone() : params;
189 * Returns this in Java method signature syntax.
191 * @param name method name
193 public String toMethodSignature(String name) {
194 return toMethodSignature(name, false);
198 * Returns this in Java method signature syntax.
200 * @param name method name
201 * @param varargs request that the last argument, if it is an array, to
202 * be formatted in varargs syntax.
204 public String toMethodSignature(String name, boolean varargs) {
205 StringBuffer buf = new StringBuffer();
206 buf.append(mRetType.getFullName());
211 TypeDesc[] params = mParams;
212 for (int i=0; i<params.length; i++) {
216 TypeDesc param = params[i];
217 if (varargs && param.isArray() && i == (params.length - 1)) {
218 buf.append(param.getComponentType().getFullName());
221 buf.append(param.getFullName());
225 return buf.append(')').toString();
228 public String toString() {
229 // TODO: Return generic descriptor
233 public int hashCode() {
234 return mDescriptor.hashCode();
237 public boolean equals(Object other) {
241 if (other instanceof MethodDesc) {
242 return ((MethodDesc)other).mDescriptor.equals(mDescriptor);
247 Object writeReplace() throws ObjectStreamException {
248 return new External(mDescriptor);
251 private static String generateDescriptor(TypeDesc ret, TypeDesc[] params) {
252 int length = ret.getDescriptor().length() + 2;
253 int paramsLength = params.length;
254 for (int i=paramsLength; --i >=0; ) {
255 length += params[i].getDescriptor().length();
257 char[] buf = new char[length];
261 for (int i=0; i<paramsLength; i++) {
262 paramDesc = params[i].getDescriptor();
263 int paramDescLength = paramDesc.length();
264 paramDesc.getChars(0, paramDescLength, buf, index);
265 index += paramDescLength;
268 paramDesc = ret.getDescriptor();
269 paramDesc.getChars(0, paramDesc.length(), buf, index);
270 return new String(buf);
273 private static class External implements Externalizable {
274 private String mDescriptor;
279 public External(String desc) {
283 public void writeExternal(ObjectOutput out) throws IOException {
284 out.writeUTF(mDescriptor);
287 public void readExternal(ObjectInput in) throws IOException {
288 mDescriptor = in.readUTF();
291 public Object readResolve() throws ObjectStreamException {
292 return forDescriptor(mDescriptor);