/*
* Bytecode Analysis Framework
* Copyright (C) 2004, University of Maryland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.umd.cs.findbugs.ba.generic;
import java.util.Iterator;
import java.util.NoSuchElementException;
import javax.annotation.CheckForNull;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.Signature;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.Type;
/**
* A simple class to parse method signatures that include generic information.
* <p>
*
* Modified from edu.umd.cs.findbugs.ba.SignatureParser
*
* @author Nat Ayewah
*/
public class GenericSignatureParser {
private class ParameterSignatureIterator implements Iterator<String> {
private int index = 1;
public boolean hasNext() {
return index < signature.length() && signature.charAt(index) != ')' && signature.charAt(index) != '^';
}
public String next() {
if (!hasNext())
throw new NoSuchElementException();
StringBuilder result = new StringBuilder();
boolean done;
do {
done = true;
char ch = signature.charAt(index);
switch (ch) {
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
case '*': // wildcard
result.append(signature.charAt(index));
++index;
break;
case 'L':
case 'T':
String tmp = "";
int startsemi = index;
int leftCount = 0;
int i = startsemi + 1;
loop: while (true) {
char c = signature.charAt(i);
switch (c) {
case ';':
if (leftCount == 0)
break loop;
break;
case '<':
leftCount++;
break;
case '>':
leftCount--;
break;
}
i++;
}
String foo = signature.substring(startsemi, i + 1);
result.append(foo);
index = i + 1;
break;
case '[':
case '+':
case '-':
result.append(signature.charAt(index));
++index;
done = false;
break;
case ')':
case '^':
throw new NoSuchElementException("Should have already thrown NoSuchElementException");
case 'V':
default:
throw new IllegalStateException("Invalid method signature: '" + signature + "' : "
+ signature.substring(index) + " " + result);
}
} while (!done);
return result.toString();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private final String signature;
/**
* Parses a generic method signature of the form:
* <code>(argument_signature)return_type_signature</code>
*
* @param signature
* the method signature to be parsed
*/
public GenericSignatureParser(String signature) {
// XXX not currently handling Type parameters for class, interface or
// method definitions
int s = signature.indexOf('(');
String sig = signature;
if (s > 0)
sig = sig.substring(s);
else if (s < 0 || sig.indexOf(':') >= 0 || sig.startsWith("(V)"))
throw new IllegalArgumentException("Bad method signature: " + signature);
this.signature = sig;
}
/**
* Get an Iterator over signatures of the method parameters.
*
* @return Iterator which returns the parameter type signatures in order
*/
public Iterator<String> parameterSignatureIterator() {
return new ParameterSignatureIterator();
}
/**
* Get the method return type signature.
*
* @return the method return type signature
*/
public String getReturnTypeSignature() {
int endOfParams = signature.lastIndexOf(')');
if (endOfParams < 0)
throw new IllegalArgumentException("Bad method signature: " + signature);
return signature.substring(endOfParams + 1);
}
/**
* Get the number of parameters in the signature.
*
* @return the number of parameters
*/
public int getNumParameters() {
int count = 0;
for (Iterator<String> i = parameterSignatureIterator(); i.hasNext();) {
i.next();
++count;
}
return count;
}
/**
* Get the number of parameters passed to method invocation.
*
* @param inv
* @param cpg
* @return int number of parameters
*/
public static int getNumParametersForInvocation(InvokeInstruction inv, ConstantPoolGen cpg) {
GenericSignatureParser sigParser = new GenericSignatureParser(inv.getSignature(cpg));
return sigParser.getNumParameters();
}
/**
* @param target
* the method whose signature is to be parsed
* @return an iterator over the parameters of the generic signature of
* method. Returns null if the generic signature cannot be parsed
*/
public static @CheckForNull
Iterator<String> getGenericSignatureIterator(Method target) {
try {
GenericSignatureParser parser = null;
String genericSignature = null;
for (Attribute a : target.getAttributes()) {
if (a instanceof Signature) {
Signature sig = (Signature) a;
if (genericSignature != null) {
if (!genericSignature.equals(sig.getSignature())) {
if (false) {
System.out.println("Inconsistent signatures: ");
System.out.println(genericSignature);
System.out.println(sig.getSignature());
}
return null; // we've seen two inconsistent
// signatures
}
continue;
}
genericSignature = sig.getSignature();
if (compareSignatures(target.getSignature(), genericSignature))
parser = new GenericSignatureParser(genericSignature);
}
}
Iterator<String> iter = parser == null ? null : parser.parameterSignatureIterator();
return iter;
} catch (RuntimeException e) {
} // degrade gracefully
return null;
}
/**
* Compare a plain method signature to the a generic method Signature and
* return true if they match
*/
public static boolean compareSignatures(String plainSignature, String genericSignature) {
GenericSignatureParser plainParser = new GenericSignatureParser(plainSignature);
GenericSignatureParser genericParser = new GenericSignatureParser(genericSignature);
if (plainParser.getNumParameters() != genericParser.getNumParameters())
return false;
return true;
}
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("Usage: " + GenericSignatureParser.class.getName() + " '<method signature>'");
System.exit(1);
}
GenericSignatureParser parser = new GenericSignatureParser(args[0]);
for (Iterator<String> i = parser.parameterSignatureIterator(); i.hasNext();) {
String s = i.next();
System.out.println(s);
Type t = GenericUtilities.getType(s);
System.out.println("-~- " + t);
if (t instanceof ObjectType)
System.out.println("-~- " + ((ObjectType) t).toString());
if (t != null)
System.out.println("-~- " + t.getClass());
}
System.out.println(parser.getNumParameters() + " parameter(s)");
}
}