diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java index e8b3ee0..6547563 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java @@ -16,9 +16,11 @@ import models.controlFlowModel.*; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.PushPullValue; import models.dataFlowModel.ResourceNode; -import java.awt.event.MouseListener; +import javax.swing.*; +import java.awt.event.*; import java.util.*; public class ControlFlowModelingStage extends Stage { @@ -27,6 +29,9 @@ private ControlFlowGraph controlFlowGraph = null; + private Map resNodeToCell = null; + private Map channelToCell = null; + public ControlFlowModelingStage(mxGraphComponent graphComponent) { super(graphComponent); } @@ -46,12 +51,14 @@ if (prevStage instanceof PushPullSelectionStage) { PushPullSelectionStage stage = (PushPullSelectionStage) prevStage; model = stage.getModel(); + resNodeToCell = stage.getResNodeToCell(); + channelToCell = stage.getChannelToCell(); DataFlowGraph dataFlowGraph = stage.getDataFlowGraph(); controlFlowGraph = new ControlFlowGraph(dataFlowGraph, model); clearControlFlowGraphCells(graph); - graph = constructGraph(graph, stage.getResNodeToCell(), stage.getChannelToCell()); + graph = constructGraph(graph, resNodeToCell, channelToCell); } } @@ -92,8 +99,171 @@ } @Override - public MouseListener createMouseEventListener(Editor editor) { - return null; + public MouseListener createMouseEventListener(final Editor editor) { + return new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopupMenu(e); + } + } + + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopupMenu(e); + } + } + + private void showPopupMenu(MouseEvent e) { + Object[] selectedCells = graph.getSelectionCells(); + if (selectedCells == null || selectedCells.length < 2) { + return; + } + + List selectedEdges = new ArrayList<>(); + for (Object cellObj : selectedCells) { + if (graph.getModel().isEdge(cellObj)) { + selectedEdges.add((mxCell) cellObj); + } else { + return; + } + } + + if (isValidPath(selectedEdges)) { + JPopupMenu popup = new JPopupMenu(); + JMenuItem menuItem = new JMenuItem("ショートカットを作成する"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + createShortcut(selectedEdges); + } + }); + popup.add(menuItem); + popup.show(e.getComponent(), e.getX(), e.getY()); + } + } + + private void createShortcut(List selectedEdges) { + List callEdges = new ArrayList<>(); + for (mxCell edgeCell : selectedEdges) { + CallEdgeAttribute attr = (CallEdgeAttribute) edgeCell.getValue(); + callEdges.add(attr.getCallEdge()); + } + + Map inDegrees = new HashMap<>(); + for (CallEdge edge : callEdges) { + Node dest = edge.getDestination(); + inDegrees.put(dest, inDegrees.getOrDefault(dest, 0) + 1); + } + + Node startNode = null; + Node endNode = null; + for (CallEdge edge : callEdges) { + if (!inDegrees.containsKey(edge.getSource())) { + startNode = edge.getSource(); + break; + } + } + + Set sources = new HashSet<>(); + for (CallEdge edge : callEdges) { + sources.add(edge.getSource()); + } + for (CallEdge edge : callEdges) { + if (!sources.contains(edge.getDestination())) { + endNode = edge.getDestination(); + break; + } + } + + if (startNode == null || endNode == null) return; + + PushPullValue option = callEdges.get(0).getSelectedOption(); + CallGraph callGraph = getCallGraph(option); + if (callGraph == null) return; + + for (CallEdge edge : callEdges) { + callGraph.removeEdge(edge); + } + + if (startNode instanceof ObjectNode && endNode instanceof ObjectNode) { + callGraph.insertEdge((ObjectNode) startNode, (ObjectNode) endNode, option, 0); + } + + reconstructGraph(); + } + + private boolean isValidPath(List selectedEdges) { + Map inDegrees = new HashMap<>(); + Map outDegrees = new HashMap<>(); + Set nodes = new HashSet<>(); + + for (mxCell edge : selectedEdges) { + mxCell src = (mxCell) edge.getSource(); + mxCell dest = (mxCell) edge.getTarget(); + if (src == null || dest == null) return false; + + outDegrees.put(src, outDegrees.getOrDefault(src, 0) + 1); + inDegrees.put(dest, inDegrees.getOrDefault(dest, 0) + 1); + nodes.add(src); + nodes.add(dest); + } + + mxCell startNode = null; + int startCount = 0; + int endCount = 0; + + for (mxCell node : nodes) { + int in = inDegrees.getOrDefault(node, 0); + int out = outDegrees.getOrDefault(node, 0); + + if (in > 1 || out > 1) { + return false; + } + + if (in == 0 && out == 1) { + startNode = node; + startCount++; + } else if (in == 1 && out == 0) { + endCount++; + } else if (in == 1 && out == 1) { + // Intermediate node + } else { + return false; + } + } + + if (startCount != 1 || endCount != 1) { + return false; + } + + int visitedEdgesCount = 0; + mxCell currentNode = startNode; + while (currentNode != null) { + mxCell nextEdge = null; + for (mxCell edge : selectedEdges) { + if (edge.getSource() == currentNode) { + nextEdge = edge; + break; + } + } + if (nextEdge != null) { + visitedEdgesCount++; + currentNode = (mxCell) nextEdge.getTarget(); + } else { + currentNode = null; + } + } + + return visitedEdgesCount == selectedEdges.size(); + } + }; + } + + public void reconstructGraph() { + clearControlFlowGraphCells(graph); + graph = constructGraph(graph, resNodeToCell, channelToCell); } /** @@ -392,6 +562,15 @@ return graph; } + private CallGraph getCallGraph(PushPullValue option) { + if (option == PushPullValue.PUSH) { + return controlFlowGraph.getPushCallGraph(); + } else if (option == PushPullValue.PULL) { + return controlFlowGraph.getPullCallGraph(); + } + return null; + } + /** * Retrieves the call graph associated with the specified control flow layer. *