//
// Copyright (C) 2009 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.util;
import java.util.BitSet;
import gov.nasa.jpf.vm.ClassInfo;
import gov.nasa.jpf.vm.MethodInfo;
import gov.nasa.jpf.vm.Types;
/**
* utility class that can match methods/args against specs.
* argument signatures can be given in dot notation (like javap), arguments
* can be marked with a preceeding '^'
* if the class or name part are omitted, "*" is assumed
* a preceeding '!' means the match is inverted
*
* spec examples
* "x.y.Foo.*"
* "java.util.HashMap.add(java.lang.Object,^java.lang.Object)"
* "*.*(x.y.MyClass)"
*
* Note: with a single '*' we can't tell if this is for the typename
* or the method, so something like "java.*" is probably not doing
* what you expect - it uses the wildcard for the method and 'java' for
* the type
*
* <2do> we should extend this to allow alternatives
*/
public class MethodSpec extends FeatureSpec {
static class MethodParseData extends ParseData {
String sigSpec;
}
static final char MARK = '^'; // to mark arguments
String sigSpec; // this is only the argument part, including parenthesis
BitSet markedArgs;
/**
* factory method that includes the parser
*/
public static MethodSpec createMethodSpec (String s){
MethodParseData d = new MethodParseData();
s = s.trim();
String src = s;
s = parseInversion(s,d);
int i = s.indexOf(('('));
if (i >= 0){ // we have a signature part
int j = s.lastIndexOf(')');
if (j > i){
d.sigSpec = s.substring(i, j+1);
s = s.substring(0, i);
} else {
return null; // error, unbalanced parenthesis
}
}
parseTypeAndName(s,d);
try {
return new MethodSpec(src, d.typeSpec, d.nameSpec, d.sigSpec, d.matchInverted);
} catch (IllegalArgumentException iax){
return null;
}
}
public MethodSpec (String rawSpec, String cls, String name, String argSig, boolean inverted){
super(rawSpec,cls,name,inverted);
if (argSig != null){
parseSignature(argSig);
}
}
/**
* assumed to be comma separated type list using fully qualified dot notation
* like javap, but arguments can be marked with preceeding '^',
* like "(java.lang.String,^int[])"
* spec includes parenthesis
*/
void parseSignature (String spec){
BitSet m = null;
StringBuilder sb = new StringBuilder();
String al = spec.substring(1, spec.length()-1);
String[] args = al.split(",");
sb.append('(');
int i=0;
for (String a : args){
a = a.trim();
if (a.length() > 0){
if (a.charAt(0) == MARK){
if (m == null){
m = new BitSet(args.length);
}
m.set(i);
a = a.substring(1);
}
String tc = Types.getTypeSignature(a, false);
sb.append(tc);
i++;
} else {
// error in arg type spec
}
}
sb.append(')');
sigSpec = sb.toString();
markedArgs = m;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("MethodSpec {");
sb.append("matchInverted:");
sb.append(matchInverted);
if (clsSpec != null){
sb.append(",clsSpec:\"");
sb.append(clsSpec);
sb.append('"');
}
if (nameSpec != null){
sb.append(",nameSpec:\"");
sb.append(nameSpec);
sb.append('"');
}
if (sigSpec != null){
sb.append(",sigSpec:\"");
sb.append(sigSpec);
sb.append('"');
}
if (markedArgs != null){
sb.append(",marked:");
sb.append(markedArgs);
}
sb.append('}');
return sb.toString();
}
public BitSet getMarkedArgs () {
return markedArgs;
}
public boolean isMarkedArg(int idx){
return (markedArgs == null || markedArgs.get(idx));
}
//--- our matchers
public boolean matches (Object feature){
if (feature instanceof MethodInfo){
return matches((MethodInfo)feature);
} else {
return false;
}
}
public boolean matches (MethodInfo mi){
boolean isMatch = false;
ClassInfo ci = mi.getClassInfo();
if (isMatchingType(ci)){
if (nameSpec.matches(mi.getName())){
if (sigSpec != null){
// sigSpec includes '(',')' but not return type
isMatch = mi.getSignature().startsWith(sigSpec);
} else { // no sigSpec -> matches all signatures
isMatch = true;
}
}
}
return (isMatch != matchInverted);
}
public boolean matches (String clsName, String mthName){
boolean isMatch = clsSpec.matches(clsName) && nameSpec.matches(mthName);
return isMatch != matchInverted;
}
public boolean matchesClass (String clsName){
return clsSpec.matches(clsName) != matchInverted;
}
}