package fr.inria.diversify.processor.main;
import fr.inria.diversify.diversification.InputProgram;
import fr.inria.diversify.processor.ProcessorUtil;
import spoon.processing.AbstractProcessor;
import spoon.reflect.code.*;
import spoon.reflect.declaration.*;
import spoon.reflect.factory.Factory;
import spoon.reflect.visitor.Query;
import spoon.reflect.visitor.QueryVisitor;
import spoon.reflect.visitor.filter.TypeFilter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Created by marodrig on 27/06/2014.
*/
public abstract class AbstractLoggingInstrumenter<E extends CtElement> extends AbstractProcessor<E> {
protected static Map<CtExecutable, Integer> localId = new HashMap();
protected static Map<Integer, CtTry> tryBodyMethod;
protected InputProgram inputProgram;
protected String logger;
public AbstractLoggingInstrumenter(InputProgram inputProgram) {
this.inputProgram = inputProgram;
}
public String getLogger() {
return logger;
}
public static void reset() {
tryBodyMethod = new HashMap<>();
}
public void setLogger(String logger) {
this.logger = logger;
}
protected CtClass<?> getClass(CtElement stmt) {
return stmt.getParent(CtClass.class);
}
protected CtExecutable<?> getMethod(CtElement stmt) {
CtExecutable<?> ret = stmt.getParent(CtMethod.class);
if (ret == null)
ret = stmt.getParent(CtConstructor.class);
return ret;
}
protected int getLocalId(CtElement stmt) {
CtExecutable parent = stmt.getParent(CtExecutable.class);
if (localId.containsKey(parent)) {
localId.put(parent, localId.get(parent) + 1);
}
else {
localId.put(parent, 0);
}
// String methodName = parent.getReference().getDeclaringType().getQualifiedName() + "." + parent.getSignature();
return ProcessorUtil.idFor(stmtWithoutLog(stmt).replaceAll("\n", "").trim() + ":" + localId.get(parent));
}
protected String stmtWithoutLog(CtElement stmt) {
Set<String> toRemove = new HashSet<>();
for(Object snippetStatement : Query.getElements(stmt, new TypeFilter(CtCodeSnippetStatement.class))) {
if(snippetStatement.toString().contains(getLogger())) {
toRemove.add(snippetStatement.toString());
}
}
String ret = stmt.toString();
for(String remove : toRemove) {
ret = ret.replace(remove+";", "");
}
return ret;
}
protected boolean containsGoto(CtElement elem) {
QueryVisitor query = new QueryVisitor(new TypeFilter(CtBreak.class));
elem.accept(query);
for(Object o : query.getResult()) {
CtBreak ctBreak = (CtBreak) o;
if(ctBreak.getTargetLabel() != null) {
return true;
}
}
return false;
}
protected int methodId(CtExecutable method) {
return ProcessorUtil.idFor(method.getReference().getDeclaringType().getQualifiedName() + "." + method.getSignature());
}
protected boolean blockNeed(CtStatement statement) {
return statement.getParent() instanceof CtLoop
|| statement.getParent() instanceof CtIf;
}
protected void insertInBlock(CtStatement statement, CtStatement snippetStmt) {
CtBlock<Object> block = getFactory().Core().createBlock();
block.setParent(statement.getParent());
block.addStatement(snippetStmt);
block.addStatement(statement);
if (statement.getParent() instanceof CtLoop) {
CtLoop loop = (CtLoop) statement.getParent();
loop.setBody(block);
}
if (statement.getParent() instanceof CtIf) {
CtIf ctIf = (CtIf) statement.getParent();
ctIf.setThenStatement(block);
}
}
protected CtTry tryFinallyBody(CtExecutable method) {
if(!tryBodyMethod.containsKey(methodId(method))) {
Factory factory = method.getFactory();
CtStatement thisStatement = getThisOrSuperCall(method.getBody());
CtTry ctTry = factory.Core().createTry();
ctTry.setBody(method.getBody());
CtBlock finalizerBlock = factory.Core().createBlock();
ctTry.setFinalizer(finalizerBlock);
CtBlock methodBlock = factory.Core().createBlock();
methodBlock.addStatement(ctTry);
method.setBody(methodBlock);
if (thisStatement != null) {
ctTry.getBody().removeStatement(thisStatement);
method.getBody().getStatements().add(0, thisStatement);
}
tryBodyMethod.put(methodId(method), ctTry);
}
return tryBodyMethod.get(methodId(method)) ;
}
protected CtStatement getThisOrSuperCall(CtBlock block) {
if(!block.getStatements().isEmpty()) {
CtStatement stmt = block.getStatement(0);
if(stmt.toString().startsWith("this(") || stmt.toString().startsWith("super(")) {
return stmt;
}
}
return null;
}
}