001: /*
002: * Bytecode Analysis Framework
003: * Copyright (C) 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.generic;
021:
022: import java.util.Iterator;
023: import java.util.NoSuchElementException;
024:
025: import org.apache.bcel.classfile.Attribute;
026: import org.apache.bcel.classfile.Method;
027: import org.apache.bcel.classfile.Signature;
028: import org.apache.bcel.generic.ConstantPoolGen;
029: import org.apache.bcel.generic.InvokeInstruction;
030: import org.apache.bcel.generic.ObjectType;
031: import org.apache.bcel.generic.Type;
032:
033: import edu.umd.cs.findbugs.annotations.CheckForNull;
034:
035: /**
036: * A simple class to parse method signatures that include
037: * generic information. <p>
038: *
039: * Modified from edu.umd.cs.findbugs.ba.SignatureParser
040: *
041: * @author Nat Ayewah
042: */
043: public class GenericSignatureParser {
044: private class ParameterSignatureIterator implements
045: Iterator<String> {
046: private int index = 1;
047:
048: public boolean hasNext() {
049: return index < signature.length()
050: && signature.charAt(index) != ')';
051: }
052:
053: public String next() {
054: if (!hasNext())
055: throw new NoSuchElementException();
056: StringBuffer result = new StringBuffer();
057: boolean done;
058: do {
059: done = true;
060: char ch = signature.charAt(index);
061: switch (ch) {
062: case 'B':
063: case 'C':
064: case 'D':
065: case 'F':
066: case 'I':
067: case 'J':
068: case 'S':
069: case 'Z':
070: case '*': // wildcard
071: result.append(signature.charAt(index));
072: ++index;
073: break;
074:
075: case 'L':
076: case 'T':
077: String tmp = "";
078: int startsemi = index;
079: int leftCount = 0;
080: int i = startsemi + 1;
081: loop: while (true) {
082: char c = signature.charAt(i);
083: switch (c) {
084: case ';':
085: if (leftCount == 0)
086: break loop;
087: break;
088: case '<':
089: leftCount++;
090: break;
091: case '>':
092: leftCount--;
093: break;
094: }
095: i++;
096:
097: }
098: String foo = signature.substring(startsemi, i + 1);
099: result.append(foo);
100: index = i + 1;
101: break;
102:
103: case '[':
104: case '+':
105: case '-':
106: result.append(signature.charAt(index));
107: ++index;
108: done = false;
109: break;
110:
111: case 'V':
112: default:
113: throw new IllegalStateException(
114: "Invalid method signature: " + signature
115: + " : "
116: + signature.substring(index) + " "
117: + result);
118: }
119: } while (!done);
120: return result.toString();
121: }
122:
123: public void remove() {
124: throw new UnsupportedOperationException();
125: }
126: }
127:
128: private final String signature;
129:
130: /**
131: * Parses a generic method signature of the form:
132: * <code>(argument_signature)return_type_signature</code>
133: *
134: * @param signature the method signature to be parsed
135: */
136: public GenericSignatureParser(String signature) {
137: // XXX not currently handling Type parameters for class, interface or method definitions
138: if (signature.indexOf('(') < 0 || signature.indexOf(':') >= 0)
139: throw new IllegalArgumentException("Bad method signature: "
140: + signature);
141: if (!signature.startsWith("("))
142: this .signature = signature
143: .substring(signature.indexOf("("));
144: else
145: this .signature = signature;
146: }
147:
148: /**
149: * Get an Iterator over signatures of the method parameters.
150: *
151: * @return Iterator which returns the parameter type signatures in order
152: */
153: public Iterator<String> parameterSignatureIterator() {
154: return new ParameterSignatureIterator();
155: }
156:
157: /**
158: * Get the method return type signature.
159: *
160: * @return the method return type signature
161: */
162: public String getReturnTypeSignature() {
163: int endOfParams = signature.lastIndexOf(')');
164: if (endOfParams < 0)
165: throw new IllegalArgumentException("Bad method signature: "
166: + signature);
167: return signature.substring(endOfParams + 1);
168: }
169:
170: /**
171: * Get the number of parameters in the signature.
172: *
173: * @return the number of parameters
174: */
175: public int getNumParameters() {
176: int count = 0;
177: for (Iterator<String> i = parameterSignatureIterator(); i
178: .hasNext();) {
179: i.next();
180: ++count;
181: }
182: return count;
183: }
184:
185: /**
186: * Get the number of parameters passed to method invocation.
187: *
188: * @param inv
189: * @param cpg
190: * @return int number of parameters
191: */
192: public static int getNumParametersForInvocation(
193: InvokeInstruction inv, ConstantPoolGen cpg) {
194: GenericSignatureParser sigParser = new GenericSignatureParser(
195: inv.getSignature(cpg));
196: return sigParser.getNumParameters();
197: }
198:
199: /**
200: * @param method
201: * @return an iterator over the parameters of the generic
202: * signature of method. Returns null if the
203: * generic signature cannot be parsed
204: */
205: public static @CheckForNull
206: Iterator<String> getGenericSignatureIterator(Method target) {
207: try {
208: GenericSignatureParser parser = null;
209: String genericSignature = null;
210: for (Attribute a : target.getAttributes()) {
211: if (a instanceof Signature) {
212:
213: Signature sig = (Signature) a;
214: if (genericSignature != null) {
215: if (!genericSignature
216: .equals(sig.getSignature())) {
217: if (false) {
218: System.out
219: .println("Inconsistent signatures: ");
220: System.out.println(genericSignature);
221: System.out.println(sig.getSignature());
222: }
223: return null; // we've seen two inconsistent signatures
224: }
225: continue;
226: }
227:
228: genericSignature = sig.getSignature();
229: if (compareSignatures(target.getSignature(),
230: genericSignature))
231: parser = new GenericSignatureParser(
232: genericSignature);
233: }
234: }
235: Iterator<String> iter = parser == null ? null : parser
236: .parameterSignatureIterator();
237: return iter;
238: } catch (RuntimeException e) {
239: } // degrade gracefully
240: return null;
241: }
242:
243: /**
244: * Compare a plain method signature to the a generic method Signature and
245: * return true if they match
246: */
247: public static boolean compareSignatures(String plainSignature,
248: String genericSignature) {
249: GenericSignatureParser plainParser = new GenericSignatureParser(
250: plainSignature);
251: GenericSignatureParser genericParser = new GenericSignatureParser(
252: genericSignature);
253:
254: if (plainParser.getNumParameters() != genericParser
255: .getNumParameters())
256: return false;
257:
258: return true;
259: }
260:
261: public static void main(String[] args) {
262: if (args.length != 1) {
263: System.err.println("Usage: "
264: + GenericSignatureParser.class.getName()
265: + " '<method signature>'");
266: System.exit(1);
267: }
268: GenericSignatureParser parser = new GenericSignatureParser(
269: args[0]);
270: for (Iterator<String> i = parser.parameterSignatureIterator(); i
271: .hasNext();) {
272: String s = i.next();
273: System.out.println(s);
274: Type t = GenericUtilities.getType(s);
275: System.out.println("-~- " + t);
276: if (t instanceof ObjectType)
277: System.out
278: .println("-~- " + ((ObjectType) t).toString());
279: System.out.println("-~- " + t.getClass());
280: }
281: System.out.println(parser.getNumParameters() + " parameter(s)");
282:
283: }
284: }
|