diff --git a/AlgebraicDataflowArchitectureModel/models/GroupChat2.model b/AlgebraicDataflowArchitectureModel/models/GroupChat2.model new file mode 100644 index 0000000..a979f48 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/GroupChat2.model @@ -0,0 +1,32 @@ +channel Signup { + out accounts(acDB:Map, signUp(aid:Str)) = insert(acDB, aid, {"notifications": nil}) +} + +channel HasRead(aid:Str) { + out accounts.{aid}.notifications(ntMap:Map, hasRead(gid:Str)) = delete(ntMap, gid) +} + +channel CreateGroup { + out groups(grDB:Map, createGroup(gid:Str)) = insert(grDB, gid, {"members": nil, "messages": nil}) +} + +channel AddGroupMember(gid:Str) { + out groups.{gid}.members(memList:List, addGroupMember(aid:Str)) = append(memList, aid) +} + +channel PostMessage(gid:Str) { + out groups.{gid}.messages(mesList:List, postMessage(message:Str)) = append(mesList, message) +} + +channel Notify(gid:Str) { + in groups.{gid}.messages(prevMesList, notify(m)) = mesList + for EachMember(mno:Int) { + ref groups.{gid}.members.{mno}(m.{mno}:Str, notify(m)) + out accounts.{m.{mno}:Str}.notifications(prevNtMap:Map, notify(m)) = insert(prevNtMap, gid, true) + } +} + +channel Event { + in groups(grDB:Map, createGroup(gid:Str)) = new + out accounts.{aid}.notifications(ntMap:Map, hasRead(gid:Str)) = new +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java index 3932005..75b7c77 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyCellEditor.java @@ -6,7 +6,10 @@ import com.mxgraph.view.mxCellState; import com.mxgraph.view.mxGraph; import models.Edge; +import models.Node; +import models.controlFlowModel.EventChannelObjectNode; import models.controlFlowModel.ObjectNode; +import models.controlFlowModel.StatefulObjectNode; import models.dependencyModel.DependencyEdge; import models.dependencyModel.DependencyGraph; import models.dependencyModel.InterfaceNode; @@ -15,6 +18,9 @@ import java.awt.*; import java.awt.event.MouseEvent; import java.util.EventObject; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; public class DependencyCellEditor extends FlowCellEditor { @@ -110,206 +116,197 @@ /** * Deletes the interface node and restores the original edge */ + private String getTerminalStyle(String style, boolean isSourceSide) { + if (style == null) return ""; + StringBuilder result = new StringBuilder(); + String[] parts = style.split(";"); + String prefix = isSourceSide ? "exit" : "entry"; + for (String part : parts) { + if (part.startsWith(prefix) || part.startsWith("perimeter")) { + result.append(part).append(";"); + } + } + return result.toString(); + } + + private String convertExitToEntry(String style) { + return style.replace("exit", "entry"); + } + + private String convertEntryToExit(String style) { + return style.replace("entry", "exit"); + } + private void removeInterface(mxCell interfaceCell) { DependencyModelingStage ddmStage = (DependencyModelingStage) stage; - mxGraph graph = graphComponent.getGraph(); - mxCell layerCell = (mxCell) ((mxCell) graph.getDefaultParent()) - .getChildAt(ddmStage.DEPENDENCY_LAYER); + mxCell layerCell = (mxCell) ((mxCell) graph.getDefaultParent()).getChildAt(ddmStage.DEPENDENCY_LAYER); - // Get edges connected to the interface node - int edgeCount = interfaceCell.getEdgeCount(); - if (edgeCount != 2) { - // If not exactly two edges, do nothing - return; - } + InterfaceNode interfaceNode = ddmStage.getInterfaceNode(interfaceCell); + if (interfaceNode == null) return; - mxCell edge1 = (mxCell) interfaceCell.getEdgeAt(0); - mxCell edge2 = (mxCell) interfaceCell.getEdgeAt(1); + ObjectNode dstNode = interfaceNode.getImplementSideNode(); + mxCell dstCell = getCellFromNode(dstNode); - // Identify source -> interface and target -> interface edges - mxCell sourceCell = null; - mxCell targetCell = null; - String edgeStyle = null; - Object edgeValue = null; - - // Determine the original source and target from edge1 and edge2 - 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 (reverse direction) - targetCell = (mxCell) edge2.getSource(); + Object[] edgesToInterface = graph.getIncomingEdges(interfaceCell); + String implEdgeStyle = ""; + for (Object edge : edgesToInterface) { + mxCell e = (mxCell) edge; + if (e.getSource() == dstCell) { + implEdgeStyle = getTerminalStyle(e.getStyle(), true); + break; } - } else if (edge2.getSource() == interfaceCell) { - // edge2: interface -> ? - if (targetCell == null) { - targetCell = (mxCell) edge2.getTarget(); - } - } - - if (sourceCell == null || targetCell == null) { - return; } graph.getModel().beginUpdate(); try { - DependencyGraph depGraph = ddmStage.getDependencyGraph(); - InterfaceNode interfaceNode = ddmStage.getInterfaceNode(interfaceCell); - if (interfaceNode == null) { - return; - } + for (ObjectNode srcNode : interfaceNode.getDependencySideNodes()) { + depGraph.addEdge(srcNode, dstNode); + mxCell srcCell = getCellFromNode(srcNode); + if (srcCell != null && dstCell != null) { + Object[] srcEdges = graph.getEdgesBetween(srcCell, interfaceCell, true); + String srcExitStyle = (srcEdges.length > 0) ? getTerminalStyle(((mxCell) srcEdges[0]).getStyle(), true) : ""; - ObjectNode srcNode = interfaceNode.getDependencySideNode(); - ObjectNode dstNode = interfaceNode.getImplementSideNode(); + String restoredStyle = "strokeWidth=1;strokeColor=green;movable=false;" + + srcExitStyle + + convertExitToEntry(implEdgeStyle); - DependencyEdge depEdge = null; - DependencyEdge implEdge = null; - - for (Edge e : depGraph.getEdges()) { - DependencyEdge de = (DependencyEdge) e; - if (de.getDestination() == interfaceNode) { - if (de.getEdgeType() == DependencyEdge.EdgeType.DEPENDENCY) { - depEdge = de; - } else if (de.getEdgeType() == DependencyEdge.EdgeType.IMPLEMENT) { - implEdge = de; - } + graph.insertEdge(layerCell, null, null, srcCell, dstCell, restoredStyle); } } - if (depEdge != null && implEdge != null) { - depGraph.removeEdge(depEdge); - depGraph.removeEdge(implEdge); - ddmStage.getDependencyGraph().removeNode(interfaceNode); - ddmStage.removeInterfaceNode(interfaceCell); - - depGraph.addEdge(new DependencyEdge(srcNode, dstNode, DependencyEdge.EdgeType.DEPENDENCY)); + Set toRemove = new HashSet<>(); + for (Edge e : depGraph.getEdges()) { + DependencyEdge de = (DependencyEdge) e; + if (isSameNode(de.getSource(), interfaceNode) || isSameNode(de.getDestination(), interfaceNode)) { + toRemove.add(de); + } } + for (DependencyEdge de : toRemove) depGraph.removeEdge(de); - // Remove the interface node and its edges - graph.getModel().remove(edge1); - graph.getModel().remove(edge2); + depGraph.removeNode(interfaceNode); + ddmStage.removeInterfaceNode(interfaceCell); graph.getModel().remove(interfaceCell); - // Restore the original edge (source -> target) - // Remove the dashed style - String restoredStyle = edgeStyle; - if (restoredStyle != null && restoredStyle.contains("dashed=true")) { - restoredStyle = restoredStyle.replace(";dashed=true", "") - .replace("dashed=true;", "") - .replace("dashed=true", ""); - } - - graph.insertEdge( - layerCell, - null, - edgeValue, - sourceCell, - targetCell, - restoredStyle - ); - } finally { graph.getModel().endUpdate(); graphComponent.refresh(); } } - /** - * Apply dependency inversion - */ private void applyDependencyInversion(mxCell edgeCell) { DependencyModelingStage ddmStage = (DependencyModelingStage) stage; + mxGraph graph = graphComponent.getGraph(); + mxCell layerCell = (mxCell) ((mxCell) graph.getDefaultParent()).getChildAt(ddmStage.DEPENDENCY_LAYER); mxCell source = (mxCell) edgeCell.getSource(); mxCell destination = (mxCell) edgeCell.getTarget(); if (source == null || destination == null) return; - mxGraph graph = graphComponent.getGraph(); - mxCell layerCell = (mxCell) ((mxCell) graph.getDefaultParent()) - .getChildAt(ddmStage.DEPENDENCY_LAYER); + String originalStyle = edgeCell.getStyle(); + String exitStyle = getTerminalStyle(originalStyle, true); + String entryStyle = getTerminalStyle(originalStyle, false); - mxCellState s = graph.getView().getState(source); - mxCellState t = graph.getView().getState(destination); + // Identify if the source is an EventChannel or a StatefulObject + ObjectNode srcNode = ddmStage.getEventChannelObjectNode(source); + if (srcNode == null) srcNode = ddmStage.getStatefulObjectNode(source); - double midX, midY; - midX = (s.getCenterX() + t.getCenterX()) / 2; - midY = (s.getCenterY() + t.getCenterY()) / 2; + // Assume the target is a StatefulObjectNode + ObjectNode dstNode = ddmStage.getStatefulObjectNode(destination); + + if (srcNode == null || dstNode == null) return; + + DependencyGraph depGraph = ddmStage.getDependencyGraph(); + InterfaceNode iNode = null; + mxCell interfaceCell = null; + + // --- Search for existing interfaces --- + // If source is EventChannel, skip search to always create a dedicated interface (iNode remains null) + if (!(srcNode instanceof EventChannelObjectNode)) { + for (Map.Entry entry : ddmStage.getCellToInterfaceNodeMap().entrySet()) { + InterfaceNode existingINode = entry.getValue(); + + // 1. Check if it points to the same implementation side (Destination) + if (isSameNode(existingINode.getImplementSideNode(), dstNode)) { + + // 2. Ensure this interface was not created by an EventChannel + // (EventChannel-originated interfaces are excluded from sharing) + boolean isEventChannelInterface = false; + for (ObjectNode node : existingINode.getDependencySideNodes()) { + if (node instanceof EventChannelObjectNode) { + isEventChannelInterface = true; + break; + } + } + + if (!isEventChannelInterface) { + iNode = existingINode; + interfaceCell = entry.getKey(); + break; + } + } + } + } graph.getModel().beginUpdate(); try { - // Remove the original edge + Object edgeValue = edgeCell.getValue(); + // Remove the original direct edge graph.getModel().remove(edgeCell); - // Insert the interface node (Interface) into the DEPENDENCY_LAYER - mxCell interfaceCell = (mxCell) graph.insertVertex( - layerCell, null, "Interface", - midX - ddmStage.INTERFACE_NODE_SIZE / 2, - midY - ddmStage.INTERFACE_NODE_SIZE / 2, - ddmStage.INTERFACE_NODE_SIZE, - ddmStage.INTERFACE_NODE_SIZE, - "shape=interface;strokeColor=green;fillColor=#BBFFBB;perimeter=interfacePerimeter;resizable=0;" - ); + if (iNode == null) { + // Create a new interface vertex + mxCellState s = graph.getView().getState(source); + mxCellState t = graph.getView().getState(destination); + double midX = (s.getCenterX() + t.getCenterX()) / 2; + double midY = (s.getCenterY() + t.getCenterY()) / 2; - // Input Interface Name - String name = JOptionPane.showInputDialog(graphComponent, "Interface name:", "Interface"); - if (name != null && !name.trim().isEmpty()) { - interfaceCell.setValue(name.trim()); + interfaceCell = (mxCell) graph.insertVertex( + layerCell, null, "Interface", + midX - ddmStage.INTERFACE_NODE_SIZE / 2, + midY - ddmStage.INTERFACE_NODE_SIZE / 2, + ddmStage.INTERFACE_NODE_SIZE, + ddmStage.INTERFACE_NODE_SIZE, + "shape=interface;strokeColor=green;fillColor=#BBFFBB;perimeter=interfacePerimeter;resizable=0;" + ); + + String name = JOptionPane.showInputDialog(graphComponent, "Interface name:", "Interface"); + if (name != null && !name.trim().isEmpty()) interfaceCell.setValue(name.trim()); + + // Add the interface to the model + iNode = new InterfaceNode(srcNode, dstNode, interfaceCell.getValue().toString()); + ddmStage.setInterfaceNode(interfaceCell, iNode); + depGraph.addNode(iNode); + + // Implementation relationship (Destination -> Interface : Dashed line) + depGraph.addEdge(new DependencyEdge(dstNode, iNode, DependencyEdge.EdgeType.IMPLEMENT)); + + String baseDashed = (originalStyle == null) ? "dashed=true" : + (originalStyle.contains("dashed") ? originalStyle : originalStyle + ";dashed=true"); + String dashedStyle = baseDashed + ";" + convertEntryToExit(entryStyle); + graph.insertEdge(layerCell, null, null, destination, interfaceCell, dashedStyle); + } else { + // Register node to existing sharable interface + iNode.addDependencySideNode(srcNode); } - // Get the original edge's style and value - String edgeStyle = edgeCell.getStyle(); - Object edgeValue = edgeCell.getValue(); - - //create InterfaceNode and InterfaceEdge - ObjectNode srcNode = ddmStage.getEventChannelObjectNode(source); - if (srcNode == null) { - srcNode = ddmStage.getStatefulObjectNode(source); - } - ObjectNode dstNode = ddmStage.getStatefulObjectNode(destination); - InterfaceNode iNode = new InterfaceNode(srcNode, dstNode, interfaceCell.getValue().toString()); - ddmStage.setInterfaceNode(interfaceCell, iNode); - - DependencyGraph depGraph = ddmStage.getDependencyGraph(); - DependencyEdge targetEdge = null; - - for (Edge e : depGraph.getEdges()) { - if (e.getSource() == srcNode) { - targetEdge = (DependencyEdge) e; - break; - } - } - - if (targetEdge == null) return; - depGraph.removeEdge(targetEdge); + // Update dependency model (Remove direct dependency and add interface-mediated dependency) + DependencyEdge targetEdge = findDependencyEdge(depGraph, srcNode, dstNode); + if (targetEdge != null) depGraph.removeEdge(targetEdge); depGraph.addEdge(new DependencyEdge(srcNode, iNode, DependencyEdge.EdgeType.DEPENDENCY)); - depGraph.addEdge(new DependencyEdge(dstNode, iNode, DependencyEdge.EdgeType.IMPLEMENT)); - // Create edge from source -> interface (keep original style) - graph.insertEdge(layerCell, null, edgeValue, source, interfaceCell, edgeStyle); + // Dependency edge (Source -> Interface : Solid line) + // Avoid creating duplicate edges if connection already exists + if (graph.getEdgesBetween(source, interfaceCell, true).length == 0) { + graph.insertEdge(layerCell, null, edgeValue, source, interfaceCell, originalStyle + ";" + exitStyle); + } - // Create edge from destination -> interface (reverse direction, dashed) - // Add "dashed=true" to the style - String dashedStyle = (edgeStyle == null) - ? "dashed=true" - : (edgeStyle.contains("dashed") ? edgeStyle : edgeStyle + ";dashed=true"); - - graph.insertEdge(layerCell, null, edgeValue, destination, interfaceCell, dashedStyle); + // Only recursively scan child dependencies if the source is a StatefulObjectNode + if (srcNode instanceof StatefulObjectNode) { + linkChildrenDependencyRecursive((StatefulObjectNode) srcNode, dstNode, iNode, interfaceCell, depGraph, graph); + } } finally { graph.getModel().endUpdate(); @@ -317,6 +314,74 @@ } } + private void linkChildrenDependencyRecursive(StatefulObjectNode parentNode, ObjectNode dstNode, InterfaceNode iNode, + mxCell interfaceCell, DependencyGraph depGraph, mxGraph graph) { + DependencyModelingStage ddmStage = (DependencyModelingStage) stage; + mxCell layerCell = (mxCell) ((mxCell) graph.getDefaultParent()).getChildAt(ddmStage.DEPENDENCY_LAYER); + + for (ObjectNode childNode : parentNode.getChildren()) { + DependencyEdge matchedEdge = findDependencyEdge(depGraph, childNode, dstNode); + + if (matchedEdge != null) { + depGraph.removeEdge(matchedEdge); + depGraph.addEdge(new DependencyEdge(childNode, iNode, DependencyEdge.EdgeType.DEPENDENCY)); + iNode.addDependencySideNode(childNode); + + mxCell childCell = getCellFromNode(childNode); + mxCell dstCell = getCellFromNode(dstNode); + + if (childCell != null) { + if (dstCell != null) { + Object[] edges = graph.getEdgesBetween(childCell, dstCell, true); + for (Object edge : edges) graph.getModel().remove(edge); + } + if (graph.getEdgesBetween(childCell, interfaceCell, true).length == 0) { + graph.insertEdge(layerCell, null, null, childCell, interfaceCell, "strokeColor=green;"); + } + } + } + if (childNode instanceof StatefulObjectNode) { + linkChildrenDependencyRecursive((StatefulObjectNode) childNode, dstNode, iNode, interfaceCell, depGraph, graph); + } + } + } + + private DependencyEdge findDependencyEdge(DependencyGraph graph, ObjectNode src, ObjectNode dst) { + for (Edge e : graph.getEdges()) { + DependencyEdge de = (DependencyEdge) e; + if (isSameNode(de.getSource(), src) && isSameNode(de.getDestination(), dst)) { + return de; + } + } + return null; + } + + private boolean isSameNode(Node n1, Node n2) { + if (n1 == n2) return true; + if (n1 == null || n2 == null) return false; + if (n1 instanceof StatefulObjectNode && n2 instanceof StatefulObjectNode) { + return ((StatefulObjectNode) n1).getResource() == ((StatefulObjectNode) n2).getResource(); + } + if (n1 instanceof EventChannelObjectNode && n2 instanceof EventChannelObjectNode) { + return ((EventChannelObjectNode) n1).getIOChannel() == ((EventChannelObjectNode) n2).getIOChannel(); + } + return false; + } + + private mxCell getCellFromNode(ObjectNode node) { + DependencyModelingStage ddmStage = (DependencyModelingStage) stage; + if (node instanceof StatefulObjectNode) { + for (Map.Entry entry : ddmStage.getCellToStatefulObjectNodeMap().entrySet()) { + if (isSameNode(entry.getValue(), node)) return entry.getKey(); + } + } else if (node instanceof EventChannelObjectNode) { + for (Map.Entry entry : ddmStage.getCellToEventChannelObjectNodeMap().entrySet()) { + if (isSameNode(entry.getValue(), node)) return entry.getKey(); + } + } + return null; + } + /** * Show dialog to edit interface name and method code */ diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyModelingStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyModelingStage.java index 64b3a64..0b71e41 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyModelingStage.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DependencyModelingStage.java @@ -54,7 +54,6 @@ //-------------------------------------------------- public InterfaceNode getInterfaceNode(mxCell cell) { - System.out.println("edge: "+ dependencyGraph.getEdges().size() +" node: " + dependencyGraph.getNodes().size() + "\n" + dependencyGraph.getNodes()); return cellToInterfaceNode.get(cell); } @@ -70,6 +69,16 @@ return cellToEventChannelObjectNode.get(cell); } + public Map getCellToInterfaceNodeMap() { + return cellToInterfaceNode; + } + public Map getCellToStatefulObjectNodeMap() { + return cellToStatefulObjectNode; + } + public Map getCellToEventChannelObjectNodeMap() { + return cellToEventChannelObjectNode; + } + public void setInterfaceNode(mxCell cell, InterfaceNode interfaceNode) { cellToInterfaceNode.put(cell, interfaceNode); } @@ -85,7 +94,6 @@ mxCell parent = (mxCell) graph.getDefaultParent(); mxCell layer = (mxCell) parent.getChildAt(DEPENDENCY_LAYER); - //↓clear機能を改善しない限り、pushpull編集後の影響を反映できない if (layer.getChildCount() == 0) { clearDependencyGraphCells(graph); DataFlowGraph dataFlowGraph = ((PushPullSelectionStage) prevStage).getDataFlowGraph(); @@ -95,8 +103,6 @@ statefulObjectNodeToCell = buildStatefulObjectNodeMap(prevStage); eventChannelObjNodeToCell = buildEventChannelObjNodeMap(prevStage); - - graph = constructGraph(graph, dependencyGraph); } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dependencyModel/DependencyGraph.java b/AlgebraicDataflowArchitectureModel/src/models/dependencyModel/DependencyGraph.java index 0c544b4..66b77bc 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dependencyModel/DependencyGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dependencyModel/DependencyGraph.java @@ -122,7 +122,6 @@ EventChannelObjectNode ecoNode = (EventChannelObjectNode) node; if (!objectNodeMap.containsKey(ecoNode)) { ecoNode = new EventChannelObjectNode(((EventChannelObjectNode) node).getIOChannel()); - System.out.println(node +" " + ecoNode); objectNodeMap.put(node, ecoNode); super.addNode(ecoNode); return ecoNode; diff --git a/AlgebraicDataflowArchitectureModel/src/models/dependencyModel/InterfaceNode.java b/AlgebraicDataflowArchitectureModel/src/models/dependencyModel/InterfaceNode.java index 1b482fb..7fd2942 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dependencyModel/InterfaceNode.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dependencyModel/InterfaceNode.java @@ -2,15 +2,17 @@ import models.Node; import models.controlFlowModel.ObjectNode; +import java.util.HashSet; +import java.util.Set; public class InterfaceNode extends Node { private String name; - private ObjectNode dependencySideNode = null; + private Set dependencySideNodes = new HashSet<>(); private ObjectNode implementSideNode = null; - public InterfaceNode(ObjectNode dpnNode, ObjectNode impNode,String name) { - dependencySideNode = dpnNode; - implementSideNode = impNode; + public InterfaceNode(ObjectNode dpnNode, ObjectNode impNode, String name) { + this.dependencySideNodes.add(dpnNode); + this.implementSideNode = impNode; this.name = name; } @@ -22,6 +24,15 @@ this.name = name; } - public ObjectNode getDependencySideNode() { return dependencySideNode; } - public ObjectNode getImplementSideNode() { return implementSideNode; } + public Set getDependencySideNodes() { + return dependencySideNodes; + } + + public void addDependencySideNode(ObjectNode node) { + dependencySideNodes.add(node); + } + + public ObjectNode getImplementSideNode() { + return implementSideNode; + } } \ No newline at end of file