package org.ntlab.deltaViewer;
import java.util.AbstractMap;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.ntlab.deltaExtractor.Alias;
import org.ntlab.deltaExtractor.IAliasCollector;
import org.ntlab.featureExtractor.Extract;
import org.ntlab.trace.MethodExecution;
import org.ntlab.trace.MethodInvocation;
import org.ntlab.trace.Statement;
import org.ntlab.trace.TracePoint;
/**
* CollaborationAliasCollector is IAliasCollector implementation class to merge aliasList in time stamp order.
*
* @author Nitta Lab.
*/
public class CollaborationAliasCollector implements IAliasCollector {
// Execution order.
private List<Alias> aliasList = new ArrayList<>();
public CollaborationAliasCollector(IAliasCollector dac) {
aliasList.addAll(dac.getAliasList());
aliasList = sortAliasListByTimeStamp(aliasList);
}
/*
* Don't write anything here.
*/
@Override
public void addAlias(Alias alias) {
}
@Override
public List<Alias> getAliasList() {
return aliasList;
}
/**
* Merge other into aliasList(this) in time stamp order.
* @param other IAliasCollector to be merged into the aliasList.
* @param extract
* @param newToOldMethodExecMap
*/
public void merge(IAliasCollector other, Extract extract, Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap) {
List<Alias> otherAliasList = other.getAliasList();
otherAliasList = sortAliasListByTimeStamp(otherAliasList);
int otherIdx = 0; // Index in otherAliasList
int thisIdx = 0; // Index in thisAliasList
int thisOrgIdx = 0; // Index in the original thisAliasList
MethodExecution newCallee = null;
while(otherIdx < otherAliasList.size()) {
Alias otherAlias = otherAliasList.get(otherIdx);
if (newCallee != null && newToOldMethodExecMap.get(newCallee).contains(otherAlias.getMethodExecution())) {
Statement st = otherAlias.getOccurrencePoint().getStatement();
if (st instanceof MethodInvocation) {
// Caution!!: The internal structure of the trace is modified.
MethodExecution calleeCallee = ((MethodInvocation) st).getCalledMethodExecution();
calleeCallee.setCaller(newCallee, calleeCallee.getCallerStatementExecution());
}
DummyTracePoint dummyTp = new DummyTracePoint(otherAlias.getOccurrencePoint(), newCallee);
Alias newAlias = new Alias(otherAlias.getAliasType(), otherAlias.getIndex(), otherAlias.getObjectId(), dummyTp);
otherAliasList.set(otherIdx, newAlias);
}
if (thisIdx >= aliasList.size()) {
if (extract != null && extract.isToConnect() && otherIdx == 0) {
Alias thisPrevAlias = aliasList.get(aliasList.size() - 1);
if (!otherAlias.getMethodExecution().isStatic() && otherAlias.getMethodExecution().getCallerMethodExecution() != thisPrevAlias.getMethodExecution()) {
// Add a dummy alias to connect disjunct call hierarchies. (thisPrevAlias -> otherAlias)
MethodExecution caller = thisPrevAlias.getMethodExecution();
MethodExecution oldCallee = otherAlias.getMethodExecution();
MethodExecution callee = null;
if (oldCallee instanceof DummyMethodExecution) {
callee = oldCallee;
} else {
callee = getNewMethodExec(newToOldMethodExecMap, oldCallee);
if (callee == null) {
callee = new DummyMethodExecution(oldCallee);
Set<MethodExecution> oldExecs = new HashSet<>();
oldExecs.add(oldCallee);
newToOldMethodExecMap.put(callee, oldExecs);
newCallee = callee;
}
}
callee.setCaller(caller, caller.getStatements().indexOf(thisPrevAlias.getOccurrencePoint().getStatement()));
DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, thisPrevAlias.getOccurrencePoint().getStatement().getThreadNo());
DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv);
aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp));
thisIdx++;
}
}
aliasList.add(otherAlias);
otherIdx++; thisIdx++;
continue;
}
Alias thisAlias = aliasList.get(thisIdx);
Alias newThisAlias = getUpdatedAlias(thisAlias, newToOldMethodExecMap);
if (newThisAlias != null) {
aliasList.set(thisIdx, newThisAlias);
thisAlias = newThisAlias;
}
if (otherAlias.equals(thisAlias)) {
otherIdx++; thisIdx++; thisOrgIdx++;
} else {
long otherAliasTs = otherAlias.getTimeStamp();
long thisAliasTs = thisAlias.getTimeStamp();
if (otherAliasTs < thisAliasTs) {
if (extract != null && extract.isToConnect() && otherIdx == 0 && thisIdx > 0) {
Alias thisPrevAlias = aliasList.get(thisIdx - 1);
if (!otherAlias.getMethodExecution().isStatic() && otherAlias.getMethodExecution().getCallerMethodExecution() != thisPrevAlias.getMethodExecution()) {
// Add a dummy alias to connect disjunct call hierarchies. (thisPrevAlias -> otherAlias)
MethodExecution caller = thisPrevAlias.getMethodExecution();
MethodExecution oldCallee = otherAlias.getMethodExecution();
MethodExecution callee = null;
if (oldCallee instanceof DummyMethodExecution) {
callee = oldCallee;
} else {
callee = getNewMethodExec(newToOldMethodExecMap, oldCallee);
if (callee == null) {
callee = new DummyMethodExecution(oldCallee);
Set<MethodExecution> oldExecs = new HashSet<>();
oldExecs.add(oldCallee);
newToOldMethodExecMap.put(callee, oldExecs);
newCallee = callee;
}
}
callee.setCaller(caller, caller.getStatements().indexOf(thisPrevAlias.getOccurrencePoint().getStatement()));
DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, thisPrevAlias.getOccurrencePoint().getStatement().getThreadNo());
DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv);
aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp));
thisIdx++;
}
}
aliasList.add(thisIdx, otherAlias);
otherIdx++; thisIdx++;
} else if (otherAliasTs > thisAliasTs) {
if (extract != null && extract.isToConnect() && thisOrgIdx == 0 && otherIdx > 0) {
Alias otherPrevAlias = otherAliasList.get(otherIdx - 1);
if (!thisAlias.getMethodExecution().isStatic() && thisAlias.getMethodExecution().getCallerMethodExecution() != otherPrevAlias.getMethodExecution()) {
// Add a dummy alias to connect disjunct call hierarchies. (otherPrevAlias -> thisAlias)
MethodExecution caller = otherPrevAlias.getMethodExecution();
MethodExecution oldCallee = thisAlias.getMethodExecution();
MethodExecution callee = null;
if (oldCallee instanceof DummyMethodExecution) {
callee = oldCallee;
} else {
callee = getNewMethodExec(newToOldMethodExecMap, oldCallee);
if (callee == null) {
callee = new DummyMethodExecution(oldCallee);
Set<MethodExecution> oldExecs = new HashSet<>();
oldExecs.add(oldCallee);
newToOldMethodExecMap.put(callee, oldExecs);
newCallee = callee;
}
}
callee.setCaller(caller, caller.getStatements().indexOf(otherPrevAlias.getOccurrencePoint().getStatement()));
DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, otherPrevAlias.getOccurrencePoint().getStatement().getThreadNo());
DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv);
aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp));
thisIdx++;
}
}
thisIdx++; thisOrgIdx++;
} else {
if (aliasList.contains(otherAlias)) {
otherIdx++;
} else {
// BUGがあるかもしれません(ACTUAL_ARGUMENTとRECEIVERの出現順)
aliasList.add(thisIdx, otherAlias);
otherIdx++; thisIdx++;
}
}
}
}
if (extract != null && extract.isToConnect() && thisOrgIdx == 0 && otherIdx > 0 && thisIdx < aliasList.size()) {
Alias thisAlias = aliasList.get(thisIdx);
Alias otherPrevAlias = otherAliasList.get(otherIdx - 1);
if (!thisAlias.getMethodExecution().isStatic() && thisAlias.getMethodExecution().getCallerMethodExecution() != otherPrevAlias.getMethodExecution()) {
// Add a dummy alias to connect disjunct call hierarchies. (otherPrevAlias -> thisAlias)
MethodExecution caller = otherPrevAlias.getMethodExecution();
MethodExecution oldCallee = thisAlias.getMethodExecution();
MethodExecution callee = null;
if (oldCallee instanceof DummyMethodExecution) {
callee = oldCallee;
} else {
callee = getNewMethodExec(newToOldMethodExecMap, oldCallee);
if (callee == null) {
callee = new DummyMethodExecution(oldCallee);
Set<MethodExecution> oldExecs = new HashSet<>();
oldExecs.add(oldCallee);
newToOldMethodExecMap.put(callee, oldExecs);
newCallee = callee;
}
}
callee.setCaller(caller, caller.getStatements().indexOf(otherPrevAlias.getOccurrencePoint().getStatement()));
DummyMethodInvocation dummyInv = new DummyMethodInvocation(callee, caller.getThisClassName(), caller.getThisObjId(), 0, otherPrevAlias.getOccurrencePoint().getStatement().getThreadNo());
DummyTracePoint dummyTp = new DummyTracePoint(caller, dummyInv);
aliasList.add(new Alias(Alias.AliasType.RECEIVER, 0, callee.getThisObjId(), dummyTp));
}
}
}
private Alias getUpdatedAlias(Alias alias, Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap) {
Alias newAlias = null;
MethodExecution newExec = getNewMethodExec(newToOldMethodExecMap, alias.getMethodExecution());
Statement st = alias.getOccurrencePoint().getStatement();
DummyMethodInvocation dummyInvocation = null;
if (st instanceof MethodInvocation) {
MethodInvocation mi = ((MethodInvocation) st);
MethodExecution calledExec = mi.getCalledMethodExecution();
MethodExecution newCalledExec = getNewMethodExec(newToOldMethodExecMap, calledExec);
if (newCalledExec != null) {
dummyInvocation = new DummyMethodInvocation(newCalledExec, mi.getThisClassName(), mi.getThisObjId(), 0, mi.getThreadNo());
}
}
if (newExec != null || dummyInvocation != null) {
TracePoint dummyTp = null;
if (newExec == null) {
// Only the called method is to be replaced.
dummyTp = new DummyTracePoint(alias.getMethodExecution(), dummyInvocation);
} else {
if (dummyInvocation == null) {
// Only this method is to be replaced.
Statement originalSt = alias.getOccurrencePoint().getStatement();
dummyTp = new DummyTracePoint(newExec, originalSt);
} else {
// Both the calling and the called methods are to be replaced.
dummyTp = new DummyTracePoint(newExec, dummyInvocation);
}
}
newAlias = new Alias(alias.getAliasType(), alias.getIndex(), alias.getObjectId(), dummyTp);
}
return newAlias;
}
/**
* Sort aliasList in time stamp order.
* @param aliasList AliasList to sort.
* @return Sorted AliasList.
*/
private List<Alias> sortAliasListByTimeStamp(List<Alias> aliasList) {
List<Alias> cloneAliasList = new ArrayList<>(aliasList);
List<Alias> sortedAliasList = cloneAliasList.stream().sorted(new Comparator<Alias>() {
@Override
public int compare(Alias alias1, Alias alias2) {
if (alias1.getTimeStamp() > alias2.getTimeStamp()) return 1;
else if (alias1.getTimeStamp() < alias2.getTimeStamp()) return -1;
return 0;
}
}
).collect(Collectors.toList());
return sortedAliasList;
}
private MethodExecution getNewMethodExec(Map<MethodExecution, Set<MethodExecution>> newToOldMethodExecMap, MethodExecution oldExec) {
for (MethodExecution newExec: newToOldMethodExecMap.keySet()) {
if (newToOldMethodExecMap.get(newExec).contains(oldExec)) return newExec;
}
return null;
}
}