|
@@ -8,76 +8,179 @@ import org.graphstream.graph.Node;
|
|
|
|
|
|
import de.tu_darmstadt.informatik.tk.scopviz.debug.Debug;
|
|
|
|
|
|
+/**
|
|
|
+ * Class extending GraphManager. Offers the possibility to merge an underlay and
|
|
|
+ * an operator graph.
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * @author Matthias Wilhelm
|
|
|
+ *
|
|
|
+ */
|
|
|
public class MappingGraphManager extends GraphManager implements EdgeCreatedListener, NodeCreatedListener {
|
|
|
- public static final String UNDERLAYER_PREFIX = "underlay";
|
|
|
- public static final String OPERATOR_PREFIX = "operator";
|
|
|
+ public static final String UNDERLAY = "underlay";
|
|
|
+ public static final String OPERATOR = "operator";
|
|
|
+
|
|
|
+ public static final String ATTRIBUTE_KEY_PROCESS_NEED = "process-need";
|
|
|
+ public static final String ATTRIBUTE_KEY_PROCESS_USE = "process-use";
|
|
|
+ public static final String ATTRIBUTE_KEY_PROCESS_MAX = "process-max";
|
|
|
+ public static final String ATTRIBUTE_KEY_MAPPING = "mapping";
|
|
|
+ public static final String ATTRIBUTE_KEY_MAPPING_PARENT = "mapping-parent";
|
|
|
+ public static final String ATTRIBUTE_KEY_MAPPING_PARENT_ID = "mapping-parent-id";
|
|
|
+ public static final String UI_CLASS_MAPPING = "mapping";
|
|
|
+
|
|
|
private static final double UNDERLAYER_MOVE_Y = 0;
|
|
|
private static final double OPERATOR_MOVE_Y = 1.5;
|
|
|
+
|
|
|
+ // Variables to keep track of new Nodes in the parent graphs
|
|
|
private boolean underlayNodesChanged = false;
|
|
|
private boolean operatorNodesChanged = false;
|
|
|
|
|
|
+ // References to the parent graphs
|
|
|
GraphManager underlay, operator;
|
|
|
+ HashMap<String, String> parentsID;;
|
|
|
|
|
|
+ /**
|
|
|
+ * Creates a new manager for an empty graph. there is no need to check for
|
|
|
+ * unique ID's for nodes and edges.
|
|
|
+ *
|
|
|
+ * @param graph
|
|
|
+ * assumes a empty graph
|
|
|
+ * @param underlay
|
|
|
+ * the underlay graph
|
|
|
+ * @param operator
|
|
|
+ * the operator graph
|
|
|
+ */
|
|
|
public MappingGraphManager(MyGraph graph, GraphManager underlay, GraphManager operator) {
|
|
|
super(graph);
|
|
|
+
|
|
|
+ underlay.deselect();
|
|
|
+ operator.deselect();
|
|
|
+
|
|
|
this.underlay = underlay;
|
|
|
this.operator = operator;
|
|
|
+
|
|
|
+ parentsID = new HashMap<>();
|
|
|
+ parentsID.put(UNDERLAY, underlay.getGraph().getId());
|
|
|
+ parentsID.put(OPERATOR, operator.getGraph().getId());
|
|
|
+
|
|
|
Debug.out("Created a new Mapping");
|
|
|
- mergeGraph(underlay, UNDERLAYER_PREFIX, UNDERLAYER_MOVE_Y);
|
|
|
- mergeGraph(operator, OPERATOR_PREFIX, OPERATOR_MOVE_Y);
|
|
|
+
|
|
|
+ mergeGraph(underlay, UNDERLAY, UNDERLAYER_MOVE_Y);
|
|
|
+ mergeGraph(operator, OPERATOR, OPERATOR_MOVE_Y);
|
|
|
+
|
|
|
view.getCamera().resetView();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Adds all nodes and edges of the given graph, adds a prefix to the ID of
|
|
|
+ * every node and edge and offsets the normalized coordinates in y
|
|
|
+ * direction.
|
|
|
+ *
|
|
|
+ * @param gm
|
|
|
+ * the graph to be added
|
|
|
+ * @param idPrefix
|
|
|
+ * the prefix for the ID of every node and edge
|
|
|
+ * @param moveY
|
|
|
+ * the offset of the y coordinate
|
|
|
+ */
|
|
|
private void mergeGraph(GraphManager gm, String idPrefix, double moveY) {
|
|
|
-
|
|
|
mergeNodes(gm, idPrefix, moveY);
|
|
|
+
|
|
|
+ // TODO: Debug only
|
|
|
int i = 0;
|
|
|
+
|
|
|
for (Edge edge : gm.getGraph().getEdgeSet()) {
|
|
|
addEdge(edge, idPrefix);
|
|
|
+
|
|
|
+ // TODO: Debug only
|
|
|
i++;
|
|
|
}
|
|
|
+
|
|
|
Debug.out("added " + i + " Edge" + (i == 1 ? "" : "s") + " from \"" + idPrefix + "\"");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Adds all nodes of the given graph, adds a prefix to the ID of every node
|
|
|
+ * and offsets the normalized coordinates in y direction.
|
|
|
+ *
|
|
|
+ * @param gm
|
|
|
+ * the graph to be added
|
|
|
+ * @param idPrefix
|
|
|
+ * the prefix for the ID of every node
|
|
|
+ * @param moveY
|
|
|
+ * the offset of the y coordinate
|
|
|
+ */
|
|
|
private void mergeNodes(GraphManager gm, String idPrefix, double moveY) {
|
|
|
- int i = 0;
|
|
|
+ // precalculate scale and offset to normalize x coordinates
|
|
|
double maxX = gm.getMaxX();
|
|
|
double minX = gm.getMinX();
|
|
|
double scaleX = 1 / (maxX - minX);
|
|
|
double addX = -minX * scaleX;
|
|
|
+
|
|
|
+ // precalculate scale and offset to normalize y coordinates
|
|
|
double maxY = gm.getMaxY();
|
|
|
double minY = gm.getMinY();
|
|
|
double scaleY = 1 / (maxY - minY);
|
|
|
- double addY = -minY * scaleY;
|
|
|
+ double addY = -minY * scaleY + moveY;
|
|
|
+
|
|
|
+ // TODO: Debug only
|
|
|
+ int i = 0;
|
|
|
+
|
|
|
+ // loops through all nodes, adds them if they don't exist already and
|
|
|
+ // normalizes their coordinates
|
|
|
for (Node node : gm.getGraph().getNodeSet()) {
|
|
|
+ // add node if it doesn't exist
|
|
|
Node newNode = getGraph().getNode(idPrefix + node.getId());
|
|
|
if (newNode == null) {
|
|
|
addNode(node, idPrefix);
|
|
|
newNode = getGraph().getNode(idPrefix + node.getId());
|
|
|
+
|
|
|
+ // TODO: Debug only
|
|
|
i++;
|
|
|
}
|
|
|
+
|
|
|
+ // normalize coordinates
|
|
|
double[] n = Toolkit.nodePosition(node);
|
|
|
double cX = n[0];
|
|
|
double x = cX * scaleX + addX;
|
|
|
double cY = n[1];
|
|
|
- double y = cY * scaleY + addY + moveY;
|
|
|
+ double y = cY * scaleY + addY;
|
|
|
newNode.changeAttribute("x", x);
|
|
|
newNode.changeAttribute("y", y);
|
|
|
+
|
|
|
+ if (hasClass(newNode, UI_CLASS_PROCESSING_ENABLED))
|
|
|
+ initCapacity(newNode);
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
Debug.out("added " + i + " Node" + (i == 1 ? "" : "s") + " from \"" + idPrefix + "\"");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Gets invoked by the GraphDisplayManager every time the mapping layer is
|
|
|
+ * loaded. Checks whether nodes have been added to the parent graphs
|
|
|
+ */
|
|
|
public void activated() {
|
|
|
if (underlayNodesChanged) {
|
|
|
- mergeNodes(underlay, UNDERLAYER_PREFIX, UNDERLAYER_MOVE_Y);
|
|
|
+ mergeNodes(underlay, UNDERLAY, UNDERLAYER_MOVE_Y);
|
|
|
underlayNodesChanged = false;
|
|
|
}
|
|
|
+
|
|
|
if (operatorNodesChanged) {
|
|
|
- mergeNodes(operator, OPERATOR_PREFIX, OPERATOR_MOVE_Y);
|
|
|
+ mergeNodes(operator, OPERATOR, OPERATOR_MOVE_Y);
|
|
|
operatorNodesChanged = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Checks whether the given graph is underlay or operator graph to this
|
|
|
+ * object or not.
|
|
|
+ *
|
|
|
+ * @param gm
|
|
|
+ * the graph to check
|
|
|
+ * @return true if the given graph is underlay or operator graph to this
|
|
|
+ * graph. false otherwise
|
|
|
+ */
|
|
|
public boolean hasGraphManagerAsParent(GraphManager gm) {
|
|
|
return (underlay.getGraph().getId().equals(gm.getGraph().getId()))
|
|
|
|| (operator.getGraph().getId().equals(gm.getGraph().getId()));
|
|
@@ -98,6 +201,11 @@ public class MappingGraphManager extends GraphManager implements EdgeCreatedList
|
|
|
for (String s : e.getAttributeKeySet()) {
|
|
|
attributes.put(s, e.getAttribute(s));
|
|
|
}
|
|
|
+
|
|
|
+ attributes.put(ATTRIBUTE_KEY_MAPPING, false);
|
|
|
+ attributes.put(ATTRIBUTE_KEY_MAPPING_PARENT, idPrefix);
|
|
|
+ attributes.put(ATTRIBUTE_KEY_MAPPING_PARENT_ID, parentsID.get(idPrefix));
|
|
|
+
|
|
|
g.addEdge(idPrefix + e.getId(), idPrefix + e.getSourceNode().getId(), idPrefix + e.getTargetNode().getId());
|
|
|
g.getEdge(idPrefix + e.getId()).addAttributes(attributes);
|
|
|
}
|
|
@@ -123,25 +231,174 @@ public class MappingGraphManager extends GraphManager implements EdgeCreatedList
|
|
|
for (String s : n.getAttributeKeySet()) {
|
|
|
attributes.put(s, n.getAttribute(s));
|
|
|
}
|
|
|
+
|
|
|
+ attributes.put(ATTRIBUTE_KEY_MAPPING_PARENT, idPrefix);
|
|
|
+ attributes.put(ATTRIBUTE_KEY_MAPPING_PARENT_ID, parentsID.get(idPrefix));
|
|
|
+
|
|
|
g.addNode(idPrefix + n.getId());
|
|
|
g.getNode(idPrefix + n.getId()).addAttributes(attributes);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void nodeCreated(Node n, String graphID) {
|
|
|
- Debug.out("Node " + n + " was added to Graph " + graphID);
|
|
|
if (graphID.equals(underlay.getGraph().getId()))
|
|
|
underlayNodesChanged = true;
|
|
|
- else
|
|
|
+ else if (graphID.equals(operator.getGraph().getId()))
|
|
|
operatorNodesChanged = true;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void edgeCreated(Edge e, String graphID) {
|
|
|
- Debug.out("Edge " + e + " was added to Graph " + graphID);
|
|
|
if (graphID.equals(underlay.getGraph().getId()))
|
|
|
- addEdge(e, UNDERLAYER_PREFIX);
|
|
|
- else
|
|
|
- addEdge(e, OPERATOR_PREFIX);
|
|
|
+ addEdge(e, UNDERLAY);
|
|
|
+ else if (graphID.equals(operator.getGraph().getId()))
|
|
|
+ addEdge(e, OPERATOR);
|
|
|
}
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void createEdges(String id) {
|
|
|
+ super.createEdges(id);
|
|
|
+
|
|
|
+ if (lastClickedID != null) {
|
|
|
+ Double need = g.getNode(lastClickedID).getAttribute(ATTRIBUTE_KEY_PROCESS_NEED);
|
|
|
+ if (need != null)
|
|
|
+ for (Node n : g.getNodeSet())
|
|
|
+ if (hasClass(n, UI_CLASS_PROCESSING_ENABLED))
|
|
|
+ showExpectedCapacity(n, need);
|
|
|
+ } else {
|
|
|
+ for (Node n : g.getNodeSet())
|
|
|
+ if (hasClass(n, UI_CLASS_PROCESSING_ENABLED))
|
|
|
+ showExpectedCapacity(n, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * checks whether the Node can handle the load first.<br/>
|
|
|
+ * creates a edge between to nodes
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public boolean createEdge(String to, String from) {
|
|
|
+ Node fromNode = getGraph().getNode(from);
|
|
|
+ Node toNode = getGraph().getNode(to);
|
|
|
+
|
|
|
+ if (fromNode.hasEdgeBetween(to))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ String fromParent = fromNode.getAttribute(ATTRIBUTE_KEY_MAPPING_PARENT);
|
|
|
+ String toParent = toNode.getAttribute(ATTRIBUTE_KEY_MAPPING_PARENT);
|
|
|
+
|
|
|
+ if (fromParent == null || toParent == null)
|
|
|
+ return false;
|
|
|
+ if (fromParent.equals(toParent)) {
|
|
|
+ deselectNodesAfterEdgeCreation(lastClickedID);
|
|
|
+ lastClickedID = to;
|
|
|
+ selectNodeForEdgeCreation(lastClickedID);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ String newID = Main.getInstance().getUnusedID();
|
|
|
+
|
|
|
+ Edge e;
|
|
|
+
|
|
|
+ Node underlayNode;
|
|
|
+ Node operatorNode;
|
|
|
+ if (fromParent.equals(UNDERLAY)) {
|
|
|
+ underlayNode = fromNode;
|
|
|
+ operatorNode = toNode;
|
|
|
+ } else if (toParent.equals(UNDERLAY)) {
|
|
|
+ underlayNode = toNode;
|
|
|
+ operatorNode = fromNode;
|
|
|
+ } else
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // check if processing enabled node
|
|
|
+ if (!hasClass(underlayNode, UI_CLASS_PROCESSING_ENABLED))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // check and update capacity
|
|
|
+ if (!updatedCapacity(underlayNode, operatorNode))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ e = getGraph().addEdge(newID, operatorNode, underlayNode, true);
|
|
|
+ Debug.out("Created an directed edge with Id " + newID + " from " + operatorNode + " to " + underlayNode);
|
|
|
+
|
|
|
+ e.addAttribute("ui.class", UI_CLASS_MAPPING);
|
|
|
+ e.addAttribute(ATTRIBUTE_KEY_MAPPING, true);
|
|
|
+
|
|
|
+ selectEdge(newID);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initCapacity(Node underlayNode) {
|
|
|
+ Double used = underlayNode.getAttribute(ATTRIBUTE_KEY_PROCESS_USE);
|
|
|
+ Double max = underlayNode.getAttribute(ATTRIBUTE_KEY_PROCESS_MAX);
|
|
|
+ if (max == null || max == 0)
|
|
|
+ return;
|
|
|
+ if (used == null)
|
|
|
+ used = new Double(0);
|
|
|
+ double[] pieValues = { used / max, 0, 1 - used / max };
|
|
|
+ underlayNode.setAttribute("ui.pie-values", pieValues);
|
|
|
+ underlayNode.setAttribute(ATTRIBUTE_KEY_PROCESS_USE, used);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean updatedCapacity(Node underlayNode, Node operatorNode) {
|
|
|
+ Double needed = operatorNode.getAttribute(ATTRIBUTE_KEY_PROCESS_NEED);
|
|
|
+ Double used = underlayNode.getAttribute(ATTRIBUTE_KEY_PROCESS_USE);
|
|
|
+ Double max = underlayNode.getAttribute(ATTRIBUTE_KEY_PROCESS_MAX);
|
|
|
+
|
|
|
+ if (needed == null)
|
|
|
+ return true;
|
|
|
+ if (max == null || max == 0)
|
|
|
+ if (needed > 0)
|
|
|
+ return false;
|
|
|
+ else
|
|
|
+ return true;
|
|
|
+ if (used == null)
|
|
|
+ used = new Double(0);
|
|
|
+ if (used + needed > max)
|
|
|
+ return false;
|
|
|
+ used += needed;
|
|
|
+ double[] pieValues = { used / max, 0, 1 - used / max };
|
|
|
+ underlayNode.setAttribute("ui.pie-values", pieValues);
|
|
|
+ underlayNode.setAttribute(ATTRIBUTE_KEY_PROCESS_USE, used);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void showExpectedCapacity(Node underlayNode, double need) {
|
|
|
+ Double used = underlayNode.getAttribute(ATTRIBUTE_KEY_PROCESS_USE);
|
|
|
+ Double max = underlayNode.getAttribute(ATTRIBUTE_KEY_PROCESS_MAX);
|
|
|
+ if (max == null || max == 0)
|
|
|
+ return;
|
|
|
+ if (used == null)
|
|
|
+ used = new Double(0);
|
|
|
+ double[] pieValues = { used / max, 0, 1 - used / max, 0 };
|
|
|
+ if (need + used > max) {
|
|
|
+ pieValues[3] = pieValues[2];
|
|
|
+ pieValues[2] = 0;
|
|
|
+ } else {
|
|
|
+ pieValues[1] = need / max;
|
|
|
+ pieValues[2] -= need / max;
|
|
|
+ }
|
|
|
+
|
|
|
+ underlayNode.setAttribute("ui.pie-values", pieValues);
|
|
|
+ underlayNode.setAttribute(ATTRIBUTE_KEY_PROCESS_USE, used);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean selectNodeForEdgeCreation(String nodeID) {
|
|
|
+ Node n = g.getNode(nodeID);
|
|
|
+ String parent = n.getAttribute(ATTRIBUTE_KEY_MAPPING_PARENT);
|
|
|
+ if (parent == null)
|
|
|
+ return false;
|
|
|
+ if (parent.equals(OPERATOR))
|
|
|
+ return super.selectNodeForEdgeCreation(nodeID);
|
|
|
+ if (hasClass(n, UI_CLASS_PROCESSING_ENABLED))
|
|
|
+ return super.selectNodeForEdgeCreation(nodeID);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void deleteEdge(final String id) {
|
|
|
+ super.deleteEdge(id);
|
|
|
+ }
|
|
|
+
|
|
|
}
|