package org.ntlab.deltaViewer;
import java.util.ArrayList;
import java.util.Comparator;
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.ExtractedStructure;
import org.ntlab.trace.MethodExecution;
import org.ntlab.trace.Reference;
import org.ntlab.trace.TracePoint;
/**
* CollaborationObjectCallGraph is IObjectCallGraph implementation class to merge ExtractedStructure.
*
* @author Nitta Lab.
*/
public class CollaborationObjectCallGraph implements IObjectCallGraph {
private Set<Reference> references = new HashSet<>();
private List<MethodExecution> startPoints = new ArrayList<>(); // Common ancestor point
private List<TracePoint> relatedPoints = new ArrayList<>();
public CollaborationObjectCallGraph(ExtractedStructure e) {
references.addAll(e.getDelta().getSrcSide());
references.addAll(e.getDelta().getDstSide());
startPoints.add(e.getCoordinator());
relatedPoints.add(e.getRelatedTracePoint().duplicate());
}
@Override
public List<Reference> getReferences() {
return new ArrayList<Reference>(references); // Convert to List from Set.
}
@Override
public List<MethodExecution> getStartPoints() {
return startPoints;
}
@Override
public List<TracePoint> getRelatedPoints() {
return relatedPoints;
}
@Override
public Map<MethodExecution, List<MethodExecution>> getCallTree() {
return null;
}
/**
* Merge ExtractedStructure not to overlap reference.
* @param e ExtractedStructure to be merged into the field.
*/
public void merge(ExtractedStructure e) {
references.addAll(e.getDelta().getSrcSide());
references.addAll(e.getDelta().getDstSide());
// There may be bug. (Two object has each coordinator like JHotDraw Transform)
MethodExecution thisStartPoint = startPoints.get(0);
MethodExecution tmp = thisStartPoint;
MethodExecution thisRoot = thisStartPoint.getParent();
MethodExecution otherStartPoint = e.getCoordinator();
while(thisRoot != null) { // Get Root of thisStartPoint.
tmp = tmp.getParent();
thisRoot = tmp.getParent();
}
thisRoot = tmp;
/* lowest common ancestor algorithm */
MethodExecution lca = lowestCommonAncestor(thisRoot, thisStartPoint, otherStartPoint);
startPoints.clear();
startPoints.add(lca);
// Is it in time stamp order?
TracePoint otherRelatedTp = e.getRelatedTracePoint().duplicate();
relatedPoints.add(otherRelatedTp);
relatedPoints = sortTracePointByTimeStamp(relatedPoints);
}
/**
* Search lowest common ancestor(lca) of p and q.
* @param root
* @param p
* @param q
* @return Lca methodExecution.
*/
public MethodExecution lowestCommonAncestor(MethodExecution root, MethodExecution p, MethodExecution q) {
if(root == null || root == p || root == q) return root;
Set<MethodExecution> pRoots = new HashSet<>();
MethodExecution pTmp = p;
while (!root.equals(pTmp) && pTmp != null) {
pRoots.add(pTmp);
pTmp = pTmp.getParent();
}
pRoots.add(root);
MethodExecution qTmp = q;
while (!root.equals(qTmp) && qTmp != null) {
if (pRoots.contains(qTmp)) return qTmp;
qTmp = qTmp.getParent();
}
return root;
}
/**
* Sort tracePoint in time stamp order.
* @param tpList TracePoint List to sort.
* @return Sorted TracePoint List.
*/
private List<TracePoint> sortTracePointByTimeStamp(List<TracePoint> tpList) {
List<TracePoint> cloneTpList = new ArrayList<>(tpList);
List<TracePoint> sortedTpList = cloneTpList.stream().sorted(new Comparator<TracePoint>() {
@Override
public int compare(TracePoint tp1, TracePoint tp2) {
long tp1TimeStamp = tp1.getStatement().getTimeStamp();
long tp2TimeStamp = tp2.getStatement().getTimeStamp();
if (tp1TimeStamp > tp2TimeStamp) return 1;
else if (tp1TimeStamp < tp2TimeStamp) return -1;
return 0;
}
}).collect(Collectors.toList());
return sortedTpList;
}
}