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; } }