diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java index 3a8603e..205b5a7 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java @@ -2,6 +2,7 @@ import application.editor.FlowCellEditor; import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; import com.mxgraph.swing.mxGraphComponent; import javax.swing.*; @@ -24,24 +25,232 @@ mxCell cell = (mxCell) cellObj; - switch (ddmStage.getCurState()){ + if (!(eventObj instanceof MouseEvent)) return; - case IDLE: //未選択状態 - if (cell.isEdge()) { - ddmStage.setState(DependencyModelingStageStatus.SELECT_DEPENDENCY_GRAPH); - } else { - ddmStage.setState(DependencyModelingStageStatus.SELECT_INTERFACE_NODE); - editInterfaceName(cell, eventObj); - ddmStage.setState(DependencyModelingStageStatus.IDLE); - } - break; - case SELECT_DEPENDENCY_GRAPH: //依存関係グラフを選択中 - break; - case SELECT_INTERFACE_NODE: //interface選択中 - break; + MouseEvent mouseEvent = (MouseEvent) eventObj; + + //Right-Click + if (SwingUtilities.isRightMouseButton(mouseEvent)) { + if (cell.isEdge()) { + showEdgeContextMenu(cell, mouseEvent); + } else if (isInterfaceNode(cell)) { + showInterfaceContextMenu(cell, mouseEvent); + } + return; } + // Left-Click + if (ddmStage.getCurState() == DependencyModelingStageStatus.IDLE && + isInterfaceNode(cell) && + mouseEvent.getClickCount() == 2) { + editInterfaceName(cell, eventObj); + } + } + + /** + * セルがインターフェースノード(ひし形)かどうかを判定 + */ + private boolean isInterfaceNode(mxCell cell) { + if (cell == null || !cell.isVertex()) return false; + String style = cell.getStyle(); + return style != null && style.contains("shape=rhombus"); + } + + /** + * エッジがインターフェースノードに接続されているかどうかを判定 + */ + private boolean isConnectedToInterface(mxCell edgeCell) { + if (edgeCell == null || !edgeCell.isEdge()) return false; + + mxCell source = (mxCell) edgeCell.getSource(); + mxCell target = (mxCell) edgeCell.getTarget(); + + return isInterfaceNode(source) || isInterfaceNode(target); + } + + /** + * エッジ右クリック時のコンテキストメニューを表示 + */ + private void showEdgeContextMenu(mxCell edgeCell, MouseEvent mouseEvent) { + // インターフェースノードに接続されているエッジの場合は何もしない + if (isConnectedToInterface(edgeCell)) { + return; + } + + JPopupMenu popup = new JPopupMenu(); + JMenuItem reverseItem = new JMenuItem("依存関係の逆転を適用"); + + reverseItem.addActionListener(e -> { + applyDependencyInversion(edgeCell); + }); + + popup.add(reverseItem); + popup.show(graphComponent, mouseEvent.getX(), mouseEvent.getY()); + } + + /** + * インターフェースノード右クリック時のコンテキストメニューを表示 + */ + private void showInterfaceContextMenu(mxCell interfaceCell, MouseEvent mouseEvent) { + JPopupMenu popup = new JPopupMenu(); + JMenuItem deleteItem = new JMenuItem("Interfaceを削除"); + + deleteItem.addActionListener(e -> { + removeInterface(interfaceCell); + }); + + popup.add(deleteItem); + popup.show(graphComponent, mouseEvent.getX(), mouseEvent.getY()); + } + + /** + * インターフェースノードを削除し、元のエッジに戻す + */ + private void removeInterface(mxCell interfaceCell) { + DependencyModelingStage ddmStage = (DependencyModelingStage) stage; + + // DEPENDENCY_LAYERを取得 + mxCell root = (mxCell) graphComponent.getGraph().getDefaultParent(); + mxCell layerCell = (mxCell) root.getChildAt(ddmStage.DEPENDENCY_LAYER); + + // インターフェースノードに接続されているエッジを取得 + int edgeCount = interfaceCell.getEdgeCount(); + if (edgeCount != 2) { + // 正確に2つのエッジがない場合は処理しない + return; + } + + mxCell edge1 = (mxCell) interfaceCell.getEdgeAt(0); + mxCell edge2 = (mxCell) interfaceCell.getEdgeAt(1); + + // source -> interface と target -> interface のエッジを識別 + mxCell sourceCell = null; + mxCell targetCell = null; + String edgeStyle = null; + Object edgeValue = null; + + // edge1とedge2から元のsourceとtargetを特定 + if (edge1.getTarget() == interfaceCell) { + // edge1: source -> interface + sourceCell = (mxCell) edge1.getSource(); + edgeStyle = edge1.getStyle(); + edgeValue = edge1.getValue(); + } else if (edge1.getSource() == interfaceCell) { + // edge1: interface -> ? + targetCell = (mxCell) edge1.getTarget(); + } + + if (edge2.getTarget() == interfaceCell) { + if (sourceCell == null) { + // edge2: source -> interface + sourceCell = (mxCell) edge2.getSource(); + edgeStyle = edge2.getStyle(); + edgeValue = edge2.getValue(); + } else { + // edge2: target -> interface (逆向き) + targetCell = (mxCell) edge2.getSource(); + } + } else if (edge2.getSource() == interfaceCell) { + // edge2: interface -> ? + if (targetCell == null) { + targetCell = (mxCell) edge2.getTarget(); + } + } + + if (sourceCell == null || targetCell == null) { + return; + } + + graphComponent.getGraph().getModel().beginUpdate(); + try { + // インターフェースノードとそのエッジを削除 + graphComponent.getGraph().getModel().remove(edge1); + graphComponent.getGraph().getModel().remove(edge2); + graphComponent.getGraph().getModel().remove(interfaceCell); + + // 元のエッジを復元 (source -> target) + // dashedスタイルを削除 + String restoredStyle = edgeStyle; + if (restoredStyle != null && restoredStyle.contains("dashed=true")) { + restoredStyle = restoredStyle.replace(";dashed=true", "").replace("dashed=true;", "").replace("dashed=true", ""); + } + + graphComponent.getGraph().insertEdge( + layerCell, + null, + edgeValue, + sourceCell, + targetCell, + restoredStyle + ); + + } finally { + graphComponent.getGraph().getModel().endUpdate(); + graphComponent.refresh(); + } + } + + /** + * Apply dependency inversion + */ + private void applyDependencyInversion(mxCell edgeCell) { + DependencyModelingStage ddmStage = (DependencyModelingStage) stage; + + mxCell source = (mxCell) edgeCell.getSource(); + mxCell target = (mxCell) edgeCell.getTarget(); + + if (source == null || target == null) return; + + mxCell root = (mxCell) graphComponent.getGraph().getDefaultParent(); + mxCell layerCell = (mxCell) root.getChildAt(ddmStage.DEPENDENCY_LAYER); + + // エッジの中点を計算 + mxGeometry sourceGeo = source.getGeometry(); + mxGeometry targetGeo = target.getGeometry(); + + double midX = (sourceGeo.getCenterX() + targetGeo.getCenterX()) / 2; + double midY = (sourceGeo.getCenterY() + targetGeo.getCenterY()) / 2; + + graphComponent.getGraph().getModel().beginUpdate(); + try { + // 元のエッジを削除 + graphComponent.getGraph().getModel().remove(edgeCell); + + // インターフェースノード(ひし形)をDEPENDENCY_LAYERに挿入 + mxCell interfaceNode = (mxCell) graphComponent.getGraph().insertVertex( + layerCell, + null, + "Interface", + midX - ddmStage.DEPENDENCY_NODE_SIZE / 2, + midY - ddmStage.DEPENDENCY_NODE_SIZE / 2, + ddmStage.DEPENDENCY_NODE_SIZE, + ddmStage.DEPENDENCY_NODE_SIZE, + "shape=rhombus;fillColor=#BBFFBB;perimeter=rhombusPerimeter;resizable=0;" + ); + + // 元のエッジのスタイルを取得 + String edgeStyle = edgeCell.getStyle(); + Object edgeValue = edgeCell.getValue(); + + // source -> interface のエッジを作成 (元のスタイルを維持) + graphComponent.getGraph().insertEdge(layerCell, null, edgeValue, source, interfaceNode, edgeStyle); + + // target -> interface のエッジを作成 (逆向き・点線) + // 元のスタイルに dashed=true を追加 + String reversedStyle = edgeStyle; + if (reversedStyle != null && !reversedStyle.contains("dashed")) { + reversedStyle = reversedStyle + ";dashed=true"; + } else if (reversedStyle == null) { + reversedStyle = "dashed=true"; + } + + graphComponent.getGraph().insertEdge(layerCell, null, edgeValue, target, interfaceNode, reversedStyle); + + } finally { + graphComponent.getGraph().getModel().endUpdate(); + graphComponent.refresh(); + } } public void editInterfaceName(mxCell cell, EventObject eventObj){ @@ -66,6 +275,7 @@ graphComponent.refresh(); } } + @Override public void stopEditing(boolean cancel) { diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyModelingStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyModelingStage.java index 7c597ef..e6ce3d0 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyModelingStage.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyModelingStage.java @@ -14,7 +14,7 @@ import generators.JavaCodeGenerator; import models.Edge; import models.algebra.*; -import models.controlFlowModel.ControlFlowGraph; +import models.controlFlowModel.*; import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.ResourcePath; @@ -27,7 +27,7 @@ import java.util.*; public class DependencyModelingStage extends Stage { - public final int DEPENDENCY_NODE_SIZE = 40; + public final int DEPENDENCY_NODE_SIZE = 50; private DependencyModelingStageStatus curState = null; @@ -103,59 +103,7 @@ @Override public MouseListener createMouseEventListener(Editor editor) { - return new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getButton() == MouseEvent.BUTTON1) { - - Object cell = graphComponent.getCellAt(e.getX(), e.getY()); - if (cell == null) { - insertDependencyNodeAt(e.getX(), e.getY()); - } - } - } - }; - } - - //interfaceを追加 - private void insertDependencyNodeAt(int mouseX, int mouseY) { - mxGraphModel model = (mxGraphModel) graph.getModel(); - mxCell parent = (mxCell) graph.getDefaultParent(); - mxGraph graph = graphComponent.getGraph(); - - double scale = graph.getView().getScale(); - double translateX = graph.getView().getTranslate().getX(); - double translateY = graph.getView().getTranslate().getY(); - - double graphX = (mouseX / scale) - translateX; - double graphY = (mouseY / scale) - translateY; - - // ノードを中心合わせで配置 - mxGeometry geometry = new mxGeometry( - graphX - (double) DEPENDENCY_NODE_SIZE / 2, - graphY - (double) DEPENDENCY_NODE_SIZE / 2, - DEPENDENCY_NODE_SIZE, - DEPENDENCY_NODE_SIZE - ); - - model.beginUpdate(); - try { - mxCell newNodeCell = (mxCell) graph.insertVertex( - parent, - null, - "Interface", - geometry.getX(), geometry.getY(), - DEPENDENCY_NODE_SIZE, DEPENDENCY_NODE_SIZE, - "shape=rhombus;fillColor=#BBFFBB;perimeter=ellipsePerimeter;resizable=0;" - ); - - graph.setSelectionCell(newNodeCell); - - nodes.add(newNodeCell); - - } finally { - model.endUpdate(); - } + return null; } @Override @@ -163,22 +111,6 @@ return true; } - private mxGraph constructGraph(mxGraph graph, DependencyGraph dependencyGraph) { - showLayers(DEPENDENCY_LAYER); - - graph.getModel().beginUpdate(); - try { - - Map dependencyResNodeCells = createCellsOfResourceMap(); - Map dependencyEventNodeCells = createEventChannelCells(); - - graph = insertDependencyEdges(dependencyResNodeCells, dependencyEventNodeCells); - } finally { - graph.getModel().endUpdate(); - } - return graph; - } - private void clearDependencyGraphCells(mxGraph graph) { mxCell root = (mxCell) graph.getDefaultParent(); mxCell dependencyLayer = (mxCell) root.getChildAt(DEPENDENCY_LAYER); @@ -196,6 +128,22 @@ } } + private mxGraph constructGraph(mxGraph graph, DependencyGraph dependencyGraph) { + showLayers(DEPENDENCY_LAYER); + + graph.getModel().beginUpdate(); + try { + + Map dependencyResNodeCells = createCellsOfResourceMap(); + Map dependencyEventNodeCells = createEventChannelCells(); + + graph = insertDependencyEdges(dependencyResNodeCells, dependencyEventNodeCells); + } finally { + graph.getModel().endUpdate(); + } + return graph; + } + private Map createCellsOfResourceMap() { Map resNodeCells = new HashMap<>(); @@ -345,43 +293,37 @@ return style.contains("ellipse") || style.contains("perimeter=ellipsePerimeter"); } - private mxGraph insertDependencyEdges(final Map resNodeCells, final Map eventChNodeCells) { mxCell root = (mxCell) graph.getDefaultParent(); - mxCell dependencyLayerCell = (mxCell) root.getChildAt(DEPENDENCY_LAYER); + mxCell layerCell = (mxCell) root.getChildAt(DEPENDENCY_LAYER); - graph.getModel().beginUpdate(); - try { - for (Edge edge : dependencyGraph.getDataFlowGraph().getEdges()) { - DataFlowEdge dataFlowEdge = (DataFlowEdge) edge; - if (dataFlowEdge.isChannelToResource()) { - // output edge - DataTransferChannel channel = ((ChannelNode) dataFlowEdge.getSource()).getChannel(); - ResourcePath dstRes = ((ResourceNode) dataFlowEdge.getDestination()).getInSideResource(channel); - graph.insertEdge(dependencyLayerCell, null, new Editor.SrcDstAttribute(channel, dstRes), eventChNodeCells.get(channel), resNodeCells.get((ResourceNode) dataFlowEdge.getDestination()), "strokeWidth=3;strokeColor=green;movable=false"); - } else { - // input edge - DataTransferChannel channel = ((ChannelNode) dataFlowEdge.getDestination()).getChannel(); - ResourcePath srcRes = ((ResourceNode) dataFlowEdge.getSource()).getOutSideResource(channel); - Set> toRes = getResourceDependencyForChannel(channel, dependencyGraph.getDataFlowGraph()); - for (Map.Entry RtoR : toRes) { - graph.insertEdge(dependencyLayerCell, null, null, resNodeCells.get(RtoR.getValue()), resNodeCells.get(RtoR.getKey()), "strokeWidth=3;strokeColor=green;dashed=true;movable=false"); - } - - graph.insertEdge(dependencyLayerCell, null, new Editor.SrcDstAttribute(srcRes, channel), resNodeCells.get((ResourceNode) dataFlowEdge.getSource()), eventChNodeCells.get(channel), "strokeWidth=3;strokeColor=green;movable=false"); + for (Edge edge : dependencyGraph.getDataFlowGraph().getEdges()) { + DataFlowEdge dataFlowEdge = (DataFlowEdge) edge; + if (dataFlowEdge.isChannelToResource()) { + // output edge + DataTransferChannel channel = ((ChannelNode) dataFlowEdge.getSource()).getChannel(); + ResourcePath dstRes = ((ResourceNode) dataFlowEdge.getDestination()).getInSideResource(channel); + graph.insertEdge(layerCell, null, new Editor.SrcDstAttribute(channel, dstRes), eventChNodeCells.get(channel), resNodeCells.get((ResourceNode) dataFlowEdge.getDestination()), "strokeWidth=3;strokeColor=green;movable=false"); + } else { + // input edge + DataTransferChannel channel = ((ChannelNode) dataFlowEdge.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) dataFlowEdge.getSource()).getOutSideResource(channel); + Set> toRes = getResourceDependencyForChannel(channel, dependencyGraph.getDataFlowGraph()); + for (Map.Entry RtoR : toRes) { + graph.insertEdge(layerCell, null, null, resNodeCells.get(RtoR.getValue()), resNodeCells.get(RtoR.getKey()), "strokeWidth=3;strokeColor=green;dashed=true;movable=false"); } + graph.insertEdge(layerCell, null, new Editor.SrcDstAttribute(srcRes, channel), resNodeCells.get((ResourceNode) dataFlowEdge.getSource()), eventChNodeCells.get(channel), "strokeWidth=3;strokeColor=green;movable=false"); } - - for (Channel ch : model.getChannels()) { - // reference edges - DataTransferChannel channel = (DataTransferChannel) ch; - for (ResourcePath refRes : channel.getReferenceResources()) { - graph.insertEdge(dependencyLayerCell, null, null, resNodeCells.get(dependencyGraph.getDataFlowGraph().getResourceNode(refRes)), eventChNodeCells.get(channel), "strokeWidth=3;strokeColor=green;dashed=true;movable=false"); - } - } - } finally { - graph.getModel().endUpdate(); } + + for (Channel ch : model.getChannels()) { + // reference edges + DataTransferChannel channel = (DataTransferChannel) ch; + for (ResourcePath refRes : channel.getReferenceResources()) { + graph.insertEdge(layerCell, null, null, resNodeCells.get(dependencyGraph.getDataFlowGraph().getResourceNode(refRes)), eventChNodeCells.get(channel), "strokeWidth=3;strokeColor=green;dashed=true;movable=false"); + } + } + return graph; }