package org.ntlab.deltaViewer;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.ntlab.deltaExtractor.Alias;
import org.ntlab.deltaExtractor.Alias.AliasType;
import org.ntlab.deltaExtractor.IAliasCollector;
import org.ntlab.trace.ArrayAccess;
import org.ntlab.trace.ArrayCreate;
import org.ntlab.trace.ArrayUpdate;
import org.ntlab.trace.FieldAccess;
import org.ntlab.trace.FieldUpdate;
import org.ntlab.trace.MethodExecution;
import org.ntlab.trace.MethodInvocation;
import org.ntlab.trace.Reference;
import org.ntlab.trace.Statement;
import com.mxgraph.model.mxICell;
import com.mxgraph.util.mxPoint;
public class ForwardLayout implements IObjectLayout {
private static int angleStep = 15;
private mxPoint coordinatorPoint = new mxPoint(0, 100);
private double step;
private double padding;
@Override
public void execute(IObjectCallGraph objectCallGraph, IAliasCollector aliasCollector, Map<String, ObjectVertex> objectToVertexMap) {
step = 250;
padding = 200;
// Extract the reference access history.
List<Reference> references = objectCallGraph.getReferences();
Map<Reference, List<Integer>> referenceHistory = new HashMap<>();
int order = 0;
for (Alias a: aliasCollector.getAliasList()) {
int idx = -1;
if (a.getAliasType() == AliasType.FIELD) {
FieldAccess f = (FieldAccess) a.getOccurrencePoint().getStatement();
idx = references.indexOf(new Reference(f.getContainerObjId(), f.getValueObjId(), f.getContainerClassName(), f.getValueClassName()));
} else if (a.getAliasType() == AliasType.ARRAY_ELEMENT) {
ArrayAccess aa = (ArrayAccess) a.getOccurrencePoint().getStatement();
idx = references.indexOf(new Reference(aa.getArrayObjectId(), aa.getValueObjectId(), aa.getArrayClassName(), aa.getValueClassName()));
} else if (a.getAliasType() == AliasType.RETURN_VALUE) {
MethodExecution methodExec = a.getMethodExecution();
if (methodExec.getSignature().contains("List.get(") ||
methodExec.getSignature().contains("Map.get(")) {
String srcObjId = methodExec.getThisObjId();
String dstObjId = methodExec.getReturnValue().getId();
String srcClassName = methodExec.getThisClassName();
String dstClassName = methodExec.getReturnValue().getActualType();
idx = references.indexOf(new Reference(srcObjId, dstObjId, srcClassName, dstClassName));
}
} else if (a.getAliasType() == AliasType.CONSTRACTOR_INVOCATION) {
MethodInvocation c = (MethodInvocation) a.getOccurrencePoint().getStatement();
String srcObjId = a.getMethodExecution().getThisObjId();
String dstObjId = c.getCalledMethodExecution().getThisObjId();
String srcClassName = a.getMethodExecution().getThisClassName();
String dstClassName = c.getCalledMethodExecution().getThisClassName();
idx = references.indexOf(new Reference(srcObjId, dstObjId, srcClassName, dstClassName));
} else if (a.getAliasType() == AliasType.ARRAY_CREATE) {
ArrayCreate ac = (ArrayCreate) a.getOccurrencePoint().getStatement();
String srcObjId = a.getMethodExecution().getThisObjId();
String srcClassName = a.getMethodExecution().getThisClassName();
idx = references.indexOf(new Reference(srcObjId, ac.getArrayObjectId(), srcClassName, ac.getArrayClassName()));
}
if (idx >= 0) {
Reference r = references.get(idx);
List<Integer> rHIstory = referenceHistory.get(r);
if (rHIstory == null) {
rHIstory = new ArrayList<>();
referenceHistory.put(r, rHIstory);
}
rHIstory.add(order);
}
order++;
}
// Construct the object graph.
Map<String, List<Reference>> succs = new HashMap<>();
for (Reference r: references) {
String srcObjId = r.getSrcObjectId();
List<Reference> suc = succs.get(srcObjId);
if (suc == null) {
suc = new ArrayList<Reference>();
succs.put(srcObjId, suc);
}
suc.add(r);
}
// Fix the all paths from the top object.
double width = 1;
coordinatorPoint.setX(coordinatorPoint.getX() + step * width + padding);
double xCor = coordinatorPoint.getX();
double yCor = coordinatorPoint.getY();
Set<String> fixed = new HashSet<>();
String topObjId = objectCallGraph.getStartPoints().get(0).getThisObjId();
setVertexCoordinate(objectToVertexMap.get(topObjId), xCor, yCor);
fixed.add(topObjId);
String curObjId = topObjId;
List<Reference> nextRefs = new ArrayList<>(succs.get(curObjId));
if (nextRefs.size() > 0) {
List<Reference> nextRefs2 = new ArrayList<>();
do {
Reference firstRef = getFirstReferece(nextRefs, referenceHistory, true);
nextRefs2.add(firstRef);
nextRefs.remove(firstRef);
} while (nextRefs.size() > 1);
nextRefs2.add(nextRefs.remove(0));
double direction = 225;
for (Reference r: nextRefs2) {
List<String> path = new ArrayList<>();
path.add(r.getSrcObjectId());
traverseSuccs(succs, r, direction, true, referenceHistory, objectToVertexMap, fixed, path);
if (nextRefs2.size() > 1) direction = getDirection(r, objectToVertexMap) + 180 + 90 / (nextRefs2.size() - 1);
}
}
}
private double getDirection(Reference ref, Map<String, ObjectVertex> objToVtx) {
ObjectVertex src = objToVtx.get(ref.getSrcObjectId());
ObjectVertex dst = objToVtx.get(ref.getDstObjectId());
return Math.toDegrees(Math.atan2(dst.getY() - src.getY(), src.getX() - dst.getX()));
}
private Reference getFirstReferece(List<Reference> refs, Map<Reference, List<Integer>> referenceHistory, boolean bLeftFirst) {
double first = 0.0;
Reference firstRef = null;
for (Reference ref: refs) {
double avgOrder = 0.0;
if (referenceHistory.get(ref) != null) {
for (int ord: referenceHistory.get(ref)) {
avgOrder += (double) ord;
}
avgOrder /= ((double) referenceHistory.get(ref).size());
}
if (firstRef == null || (bLeftFirst && avgOrder < first) || (!bLeftFirst && avgOrder > first)) {
firstRef = ref;
first = avgOrder;
}
}
return firstRef;
}
private void traverseSuccs(Map<String, List<Reference>> succs, Reference ref, double direction, boolean bLeftFirst, Map<Reference, List<Integer>> referenceHistory, Map<String, ObjectVertex> objToVtx, Set<String> fixed, List<String> path) {
String curObj = ref.getDstObjectId();
path.add(curObj);
if (fixed.contains(curObj)) {
if (path.size() > 2) fixPath(path, direction, objToVtx, fixed, true);
return;
}
List<Reference> nextRefs = succs.get(curObj);
if (nextRefs == null || nextRefs.size() == 0) {
if (path.size() >= 2) fixPath(path, direction, objToVtx, fixed, false);
return;
}
nextRefs = new ArrayList<>(nextRefs);
do {
Reference firstRef = getFirstReferece(nextRefs, referenceHistory, bLeftFirst);
traverseSuccs(succs, firstRef, direction, bLeftFirst, referenceHistory, objToVtx, fixed, path);
path = new ArrayList<>();
path.add(curObj);
nextRefs.remove(firstRef);
direction = getDirection(firstRef, objToVtx) + 180;
if (bLeftFirst) {
direction += angleStep;
} else {
direction -= angleStep;
}
} while (nextRefs.size() > 0);
return;
}
private void fixPath(List<String> path, double direction, Map<String, ObjectVertex> objToVtx, Set<String> fixed, boolean bClose) {
if (path.size() < 2) return;
ObjectVertex start = objToVtx.get(path.get(0));
double x = start.getX();
double y = start.getY();
double dirY = -Math.sin(Math.toRadians(direction));
double dirX = Math.cos(Math.toRadians(direction));
if (!bClose) {
// straight line
for (int i = 1; i < path.size(); i++) {
x += step * dirX;
y += step * dirY;
setVertexCoordinate(objToVtx.get(path.get(i)), x, y);
fixed.add(path.get(i));
}
} else {
ObjectVertex end = objToVtx.get(path.get(path.size() - 1));
double diffX = end.getX() - start.getX();
double diffY = end.getY() - start.getY();
double distance = Math.sqrt(diffX * diffX + diffY * diffY);
diffX /= distance;
diffY /= distance;
double outer = dirX * diffY - dirY * diffX;
double centerX, centerY, vecX, vecY;
double theta = Math.acos(diffX * dirX + diffY * dirY);
double radius = distance / 2 / Math.sin(theta);
if (outer > 0) {
vecX = -dirY;
vecY = dirX;
theta = theta * 2.0 / (double) (path.size() - 1);
} else if (outer < 0) {
vecX = dirY;
vecY = -dirX;
theta = -theta * 2.0 / (double) (path.size() - 1);
} else {
// straight line
diffX *= distance / (double) (path.size() - 1);
diffY *= distance / (double) (path.size() - 1);
for (int i = 1; i < path.size(); i++) {
x += diffX;
y += diffY;
setVertexCoordinate(objToVtx.get(path.get(i)), x, y);
fixed.add(path.get(i));
}
return;
}
// arc
centerX = x + radius * vecX;
centerY = y + radius * vecY;
for (int i = 1; i < path.size() - 1; i++) {
double tmp = vecX * Math.cos(theta) - vecY * Math.sin(theta);
vecY = vecX * Math.sin(theta) + vecY * Math.cos(theta);
vecX = tmp;
setVertexCoordinate(objToVtx.get(path.get(i)), centerX - radius * vecX, centerY - radius * vecY);
fixed.add(path.get(i));
}
}
}
private void setVertexCoordinate(ObjectVertex vertex, double x, double y) {
vertex.setX(x);
vertex.setY(y);
vertex.setInitialPoint(x, y);
}
}