001: /*
002: * Copyright 2004 Brian S O'Neill
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.cojen.classfile;
018:
019: import java.io.Serializable;
020: import java.io.Externalizable;
021: import java.io.ObjectOutput;
022: import java.io.ObjectInput;
023: import java.io.IOException;
024: import java.io.ObjectStreamException;
025: import java.lang.reflect.Method;
026: import java.util.List;
027: import java.util.ArrayList;
028: import org.cojen.util.WeakCanonicalSet;
029:
030: /**
031: * This class is used to build method descriptor strings as
032: * defined in <i>The Java Virtual Machine Specification</i>, section 4.3.3.
033: * MethodDesc instances are canonicalized and therefore "==" comparable.
034: *
035: * @author Brian S O'Neill
036: */
037: public class MethodDesc extends Descriptor implements Serializable {
038: private static final TypeDesc[] EMPTY_PARAMS = new TypeDesc[0];
039:
040: // MethodDesc and TypeDesc can share the same instance cache.
041: private final static WeakCanonicalSet mInstances = TypeDesc.cInstances;
042:
043: static MethodDesc intern(MethodDesc desc) {
044: return (MethodDesc) mInstances.put(desc);
045: }
046:
047: /**
048: * Acquire a MethodDesc from a set of arguments.
049: * @param ret return type of method; null implies void
050: * @param params parameters to method; null implies none
051: */
052: public static MethodDesc forArguments(TypeDesc ret,
053: TypeDesc[] params) {
054: if (ret == null) {
055: ret = TypeDesc.VOID;
056: }
057: if (params == null || params.length == 0) {
058: params = EMPTY_PARAMS;
059: }
060: return intern(new MethodDesc(ret, params));
061: }
062:
063: /**
064: * Acquire a MethodDesc from a type descriptor. This syntax is described in
065: * section 4.3.3, Method Descriptors.
066: */
067: public static MethodDesc forDescriptor(String desc)
068: throws IllegalArgumentException {
069: try {
070: int cursor = 0;
071: char c;
072:
073: if ((c = desc.charAt(cursor++)) != '(') {
074: throw invalidDescriptor(desc);
075: }
076:
077: StringBuffer buf = new StringBuffer();
078: List list = new ArrayList();
079:
080: while ((c = desc.charAt(cursor++)) != ')') {
081: switch (c) {
082: case 'V':
083: case 'I':
084: case 'C':
085: case 'Z':
086: case 'D':
087: case 'F':
088: case 'J':
089: case 'B':
090: case 'S':
091: buf.append(c);
092: break;
093: case '[':
094: buf.append(c);
095: continue;
096: case 'L':
097: while (true) {
098: buf.append(c);
099: if (c == ';') {
100: break;
101: }
102: c = desc.charAt(cursor++);
103: }
104: break;
105: default:
106: throw invalidDescriptor(desc);
107: }
108:
109: list.add(TypeDesc.forDescriptor(buf.toString()));
110: buf.setLength(0);
111: }
112:
113: TypeDesc ret = TypeDesc.forDescriptor(desc
114: .substring(cursor));
115:
116: TypeDesc[] tds = new TypeDesc[list.size()];
117: tds = (TypeDesc[]) list.toArray(tds);
118:
119: return intern(new MethodDesc(desc, ret, tds));
120: } catch (NullPointerException e) {
121: throw invalidDescriptor(desc);
122: } catch (IndexOutOfBoundsException e) {
123: throw invalidDescriptor(desc);
124: }
125: }
126:
127: public static MethodDesc forMethod(Method method) {
128: Class[] paramClasses = method.getParameterTypes();
129: TypeDesc[] paramTypes;
130: if (paramClasses == null || paramClasses.length == 0) {
131: paramTypes = EMPTY_PARAMS;
132: } else {
133: paramTypes = new TypeDesc[paramClasses.length];
134: for (int i = paramClasses.length; --i >= 0;) {
135: paramTypes[i] = TypeDesc.forClass(paramClasses[i]);
136: }
137: }
138: return forArguments(TypeDesc.forClass(method.getReturnType()),
139: paramTypes);
140: }
141:
142: private static IllegalArgumentException invalidDescriptor(
143: String desc) {
144: return new IllegalArgumentException("Invalid descriptor: "
145: + desc);
146: }
147:
148: private transient final String mDescriptor;
149: private transient final TypeDesc mRetType;
150: private transient final TypeDesc[] mParams;
151:
152: private MethodDesc(TypeDesc ret, TypeDesc[] params) {
153: mDescriptor = generateDescriptor(ret, params);
154: mRetType = ret;
155: mParams = params;
156: }
157:
158: private MethodDesc(String desc, TypeDesc ret, TypeDesc[] params) {
159: mDescriptor = desc;
160: mRetType = ret;
161: mParams = params;
162: }
163:
164: /**
165: * Returns a method descriptor string, excluding generics.
166: */
167: public String getDescriptor() {
168: return mDescriptor;
169: }
170:
171: /**
172: * Returns a method descriptor string, including any generics.
173: */
174: //public abstract String getGenericDescriptor();
175: /**
176: * Returns the described return type, which is TypeDesc.VOID if void.
177: */
178: public TypeDesc getReturnType() {
179: return mRetType;
180: }
181:
182: public int getParameterCount() {
183: return mParams.length;
184: }
185:
186: public TypeDesc[] getParameterTypes() {
187: TypeDesc[] params = mParams;
188: return (params != EMPTY_PARAMS) ? (TypeDesc[]) params.clone()
189: : params;
190: }
191:
192: /**
193: * Returns this in Java method signature syntax.
194: *
195: * @param name method name
196: */
197: public String toMethodSignature(String name) {
198: return toMethodSignature(name, false);
199: }
200:
201: /**
202: * Returns this in Java method signature syntax.
203: *
204: * @param name method name
205: * @param varargs request that the last argument, if it is an array, to
206: * be formatted in varargs syntax.
207: */
208: public String toMethodSignature(String name, boolean varargs) {
209: StringBuffer buf = new StringBuffer();
210: buf.append(mRetType.getFullName());
211: buf.append(' ');
212: buf.append(name);
213: buf.append('(');
214:
215: TypeDesc[] params = mParams;
216: for (int i = 0; i < params.length; i++) {
217: if (i > 0) {
218: buf.append(", ");
219: }
220: TypeDesc param = params[i];
221: if (varargs && param.isArray() && i == (params.length - 1)) {
222: buf.append(param.getComponentType().getFullName());
223: buf.append("...");
224: } else {
225: buf.append(param.getFullName());
226: }
227: }
228:
229: return buf.append(')').toString();
230: }
231:
232: public String toString() {
233: // TODO: Return generic descriptor
234: return mDescriptor;
235: }
236:
237: public int hashCode() {
238: return mDescriptor.hashCode();
239: }
240:
241: public boolean equals(Object other) {
242: if (this == other) {
243: return true;
244: }
245: if (other instanceof MethodDesc) {
246: return ((MethodDesc) other).mDescriptor.equals(mDescriptor);
247: }
248: return false;
249: }
250:
251: Object writeReplace() throws ObjectStreamException {
252: return new External(mDescriptor);
253: }
254:
255: private static String generateDescriptor(TypeDesc ret,
256: TypeDesc[] params) {
257: int length = ret.getDescriptor().length() + 2;
258: int paramsLength = params.length;
259: for (int i = paramsLength; --i >= 0;) {
260: length += params[i].getDescriptor().length();
261: }
262: char[] buf = new char[length];
263: buf[0] = '(';
264: int index = 1;
265: String paramDesc;
266: for (int i = 0; i < paramsLength; i++) {
267: paramDesc = params[i].getDescriptor();
268: int paramDescLength = paramDesc.length();
269: paramDesc.getChars(0, paramDescLength, buf, index);
270: index += paramDescLength;
271: }
272: buf[index++] = ')';
273: paramDesc = ret.getDescriptor();
274: paramDesc.getChars(0, paramDesc.length(), buf, index);
275: return new String(buf);
276: }
277:
278: private static class External implements Externalizable {
279: private String mDescriptor;
280:
281: public External() {
282: }
283:
284: public External(String desc) {
285: mDescriptor = desc;
286: }
287:
288: public void writeExternal(ObjectOutput out) throws IOException {
289: out.writeUTF(mDescriptor);
290: }
291:
292: public void readExternal(ObjectInput in) throws IOException {
293: mDescriptor = in.readUTF();
294: }
295:
296: public Object readResolve() throws ObjectStreamException {
297: return forDescriptor(mDescriptor);
298: }
299: }
300: }
|