diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java index e118527..bf60237 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java @@ -14,7 +14,6 @@ import models.Edge; import models.Node; import models.controlFlowModel.*; -import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.ResourceNode; @@ -26,12 +25,6 @@ public final int PORT_DIAMETER = 8; public final int PORT_RADIUS = PORT_DIAMETER / 2; - public static final int MIN_RESOURCE_NODE_WIDTH = 100; - public static final int MIN_RESOURCE_NODE_HEIGHT = 50; - - public static final int MIN_EVENT_CHANNEL_NODE_WIDTH = 100; - public static final int MIN_EVENT_CHANNEL_NODE_HEIGHT = 50; - private ControlFlowGraph controlFlowGraph = null; public ControlFlowModelingStage(mxGraphComponent graphComponent) { @@ -51,13 +44,14 @@ @Override public void init(Stage prevStage) { if (prevStage instanceof PushPullSelectionStage) { - model = prevStage.getModel(); + PushPullSelectionStage stage = (PushPullSelectionStage) prevStage; + model = stage.getModel(); - DataFlowGraph dataFlowGraph = ((PushPullSelectionStage) prevStage).getDataFlowGraph(); + DataFlowGraph dataFlowGraph = stage.getDataFlowGraph(); controlFlowGraph = new ControlFlowGraph(dataFlowGraph, model); clearControlFlowGraphCells(graph); - graph = constructGraph(graph); + graph = constructGraph(graph, stage.getResNodeToCell(), stage.getChannelToCell()); } } @@ -103,21 +97,28 @@ } /** - * Construct a control-flow graph on an mxGraph + * Constructs and initializes a control flow graph for the PUSH and PULL layers. This method creates graph cells + * for resource nodes and event-channel nodes, and connects them with edges corresponding to the control flow. + * Updates to the graph are performed within a single transaction for consistency. * - * @param graph The mxGraph to be constructed + * @param graph The {@link mxGraph} instance to be constructed and updated. + * @param resNodeToCell A map associating {@link ResourceNode} instances to their corresponding {@link mxCell} + * instances in the graph. + * @param channelToCell A map associating {@link DataTransferChannel} instances to their corresponding + * {@link mxCell} instances in the graph. + * @return The updated {@link mxGraph} instance after constructing the control flow graph. */ - private mxGraph constructGraph(mxGraph graph) { + private mxGraph constructGraph(mxGraph graph, final Map resNodeToCell, final Map channelToCell) { showLayers(PUSH_FLOW_LAYER, PULL_FLOW_LAYER); graph.getModel().beginUpdate(); try { // Create cells correspond to resource nodes for the PUSH/PULL layer. - Map pushResourceNodeCells = createResourceNodeCells(graph, PUSH_FLOW_LAYER); - Map pullResourceNodeCells = createResourceNodeCells(graph, PULL_FLOW_LAYER); + Map pushResourceNodeCells = createResourceNodeCells(graph, resNodeToCell, PUSH_FLOW_LAYER); + Map pullResourceNodeCells = createResourceNodeCells(graph, resNodeToCell, PULL_FLOW_LAYER); // Create cells correspond to event-channel nodes for the PUSH layer. - Map pushFlowEventNodeCells = createEventChannelNodeCells(graph, PUSH_FLOW_LAYER); + Map pushFlowEventNodeCells = createEventChannelNodeCells(graph, channelToCell, PUSH_FLOW_LAYER); // Insert edges between the connected vertices graph = insertControlFlowEdges(PUSH_FLOW_LAYER, pushResourceNodeCells, pushFlowEventNodeCells); @@ -150,20 +151,26 @@ } /** - * Create and register a resource node cell on the specified layer. + * Creates and registers resource node cells on the specified layer of the given graph. This method processes + * the resource nodes in the call graph corresponding to the specified layer and maps them to newly created + * graph cells. It uses the original resource node cells as templates for dimensions, coordinates, and styles. * - * @param graph The {@link mxGraph} to be modified. - * @param layer The layer number - * @return The map of {@link ResourceNode} and its corresponding cell. + * @param graph The {@link mxGraph} instance where the resource node cells will be created and added. + * @param originalResourceNodeCells A map of {@link ResourceNode} instances to their original {@link mxCell} instances. + * Used for determining dimensions, coordinates, and styles of the new cells. + * @param layer The layer number. Must be either {@code PUSH_FLOW_LAYER} or {@code PULL_FLOW_LAYER}. + * @return A map associating {@link StatefulObjectNode} instances to the created {@link mxCell} instances + * representing the resource nodes in the control flow graph. + * @throws IllegalArgumentException if the provided layer is neither {@code PUSH_FLOW_LAYER} nor {@code PULL_FLOW_LAYER}. */ - private Map createResourceNodeCells(final mxGraph graph, final int layer) { + private Map createResourceNodeCells(final mxGraph graph, final Map originalResourceNodeCells, final int layer) { if (layer != PUSH_FLOW_LAYER && layer != PULL_FLOW_LAYER) { throw new IllegalArgumentException("The layer number must be either PUSH_FLOW_LAYER or PULL_FLOW_LAYER."); } final mxCell root = (mxCell) graph.getDefaultParent(); final mxCell layerCell = (mxCell) root.getChildAt(layer); - final Map resourceNodeCells = new HashMap<>(); + final Map resourceNodeCells = new HashMap<>(); final CallGraph callGraph = getCallGraph(layer); if (callGraph == null) { @@ -175,106 +182,63 @@ if (!(rootNode instanceof StatefulObjectNode)) { continue; } - ResourceNode resourceNode = ((StatefulObjectNode) rootNode).getResource(); - ResourcePath resourcePath = resourceNode.getPrimaryResourcePath(); - - int width = calculateRequiredWidth(resourceNode); - int height = calculateRequiredHeight(resourceNode); - String name = resourcePath.getLeafResourceName(); - if (resourcePath.endsWithParam()) { - name = "{" + resourcePath.getLastParam().toString() + "}"; + StatefulObjectNode objectNode = (StatefulObjectNode) rootNode; + ResourceNode resourceNode = objectNode.getResource(); + if (!originalResourceNodeCells.containsKey(resourceNode)) { + continue; } - String style = "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"; + ObjectNodeAttribute attribute = new ObjectNodeAttribute(objectNode); - mxCell insertedCell = (mxCell) graph.insertVertex(layerCell, null, name, 20, 20, width, height, style); // insert a resource as a vertex - resourceNodeCells.put(resourceNode, insertedCell); + mxCell originalCell = originalResourceNodeCells.get(resourceNode); + double width = originalCell.getGeometry().getWidth(); + double height = originalCell.getGeometry().getHeight(); + double x = originalCell.getGeometry().getX(); + double y = originalCell.getGeometry().getY(); + String style = originalCell.getStyle(); - createChildResourceNodeCell(graph, insertedCell, resourceNode, resourceNodeCells, (int) (width * getScale(resourceNode.getChildren().size())), (int) (height * getScale(resourceNode.getChildren().size()))); + mxCell insertedCell = (mxCell) graph.insertVertex(layerCell, null, resourceNode.getPrimaryResourcePath().getName(), x, y, width, height, style); // insert a resource as a vertex + resourceNodeCells.put(objectNode, insertedCell); + + createChildResourceNodeCell(graph, insertedCell, objectNode, resourceNodeCells, originalResourceNodeCells); } return resourceNodeCells; } /** - * Calculate the scaling factor for child nodes based on their count. + * Creates and registers a child resource node cell for the specified parent node in the control flow graph. + * This method recursively processes the children of the parent node to generate corresponding graph cells. + * It utilizes the original resource node cells for dimensions, positioning, and styling information. * - * @param n The number of child nodes - * @return The scaling factor + * @param graph The {@link mxGraph} instance where the cells will be created and added. + * @param parentCell The parent {@link mxCell} to which the newly created child cells will be linked. + * @param parentNode The {@link StatefulObjectNode} representing the parent resource node whose children are processed. + * @param resourceNodeCells A map associating {@link StatefulObjectNode} instances to the created {@link mxCell} instances. + * @param originalResourceNodeCells A map of {@link ResourceNode} instances to their original {@link mxCell} instances, + * used to determine dimensions, coordinates, and styles for new cells. */ - private double getScale(int n) { - if (n <= 1) return 0.6; - return (0.8 * Math.sin(Math.PI / n)) / (1.0 + 0.8 * Math.sin(Math.PI / n)); - } - - /** - * Calculate the required width of the resource node to fit all its descendants. - * - * @param node The resource node - * @return The required width - */ - private int calculateRequiredWidth(ResourceNode node) { - if (node.getChildren().isEmpty()) return MIN_RESOURCE_NODE_WIDTH; - int maxChildWidth = 0; - for (ResourceNode child : node.getChildren()) { - maxChildWidth = Math.max(maxChildWidth, calculateRequiredWidth(child)); - } - return (int) Math.ceil(maxChildWidth / getScale(node.getChildren().size())); - } - - /** - * Calculate the required height of the resource node to fit all its descendants. - * - * @param node The resource node - * @return The required height - */ - private int calculateRequiredHeight(ResourceNode node) { - if (node.getChildren().isEmpty()) return MIN_RESOURCE_NODE_HEIGHT; - int maxChildHeight = 0; - for (ResourceNode child : node.getChildren()) { - maxChildHeight = Math.max(maxChildHeight, calculateRequiredHeight(child)); - } - return (int) Math.ceil(maxChildHeight / getScale(node.getChildren().size())); - } - - /** - * Create and register a new graph cell for a child resource node. - * - * @param graph {@link mxGraph} instance to be modified. - * @param parentCell Parent cell of the new cell. - * @param parentNode {@link ResourceNode} instance to be registered. - * @param resourceNodeCells The map of {@link ResourceNode} and its corresponding cell. - */ - private void createChildResourceNodeCell(final mxGraph graph, final mxCell parentCell, final ResourceNode parentNode, final Map resourceNodeCells, int width, int height) { - int i = 0; - int n = parentNode.getChildren().size(); - for (ResourceNode childNode : parentNode.getChildren()) { - ResourcePath resourcePath = childNode.getPrimaryResourcePath(); - - String name = resourcePath.getLeafResourceName(); - if (resourcePath.endsWithParam()) { - name = "{" + resourcePath.getLastParam().toString() + "}"; + private void createChildResourceNodeCell(final mxGraph graph, final mxCell parentCell, final StatefulObjectNode parentNode, final Map resourceNodeCells, final Map originalResourceNodeCells) { + for (ObjectNode childNode : parentNode.getChildren()) { + if (!(childNode instanceof StatefulObjectNode)) { + continue; } - String style = "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"; - - mxGeometry parentGeo = parentCell.getGeometry(); - double x = 0; - double y = 0; - if (parentGeo != null) { - x = (parentGeo.getWidth() - width) / 2; - y = (parentGeo.getHeight() - height) / 2; - if (n > 1) { - double rX = (parentGeo.getWidth() - width) / 2 * 0.8; - double rY = (parentGeo.getHeight() - height) / 2 * 0.8; - double theta = 2 * Math.PI / n * i; - x += rX * Math.cos(theta); - y += rY * Math.sin(theta); - } + StatefulObjectNode childObjectNode = (StatefulObjectNode) childNode; + ResourceNode childResourceNode = childObjectNode.getResource(); + if (!originalResourceNodeCells.containsKey(childResourceNode)) { + continue; } + ObjectNodeAttribute attribute = new ObjectNodeAttribute(childObjectNode); - mxCell insertedCell = (mxCell) graph.insertVertex(parentCell, null, name, x, y, width, height, style); // insert a resource as a vertex - resourceNodeCells.put(childNode, insertedCell); + mxCell originalCell = originalResourceNodeCells.get(childResourceNode); + double width = originalCell.getGeometry().getWidth(); + double height = originalCell.getGeometry().getHeight(); + double x = originalCell.getGeometry().getX(); + double y = originalCell.getGeometry().getY(); + String style = originalCell.getStyle(); - createChildResourceNodeCell(graph, insertedCell, childNode, resourceNodeCells, (int) (width * getScale(childNode.getChildren().size())), (int) (height * getScale(childNode.getChildren().size()))); - i++; + mxCell insertedCell = (mxCell) graph.insertVertex(parentCell, null, childResourceNode.getPrimaryResourcePath().getName(), x, y, width, height, style); // insert a resource as a vertex + resourceNodeCells.put(childObjectNode, insertedCell); + + createChildResourceNodeCell(graph, insertedCell, childObjectNode, resourceNodeCells, originalResourceNodeCells); } } @@ -285,7 +249,7 @@ * @param layer The layer number. Only {@link Stage#PUSH_FLOW_LAYER} is supported. * @return The map of {@link EventChannelObjectNode} and its corresponding cell. */ - private Map createEventChannelNodeCells(final mxGraph graph, final int layer) { + private Map createEventChannelNodeCells(final mxGraph graph, final Map channelCells, final int layer) { if (layer != PUSH_FLOW_LAYER && layer != PULL_FLOW_LAYER) { throw new IllegalArgumentException("The layer number must be either PUSH_FLOW_LAYER or PULL_FLOW_LAYER."); } @@ -305,23 +269,24 @@ continue; } final EventChannelObjectNode rootEventChannelNode = (EventChannelObjectNode) rootNode; - createEventChannelNodeCell(graph, layerCell, rootEventChannelNode, eventChannelNodeCells, outputEventChannelNodeCells, MIN_EVENT_CHANNEL_NODE_WIDTH, MIN_EVENT_CHANNEL_NODE_HEIGHT); + + createEventChannelNodeCell(graph, layerCell, rootEventChannelNode, eventChannelNodeCells, outputEventChannelNodeCells, channelCells); } return eventChannelNodeCells; } /** - * Create and register a new cell for a specific channel node and its children + * Creates and initializes a new event channel node cell in the graph and associates it with the given event channel object node. + * This method also adds an output port to the created channel cell and updates the corresponding mappings. * - * @param graph {@link mxGraph} instance to be modified. - * @param parentCell Parent cell of the new cell. - * @param eventChannelNode {@link EventChannelObjectNode} instance to be registered. - * @param eventChannelNodeCells {@link EventChannelObjectNode} and its corresponding cell. - * @param outputEventChannelNodeCells {@link EventChannelObjectNode} and its corresponding output port cell. - * @param cellWidth The width of a new cell. - * @param cellHeight The height of a new cell. + * @param graph The mxGraph instance where the event channel node cell is to be created. + * @param parentCell The parent mxCell that serves as the container for the newly created channel cell. + * @param eventChannelNode The EventChannelObjectNode representing the data and logic for the event channel node. + * @param eventChannelNodeCells A mapping of EventChannelObjectNode instances to their corresponding mxCell representations in the graph. + * @param outputEventChannelNodeCells A mapping of EventChannelObjectNode instances to their corresponding output port mxCells. + * @param originalChannelCells A mapping of DataTransferChannel instances to their original mxCell representations in the graph. */ - private void createEventChannelNodeCell(final mxGraph graph, final mxCell parentCell, final EventChannelObjectNode eventChannelNode, Map eventChannelNodeCells, Map outputEventChannelNodeCells, double cellWidth, double cellHeight) { + private void createEventChannelNodeCell(final mxGraph graph, final mxCell parentCell, final EventChannelObjectNode eventChannelNode, Map eventChannelNodeCells, Map outputEventChannelNodeCells, Map originalChannelCells) { DataTransferChannel channel = eventChannelNode.getIOChannel(); // Corresponding cell is already created @@ -329,11 +294,27 @@ return; } + if (!originalChannelCells.containsKey(channel)) { + System.out.println("Channel " + channel.getChannelName() + " is not found in the original graph."); + return; + } + + // Get original cell information from DataFlowGraph + mxCell originalCell = originalChannelCells.get(channel); + double cellWidth = originalCell.getGeometry().getWidth(); + double cellHeight = originalCell.getGeometry().getHeight(); + double x = originalCell.getGeometry().getX(); + double y = originalCell.getGeometry().getY(); + String style = originalCell.getStyle(); + // Get channel information String channelName = channel.getChannelName(); + if (originalCell.getValue() instanceof String) { + channelName = (String) originalCell.getValue(); + } // Create and register a new cell for a channel node - mxCell insertedChannelCell = (mxCell) graph.insertVertex(parentCell, null, channelName, cellWidth / 2, cellHeight / 4, cellWidth, cellHeight, "verticalAlign=top"); // insert a channel as a vertex + mxCell insertedChannelCell = (mxCell) graph.insertVertex(parentCell, null, channelName, x, y, cellWidth, cellHeight, style); // insert a channel as a vertex eventChannelNodeCells.put(eventChannelNode, insertedChannelCell); // Attach an output port to the channel @@ -347,13 +328,17 @@ } /** - * コントロールフローグラフの各頂点セルの間に, エッジのセルを追加する. + * Inserts control flow edges into the specified layer of the given graph. This method + * processes edges in the call graph and creates corresponding edges between nodes + * in the mxGraph representation. The source and destination nodes of the call edges + * must be valid instances of StatefulObjectNode or EventChannelObjectNode. * - * @param layer エッジを追加したいmxGraphのレイヤー - * @param resourceNodeCells リソースと, その頂点セルのインスタンスのマップ - * @param eventChannelNodeCells イベントチャンネルと, その頂点セルのインスタンスのマップ + * @param layer The index of the layer within the graph where the edges will be inserted. + * @param resourceNodeCells A map associating StatefulObjectNode instances with mxCell objects. + * @param eventChannelNodeCells A map associating EventChannelObjectNode instances with mxCell objects. + * @return The updated mxGraph with the inserted control flow edges. */ - private mxGraph insertControlFlowEdges(final int layer, final Map resourceNodeCells, final Map eventChannelNodeCells) { + private mxGraph insertControlFlowEdges(final int layer, final Map resourceNodeCells, final Map eventChannelNodeCells) { mxCell root = (mxCell) graph.getDefaultParent(); mxCell layerCell = (mxCell) root.getChildAt(layer); @@ -381,7 +366,7 @@ mxCell srcOutputPortCell = null; if (callEdge.getSource() instanceof StatefulObjectNode) { sourceNode = (StatefulObjectNode) callEdge.getSource(); - sourceNodeCell = resourceNodeCells.get(((StatefulObjectNode) sourceNode).getResource()); + sourceNodeCell = resourceNodeCells.get(sourceNode); } else if (callEdge.getSource() instanceof EventChannelObjectNode) { sourceNode = (EventChannelObjectNode) (callEdge.getSource()); sourceNodeCell = eventChannelNodeCells.get(sourceNode); @@ -390,9 +375,8 @@ } srcOutputPortCell = (mxCell) sourceNodeCell.getChildAt(0); } - StatefulObjectNode destinationNode = ((StatefulObjectNode) callEdge.getDestination()); // Destination node might be the StatefulObjectNode - mxCell destinationNodeCell = resourceNodeCells.get(destinationNode.getResource()); // TODO: Change the type of resourceNodeCells map + mxCell destinationNodeCell = resourceNodeCells.get(destinationNode); if (sourceNode == null || sourceNodeCell == null || destinationNodeCell == null) { continue;