package org.intrace.agent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.intrace.output.trace.TraceHandler;
/**
* Contains criteria that the 'user' specifies to request certain classes to
* be instrumented.
* @author erikostermueller
*
*/
public class InstrCriteria {
public VerboseLogger verboseLogger = null;
/**
* Each item in this Map is a classname that has methods.
*/
final Map<String,List<SimpleMethod> > myInstrCriteria = new Hashtable<String,List<SimpleMethod>>();
static final String CLASS_METHOD_DELIMITER = "#";
private static final String CRITERIA_DELIM = "|";
private static final String REGEX_CRITERIA_DELIM = "\\"+CRITERIA_DELIM;
private static final String INSTRUMENT_ALL_METHODS = "INSTR_ALL_METHODS";
private String[] classNamesOnly = null;
private List<String> classNamesOnlyList = new ArrayList<String>();
private String originalCriteria = null;
public InstrCriteria(String criteria) {
this.originalCriteria = criteria;
criteria = criteria.replace('{', '[');
String[] tmp = criteria.split(this.REGEX_CRITERIA_DELIM);
for (String s : tmp)
addClassOrMethod(s);
}
/*
* Example: org.intracetest.agent.ArgumentTypes#charArrayArg([C)V
*
*/
static class SimpleMethod {
boolean ynAllMethods = false;
/*
* example: charArrayArg
*/
private String name;
/*
* example: ([C)V
*
*/
private String args;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getArgs() {
String rc = "";
if (args!=null)
rc = this.args.replace('[', '{');
return rc;
}
public void setArgs(String args) {
this.args = args;
}
public String toString() {
return name+getArgs();
}
public void setNameAndArgs(String methodNameAndArgs) {
int firstLeftParen = methodNameAndArgs.indexOf("(");
if (firstLeftParen >= 0) {
name = methodNameAndArgs.substring(0, firstLeftParen);
args = methodNameAndArgs.substring(firstLeftParen);
//System.out.println("in setNameAndArgs just added [" + methodNameAndArgs + "] as [" + this.toString() + "]");
} else
name = methodNameAndArgs;
}
}
public String toString() {
StringBuilder sb = new StringBuilder();
int count = 0;
for(String className : this.getClassRegex()) {
List<SimpleMethod> methods = this.myInstrCriteria.get(className);
for(SimpleMethod myMethod : methods) {
if (++count > 1) sb.append(CRITERIA_DELIM); //This line add this | to make crit1|crit2|crit3
if (myMethod.ynAllMethods)
sb.append(className);
else {
sb.append(className);
sb.append(CLASS_METHOD_DELIMITER);
sb.append( myMethod.toString() );
}
}
}
return sb.toString();
}
public String[] getClassRegex() {
if (classNamesOnly==null) {
Set<String> keys = this.myInstrCriteria.keySet();
classNamesOnly = keys.toArray( new String[0] );
}
return classNamesOnly;
}
/**
* Example: org.intracetest.agent.ArgumentTypes#charArrayArg([C)V
*
* @param myclass: example: org.intracetest.agent.ArgumentTypes
* @param method: example: charArrayArg
* @param arguments: example: ([C)V
* @return
*/
public boolean thisMethodSpecified(String myClass, String method, String arguments) {
List<SimpleMethod> allMethods = null;
arguments = arguments.replace('[', '{');
boolean rc = false;
if (this.allMethodsSpecified(myClass)) {
rc = true;
}else {
allMethods = this.myInstrCriteria.get(myClass);
if (findMethod(allMethods, method,arguments)!=null)
rc = true;
}
logVerbose("instrument method? [" + rc +
"] class[" + myClass +
"] method[" + method +
"] + method args [" + arguments +
"] count of methods instrumented [" + ( (allMethods!=null) ? allMethods.size() : "zero" )+
"]");
return rc;
}
public int methodCountPerClass(String myClass) {
int count = 0;
List<SimpleMethod> allMethods = this.myInstrCriteria.get(myClass);
if (allMethods!=null) {
count = allMethods.size();
}
return count;
}
private void logVerbose(String s) {
if (this.verboseLogger!=null) {
this.verboseLogger.logVerbose(s);
}
}
public boolean allMethodsSpecified(String myClass) {
boolean rc = false;
List<SimpleMethod> allMethods = this.myInstrCriteria.get(myClass);
if (allMethods !=null)
for(SimpleMethod method : allMethods) {
if (method.ynAllMethods)
rc = true;
}
//logVerbose("instrument all methods for this class? [" + rc + "] class[" + myClass + "]");
return rc;
}
/**
*
* The bug lies here:
* Comparing fullMethodNameCriteria [byteArrayArg({B)V] to [byteArrayArg([B)V]
*
* @param methods
* @param nameCriteria
* @param argsCriteria
* @return
*/
private SimpleMethod findMethod(List<SimpleMethod> methods, String nameCriteria, String argsCriteria){
SimpleMethod rc = null;
if (nameCriteria !=null && argsCriteria !=null) {
String fullMethodNameCriteria = nameCriteria + argsCriteria;
if (methods != null)
for(SimpleMethod sm : methods) {
//logVerbose("Comparing fullMethodNameCriteria [" + fullMethodNameCriteria + "] to [" + sm.toString() + "]");
if (fullMethodNameCriteria.equals(sm.toString())) {
rc = sm;
break;
}
}
}
return rc;
}
/**
* trying to split this into two: MyClass#myMethod(D)V, but MyClass is also allowable here
* @param methodOrClass
*/
private void addClassOrMethod(String methodOrClass) {
//System.out.println("###addClassOrMethod [" + methodOrClass + "]");
SimpleMethod method = new SimpleMethod();
List<SimpleMethod> allMethods = null;
String[] parts = methodOrClass.split(CLASS_METHOD_DELIMITER);
if (parts.length >= 1 && parts[0]!=null) {
allMethods = this.myInstrCriteria.get(parts[0]);
if (allMethods == null) {
allMethods = new ArrayList<SimpleMethod>();
//this.logVerbose("in addClassOrMethod just created allMethods hash[" + allMethods.hashCode() + "]myInstrCriteriaHash[" + myInstrCriteria.hashCode() + "]");
this.myInstrCriteria.put(parts[0], allMethods);
}
switch (parts.length) {
case 1: //just the package and class name were specified, meaning that all methods should be instrumented.
method.ynAllMethods = true;
break;
case 2: //a method was specified after a # sign...only instrument this specific method.
method.setNameAndArgs(parts[1]);
break;
default:
throw new RuntimeException("Was expecting to find either zero or one of the [" + this.CLASS_METHOD_DELIMITER + "] character inside of [" + methodOrClass + "]");
}
} else {
throw new RuntimeException("Error adding this method/class [" + methodOrClass + "]");
}
allMethods.add(method);
// System.out.println("## Just added methodName[" + method.name + "] methodArg [" + method.args + "]methodArgGetter [" + method.getArgs() + "]");
// System.out.println("@@@@@@@Class count [" + myInstrCriteria.size() + "] for class[" + parts[0] + "] count is [" + allMethods.size() + "]");
}
}