package ca.uwaterloo.ece.qhanam.jrsrepair;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Stack;
import org.eclipse.jdt.core.dom.*;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
/**
* Handles new ASTs and variable bindings that are generated by parsing a
* list of source code files. Inserts the statements in the ASTs into a
* Statements object if they are in the set of potential seed statements
* or faulty statements (determined by statement coverage of the test
* cases).
* @author qhanam
*
*/
public class MutationASTRequestor extends FileASTRequestor {
private HashMap<String, HashSet<String>> scope;
private HashMap<String, DocumentASTRewrite> sourceFileContents;
private LineCoverage faultyLineCoverage;
private LineCoverage seedLineCoverage;
private Statements faultyStatements;
private Statements seedStatements;
/**
* @param faultyStatements
* @param seedStatements
*/
public MutationASTRequestor(HashMap<String, DocumentASTRewrite> sourceFileContents, HashMap<String, HashSet<String>> scope,
LineCoverage faultyLineCoverage, LineCoverage seedLineCoverage,
Statements faultyStatements, Statements seedStatements){
this.sourceFileContents = sourceFileContents;
this.faultyLineCoverage = faultyLineCoverage;
this.seedLineCoverage = seedLineCoverage;
this.faultyStatements = faultyStatements;
this.seedStatements = seedStatements;
this.scope = scope;
}
@Override
public void acceptBinding(String bindingKey, IBinding binding) { }
/**
* Handles new ASTs that are generated from parsing a list of source
* code files.
*/
@Override
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
/* TODO: Turn on modification logging for the CompilationUnit. We
* can use this instead of manually keeping track of
* ASTRewrite objects for each CompilationUnit. */
// cu.recordModifications();
/* Create an ASTRewriter to track the this file's mutations. */
AST ast = cu.getAST();
this.sourceFileContents.get(sourceFilePath).rewriter = ASTRewrite.create(ast);
/* Store the statements that are covered by test cases. */
StatementASTVisitor statementASTVisitor = new StatementASTVisitor(sourceFilePath);
cu.accept(statementASTVisitor);
/* Get the member variable names. */
ClassVarASTVisitor cvav = new ClassVarASTVisitor();
cu.accept(cvav);
/* Build a HashSet of variable names for each method. This is the scope. */
MethodVarASTVisitor mvav = new MethodVarASTVisitor(sourceFilePath, cvav.variableNames);
cu.accept(mvav);
}
/**
* Adds statements to the statement lists if they are in the faulty/seed
* statement coverage lists.
* @author qhanam
*
*/
private class StatementASTVisitor extends ASTVisitor {
String sourceFilePath;
public StatementASTVisitor(String sourceFilePath){
this.sourceFilePath = sourceFilePath;
}
/**
* Checks a statement against the coverage lists and inserts valid statements.
*
* @param s
*/
private void insertStatement(Statement s){
/* Build the LCNode to check if this statement is in the line coverage. */
CompilationUnit cu = (CompilationUnit) s.getRoot();
String packageName = cu.getPackage() == null ? "" : cu.getPackage().getName().toString();
String className = ((AbstractTypeDeclaration)cu.types().get(0)).getName().toString();
int lineNumber = cu.getLineNumber(s.getStartPosition());
LCNode node = new LCNode(packageName, className, lineNumber);
/* Check if this statement has been covered. If so add it to the appropriate statement
* list with its weight. */
Double weight;
if((weight = MutationASTRequestor.this.faultyLineCoverage.contains(node)) != null){
MutationASTRequestor.this.faultyStatements.addStatement(new SourceStatement(this.sourceFilePath, s), weight);
}
if((weight = MutationASTRequestor.this.seedLineCoverage.contains(node)) != null){
MutationASTRequestor.this.seedStatements.addStatement(new SourceStatement(this.sourceFilePath, s), weight);
}
}
/**
* We need to handle all subtypes of Statement.
*/
public boolean visit(AssertStatement node){insertStatement(node); return true;}
//public boolean visit(Block node){insertStatement(node); return true;}
public boolean visit(BreakStatement node){insertStatement(node); return true;}
public boolean visit(ConstructorInvocation node){insertStatement(node); return true;}
public boolean visit(ContinueStatement node){insertStatement(node); return true;}
public boolean visit(DoStatement node){insertStatement(node); return true;}
public boolean visit(EmptyStatement node){insertStatement(node); return true;}
public boolean visit(ExpressionStatement node){insertStatement(node); return true;}
public boolean visit(ForStatement node){insertStatement(node); return true;}
public boolean visit(IfStatement node){insertStatement(node); return true;}
public boolean visit(LabeledStatement node){insertStatement(node); return true;}
public boolean visit(ReturnStatement node){insertStatement(node); return true;}
public boolean visit(SuperConstructorInvocation node){insertStatement(node); return true;}
public boolean visit(SwitchCase node){insertStatement(node); return true;}
public boolean visit(SwitchStatement node){insertStatement(node); return true;}
public boolean visit(SynchronizedStatement node){insertStatement(node); return true;}
public boolean visit(ThrowStatement node){insertStatement(node); return true;}
public boolean visit(TryStatement node){insertStatement(node); return true;}
public boolean visit(TypeDeclarationStatement node){insertStatement(node); return true;}
public boolean visit(VariableDeclarationStatement node){insertStatement(node); return true;}
public boolean visit(WhileStatement node){insertStatement(node); return true;}
}
/**
* Collects the names of member variables used in the method.
* @author qhanam
*/
private class MethodVarASTVisitor extends ASTVisitor{
public HashSet<String> variableNames;
public String sourceFilePath;
/**
* @param memberVariableNames The collection of member variables from the method's class.
*/
public MethodVarASTVisitor(String sourceFilePath, Stack<String> memberVariableNames){
this.variableNames = new HashSet<String>(memberVariableNames);
this.sourceFilePath = sourceFilePath;
}
/**
* Get the names of variables declared in the method.
*/
public boolean visit(MethodDeclaration md) {
md.accept(new ASTVisitor() {
public boolean visit(VariableDeclarationFragment var) { // Field declarations, local variable declarations, ForStatement initializers
MethodVarASTVisitor.this.variableNames.add(var.getName().toString());
return false;
}
public boolean visit(SingleVariableDeclaration var) { // Formal parameters and catch statements
MethodVarASTVisitor.this.variableNames.add(var.getName().toString());
return false;
}
});
MutationASTRequestor.this.scope.put(this.sourceFilePath + "." + md.getName().toString(), this.variableNames);
return false;
}
/**
* Maybe don't use this yet...
*
* TODO: Build a specific scope for every class in a compilation unit.
* @param md
* @return
*/
@SuppressWarnings("unused")
private String getClassPath(MethodDeclaration md){
ASTNode node = md;
while(!(node instanceof AbstractTypeDeclaration)){
node = node.getParent();
}
AbstractTypeDeclaration atd = (AbstractTypeDeclaration) node;
return ((CompilationUnit) atd.getRoot()).getPackage().getName().toString() + "." + atd.getName().toString();
}
}
/**
* Collects the names of member variables used in the class.
* @author qhanam
*/
private class ClassVarASTVisitor extends ASTVisitor{
public Stack<String> variableNames;
public ClassVarASTVisitor(){
this.variableNames = new Stack<String>();
}
/**
* Get the names of member variables declared in this class.
*/
public boolean visit(VariableDeclarationFragment var) {
this.variableNames.add(var.getName().toString());
return false;
}
/**
* We are only getting member variables, so don't visit anything below methods.
*/
public boolean visit(MethodDeclaration md) { return false; }
}
}