001: /*
002: * Bytecode Analysis Framework
003: * Copyright (C) 2003,2004 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.ba;
021:
022: import org.apache.bcel.classfile.JavaClass;
023: import org.apache.bcel.classfile.Method;
024: import org.apache.bcel.generic.ConstantPoolGen;
025: import org.apache.bcel.generic.InvokeInstruction;
026: import org.apache.bcel.generic.MethodGen;
027:
028: import edu.umd.cs.findbugs.classfile.MethodDescriptor;
029: import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
030:
031: /**
032: * Convert part or all of a Java type signature into something
033: * closer to what types look like in the source code.
034: * Both field and method signatures may be processed by this class.
035: * For a field signature, just call parseNext() once.
036: * For a method signature, parseNext() must be called multiple times,
037: * and the parens around the arguments must be skipped manually
038: * (by calling the skip() method).
039: *
040: * @author David Hovemeyer
041: */
042:
043: public class SignatureConverter {
044: private String signature;
045:
046: /**
047: * Constructor.
048: *
049: * @param signature the field or method signature to convert
050: */
051: public SignatureConverter(String signature) {
052: this .signature = signature;
053: }
054:
055: /**
056: * Get the first character of the remaining part of the signature.
057: */
058: public char getFirst() {
059: return signature.charAt(0);
060: }
061:
062: /**
063: * Skip the first character of the remaining part of the signature.
064: */
065: public void skip() {
066: signature = signature.substring(1);
067: }
068:
069: /**
070: * Parse a single type out of the signature, starting at the beginning
071: * of the remaining part of the signature. For example, if the first
072: * character of the remaining part is "I", then this method will return
073: * "int", and the "I" will be consumed. Arrays, reference types,
074: * and basic types are all handled.
075: *
076: * @return the parsed type string
077: */
078: public String parseNext() {
079: StringBuffer result = new StringBuffer();
080:
081: if (signature.startsWith("[")) {
082: int dimensions = 0;
083: do {
084: ++dimensions;
085: signature = signature.substring(1);
086: } while (signature.charAt(0) == '[');
087: result.append(parseNext());
088:
089: while (dimensions-- > 0) {
090: result.append("[]");
091: }
092: } else if (signature.startsWith("L")) {
093: int semi = signature.indexOf(';');
094: if (semi < 0)
095: throw new IllegalStateException(
096: "missing semicolon in signature " + signature);
097: result.append(signature.substring(1, semi)
098: .replace('/', '.'));
099: signature = signature.substring(semi + 1);
100: } else {
101: switch (signature.charAt(0)) {
102: case 'B':
103: result.append("byte");
104: break;
105: case 'C':
106: result.append("char");
107: break;
108: case 'D':
109: result.append("double");
110: break;
111: case 'F':
112: result.append("float");
113: break;
114: case 'I':
115: result.append("int");
116: break;
117: case 'J':
118: result.append("long");
119: break;
120: case 'S':
121: result.append("short");
122: break;
123: case 'Z':
124: result.append("boolean");
125: break;
126: case 'V':
127: result.append("void");
128: break;
129: default:
130: throw new IllegalArgumentException("bad signature "
131: + signature);
132: }
133: skip();
134: }
135:
136: return result.toString();
137: }
138:
139: /**
140: * Convenience method for generating a method signature in
141: * human readable form.
142: *
143: * @param javaClass the class
144: * @param method the method
145: */
146: public static String convertMethodSignature(JavaClass javaClass,
147: Method method) {
148: return convertMethodSignature(javaClass.getClassName(), method
149: .getName(), method.getSignature());
150: }
151:
152: /**
153: * Convenience method for generating a method signature in
154: * human readable form.
155: *
156: * @param methodGen the method to produce a method signature for
157: */
158: public static String convertMethodSignature(MethodGen methodGen) {
159: return convertMethodSignature(methodGen.getClassName(),
160: methodGen.getName(), methodGen.getSignature());
161: }
162:
163: /**
164: * Convenience method for generating a method signature in
165: * human readable form.
166: *
167: * @param inv an InvokeInstruction
168: * @param cpg the ConstantPoolGen for the class the instruction belongs to
169: */
170: public static String convertMethodSignature(InvokeInstruction inv,
171: ConstantPoolGen cpg) {
172: return convertMethodSignature(inv.getClassName(cpg), inv
173: .getName(cpg), inv.getSignature(cpg));
174: }
175:
176: /**
177: * Convenience method for generating a method signature in
178: * human readable form.
179: *
180: * @param className name of the class containing the method
181: * @param methodName the name of the method
182: * @param methodSig the signature of the method
183: */
184: public static String convertMethodSignature(String className,
185: String methodName, String methodSig) {
186: return convertMethodSignature(className, methodName, methodSig,
187: "");
188: }
189:
190: /**
191: * Convenience method for generating a method signature in
192: * human readable form.
193: *
194: * @param xmethod an XMethod
195: * @return the formatted version of that signature
196: */
197: public static String convertMethodSignature(XMethod xmethod) {
198: @DottedClassName
199: String className = xmethod.getClassName();
200: assert className.indexOf('/') == -1;
201: return convertMethodSignature(className, xmethod.getName(),
202: xmethod.getSignature());
203: }
204:
205: /**
206: * Convenience method for generating a method signature in
207: * human readable form.
208: *
209: * @param methodDescriptor a MethodDescriptor
210: * @return the formatted version of that signature
211: */
212: public static String convertMethodSignature(
213: MethodDescriptor methodDescriptor) {
214: return convertMethodSignature(methodDescriptor
215: .getClassDescriptor().toDottedClassName(),
216: methodDescriptor.getName(), methodDescriptor
217: .getSignature());
218: }
219:
220: /**
221: * Convenience method for generating a method signature in
222: * human readable form.
223: *
224: * @param className name of the class containing the method
225: * @param methodName the name of the method
226: * @param methodSig the signature of the method
227: * @param pkgName the name of the package the method is in (used to shorten
228: * class names)
229: */
230: public static String convertMethodSignature(String className,
231: String methodName, String methodSig, String pkgName) {
232: StringBuffer args = new StringBuffer();
233: SignatureConverter converter = new SignatureConverter(methodSig);
234:
235: converter.skip();
236: args.append('(');
237:
238: while (converter.getFirst() != ')') {
239: if (args.length() > 1)
240: args.append(", ");
241: args.append(shorten(pkgName, converter.parseNext()));
242: }
243: converter.skip();
244: args.append(')');
245:
246: // Ignore return type
247:
248: StringBuffer result = new StringBuffer();
249: result.append(className);
250: result.append('.');
251: result.append(methodName);
252: result.append(args.toString());
253:
254: return result.toString();
255: }
256:
257: /**
258: * Convenience method for converting a single signature component to
259: * human-readable form.
260: *
261: * @param signature the signature
262: */
263: public static String convert(String signature) {
264: return new SignatureConverter(signature).parseNext();
265: }
266:
267: public static String shorten(String pkgName, String typeName) {
268: int index = typeName.lastIndexOf('.');
269: if (index >= 0) {
270: String otherPkg = typeName.substring(0, index);
271: if (otherPkg.equals(pkgName)
272: || otherPkg.equals("java.lang"))
273: typeName = typeName.substring(index + 1);
274: }
275: return typeName;
276: }
277: }
278:
279: // vim:ts=4
|