diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java index 75aa05f..1b4bb9a 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java @@ -17,9 +17,8 @@ import models.Edge; import models.Node; import models.controlFlowModel.*; -import models.dataFlowModel.DataFlowGraph; -import models.dataFlowModel.PushPullValue; -import models.dataFlowModel.ResourceNode; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.*; import javax.swing.*; import java.awt.event.MouseEvent; @@ -92,7 +91,6 @@ model = prevStage.getModel(); DataFlowGraph dataFlowGraph = ((PushPullSelectionStage) prevStage).getDataFlowGraph(); - controlFlowGraph = new ControlFlowGraph(dataFlowGraph, model); clearControlFlowGraphCells(graph); @@ -529,15 +527,14 @@ graph.getModel().beginUpdate(); try { // PUSH/PULLごとに ResourceNode, mxCellのインスタンスを対応させる. - Map pushResNodeCells = createCellsOfResourceMap(PUSH_FLOW_LAYER); - Map pullResNodeCells = createCellsOfResourceMap(PULL_FLOW_LAYER); + Map pushResourceNodeCells = createResourceNodeCells(graph, controlFlowGraph, PUSH_FLOW_LAYER); + Map pullResourceNodeCells = createResourceNodeCells(graph, controlFlowGraph, PULL_FLOW_LAYER); - Map pushFlowEventNodeCells = createEventChannelCells(PUSH_FLOW_LAYER); + Map pushFlowEventNodeCells = createEventChannelNodeCells(graph, controlFlowGraph, PUSH_FLOW_LAYER); - // PUSH/PULLのmxGraph上の頂点をエッジでつなぐ. - graph = insertControlFlowEdges(PUSH_FLOW_LAYER, pushResNodeCells, pushFlowEventNodeCells); - graph = insertControlFlowEdges(PULL_FLOW_LAYER, pullResNodeCells, null); - + // PUSH/PULLのmxGraph上の頂点をエッジでつなぐ. + graph = insertControlFlowEdges(PUSH_FLOW_LAYER, pushResourceNodeCells, pushFlowEventNodeCells); + graph = insertControlFlowEdges(PULL_FLOW_LAYER, pullResourceNodeCells, null); } finally { graph.getModel().endUpdate(); } @@ -567,124 +564,121 @@ } /** - * mxGraphの指定したレイヤー上にリソースのノードを追加する. - * また, mxGraph上のセルにリソースの情報をマッピングする. + * Create and register a resource node cell on the specified layer. * - * @param layerNumber mxCellを追加する, PUSH/PULLのレイヤー番号 - * @return ノード(リソース)の情報と, 対応するセルのインスタンスのマップ + * @param graph The {@link mxGraph} to be modified. + * @param controlFlowGraph An instance of {@link ControlFlowGraph} to be used to construct a graph. + * @param layer The layer number + * @return The map of {@link ResourceNode} and its corresponding cell. */ - private Map createCellsOfResourceMap(final int layerNumber) { - Map resNodeCells = new HashMap<>(); - + private Map createResourceNodeCells(final mxGraph graph, final ControlFlowGraph controlFlowGraph, final int layer) { mxCell root = (mxCell) graph.getDefaultParent(); - mxCell nodeLayerCell = (mxCell) root.getChildAt(NODE_LAYER); - mxCell layerCell = (mxCell) root.getChildAt(layerNumber); + mxCell layerCell = (mxCell) root.getChildAt(layer); + final Map resourceNodeCells = new HashMap<>(); - // データフローグラフ上に存在するリソースの情報をコントロールフローグラフに引き継ぐ. - for (ResourceNode resNode : controlFlowGraph.getDataFlowGraph().getResourceNodes()) { - // 「リソース」に対応するノードのインスタンスを生成. - ObjectNode objNode = null; - switch (layerNumber) { - case PUSH_FLOW_LAYER: { - if (controlFlowGraph.getPushCallGraph().getStatefulObjectNode(resNode) != null) { - objNode = controlFlowGraph.getPushCallGraph().getStatefulObjectNode(resNode); - } - break; - } - case PULL_FLOW_LAYER: { - if (controlFlowGraph.getPullCallGraph().getStatefulObjectNode(resNode) != null) { - objNode = controlFlowGraph.getPullCallGraph().getStatefulObjectNode(resNode); - } - break; - } - } + for (ResourceNode rootNode : controlFlowGraph.getDataFlowGraph().getRootResourceNodes()) { + ResourcePath rootResourcePath = rootNode.getPrimaryResourcePath(); + int width = 320; + int height = 160; - if (objNode == null) continue; + mxCell insertedCell = (mxCell) graph.insertVertex(layerCell, null, rootResourcePath.getLeafResourceName(), 20, 20, width, height, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + resourceNodeCells.put(rootNode, insertedCell); - for (int i = 0; i < nodeLayerCell.getChildCount(); i++) { - mxCell nodeCell = (mxCell) nodeLayerCell.getChildAt(i); - - // TODO: ResourceNodeAttributeをしっかりと追加する -// if (nodeCell.getValue() instanceof ResourceNodeAttribute) { -// nodeCell = (mxCell) nodeLayerCell.getChildAt(i); -// } else continue; - - // TODO: ResourceNodeAttributeをしっかりと追加する - // ResourceNodeがちゃんとAttributeを持っているかをチェックして, 不正値検出しようとしてるみたい. -// ResourceNodeAttribute resNodeAttr = (ResourceNodeAttribute) nodeCell.getValue(); -// if (!resNodeAttr.getResourceNode().equals(resNode)) continue; - - // ノードの情報をmxCellにマッピング. - ObjectNodeAttribute objNodeAttr = new ObjectNodeAttribute(objNode); - - mxCell resNodeObjCell = (mxCell) graph.insertVertex(layerCell, null, objNodeAttr, - /* サイズ */nodeCell.getGeometry().getX(), nodeCell.getGeometry().getY(), - /* 座標 */nodeCell.getGeometry().getWidth(), nodeCell.getGeometry().getHeight(), objNodeAttr.getDefaultStyle()); - - resNodeCells.put(resNode, resNodeObjCell); - } + createChildResourceNodeCell(graph, insertedCell, rootNode, resourceNodeCells, width / 2, height / 2); } - return resNodeCells; + return resourceNodeCells; } /** - * mxGraphの指定したレイヤー上にイベントチャンネルのノードを追加する. - * また, mxGraph上のセルにイベントチャンネルの情報をマッピングする. + * Create and register a new graph cell for a child resource node. * - * @param layerNumber 指定したレイヤー(現状PULLは例外扱いしている) - * @return イベントチャンネルの情報と, 対応するセルのインスタンスのマップ + * @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 Map createEventChannelCells(final int layerNumber) { - if (layerNumber == PULL_FLOW_LAYER) return null; - - mxCell root = (mxCell) graph.getDefaultParent(); - mxCell layerCell = (mxCell) root.getChildAt(layerNumber); - - Map eventChannelCells = new HashMap<>(); - - graph.getModel().beginUpdate(); - try { - mxGeometry outPortGeometry = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); - outPortGeometry.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); - outPortGeometry.setRelative(true); + private void createChildResourceNodeCell(final mxGraph graph, final mxCell parentCell, final ResourceNode parentNode, final Map resourceNodeCells, int width, int height) { + for (ResourceNode childNode : parentNode.getChildren()) { + ResourcePath resourcePath = childNode.getPrimaryResourcePath(); - CallGraph callGraph = controlFlowGraph.getPushCallGraph(); - - // イベントチャンネルのセルを追加 - for (Node node : callGraph.getNodes()) { - EventChannelObjectNode entryPointObjNode = null; - if (node instanceof EventChannelObjectNode) entryPointObjNode = (EventChannelObjectNode) node; - else continue; - - ObjectNodeAttribute entryObjAttr = new ObjectNodeAttribute(entryPointObjNode); - - // イベントチャンネルの名前と座標値をデータフローグラフから取得. - mxCell dataFlowLayerCell = (mxCell) root.getChildAt(Stage.DATA_FLOW_LAYER); - for (int i = 0; i < dataFlowLayerCell.getChildCount(); i++) { - mxCell channelCell = (mxCell) dataFlowLayerCell.getChildAt(i); - - String eventChObjNodeName = entryPointObjNode.getIOChannel().getChannelName(); - String channelCellName = ""; - if (channelCell.getValue() instanceof String) channelCellName = (String) channelCell.getValue(); - else continue; - - if (!eventChObjNodeName.equals(channelCellName)) continue; - - mxCell entryPointCelll = (mxCell) graph.insertVertex(layerCell, null, entryObjAttr, - /* スケール */ channelCell.getGeometry().getX(), channelCell.getGeometry().getY(), - /* 座標 */ channelCell.getGeometry().getWidth(), channelCell.getGeometry().getHeight()); - mxCell port_out = new mxCell(null, outPortGeometry, "shape=ellipse;perimter=ellipsePerimeter"); - port_out.setVertex(true); - - graph.addCell(port_out, entryPointCelll); - eventChannelCells.put(entryPointObjNode, entryPointCelll); - } + mxGeometry parentGeo = parentCell.getGeometry(); + double x = 0; + double y = 0; + if (parentGeo != null) { + x = (parentGeo.getWidth() - width) / 2; + y = (parentGeo.getHeight() - height) / 2; } - } finally { - graph.getModel().endUpdate(); + + mxCell insertedCell = (mxCell) graph.insertVertex(parentCell, null, resourcePath.getName(), x, y, width, height, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + resourceNodeCells.put(childNode, insertedCell); + + createChildResourceNodeCell(graph, insertedCell, childNode, resourceNodeCells, width / 2, height / 2); + } + } + + /** + * Create and register a new graph cell for event channel nodes. + * + * @param graph {@link mxGraph} instance to be modified. + * @param controlFlowGraph {@link ControlFlowGraph} instance to be used to construct a graph. + * @param layer The layer number. Only {@link Stage#PUSH_FLOW_LAYER} is supported. + * @return The map of {@link ChannelNode} and its corresponding cell. + */ + private Map createEventChannelNodeCells(final mxGraph graph, final ControlFlowGraph controlFlowGraph, final int layer) { + final mxCell rootCell = (mxCell) graph.getDefaultParent(); + final mxCell layerCell = (mxCell) rootCell.getChildAt(layer); + final Map channelNodeCells = new HashMap<>(); + final Map outputChannelNodeCells = new HashMap<>(); // TODO: Name it properly + + for (ChannelNode rootChannelNode : controlFlowGraph.getDataFlowGraph().getRootChannelNodes()) { + createEventChannelNodeCell(graph, layerCell, rootChannelNode, channelNodeCells, outputChannelNodeCells, 200, 100); + } + return channelNodeCells; + } + + /** + * Create and register a new cell for a specific channel node and its children + * + * @param graph {@link mxGraph} instance to be modified. + * @param parentCell Parent cell of the new cell. + * @param channelNode {@link ChannelNode} instance to be registered. + * @param channelNodeCells {@link ChannelNode} and its corresponding cell. + * @param outputChannelNodeCells {@link ChannelNode} and its corresponding output port cell. + * @param cellWidth The width of a new cell. + * @param cellHeight The height of a new cell. + */ + private void createEventChannelNodeCell(final mxGraph graph, final mxCell parentCell, final ChannelNode channelNode, Map channelNodeCells, Map outputChannelNodeCells, double cellWidth, double cellHeight) { + DataTransferChannel channel = channelNode.getChannel(); + boolean isEventChannel = channel.getInputResources().isEmpty(); + if (!isEventChannel) { + return; } - return eventChannelCells; + // Corresponding cell is already created + if (outputChannelNodeCells.get(channelNode) != null) { + return; + } + + // Get channel information + String channelName = channel.getChannelName(); + + // 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 + channelNodeCells.put(channelNode, insertedChannelCell); + + // Attach an output port to the channel + mxGeometry outputPortGeometry = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + mxCell outputPort = new mxCell(null, outputPortGeometry, "shape=ellipse;perimeter=ellipsePerimeter"); + outputPortGeometry.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + outputPortGeometry.setRelative(true); + outputPort.setVertex(true); + graph.addCell(outputPort, insertedChannelCell); // insert the output port of a channel + outputChannelNodeCells.put(channelNode, outputPort); + + // Create child cells for the channel node recursively + for (ChannelNode childChannelNode : channelNode.getChildren()) { + createEventChannelNodeCell(graph, insertedChannelCell, childChannelNode, channelNodeCells, outputChannelNodeCells, cellWidth / 2.0, cellHeight / 2.0); + } } /** @@ -694,7 +688,7 @@ * @param resNodeCells リソースと, その頂点セルのインスタンスのマップ * @param eventChNodeCells イベントチャンネルと, その頂点セルのインスタンスのマップ */ - private mxGraph insertControlFlowEdges(final int layerNumber, final Map resNodeCells, final Map eventChNodeCells) { + private mxGraph insertControlFlowEdges(final int layerNumber, final Map resNodeCells, final Map eventChNodeCells) { mxCell root = (mxCell) graph.getDefaultParent(); mxCell layerCell = (mxCell) root.getChildAt(layerNumber); @@ -718,8 +712,14 @@ srcResNode = ((StatefulObjectNode) callEdge.getSource()).getResource(); srcNodeCell = resNodeCells.get(srcResNode); } else if (callEdge.getSource() instanceof EventChannelObjectNode) { - srcResNode = (EventChannelObjectNode) callEdge.getSource(); - srcNodeCell = eventChNodeCells.get(srcResNode); + srcResNode = callEdge.getSource(); + EventChannelObjectNode eventChNode = (EventChannelObjectNode) srcResNode; + DataTransferChannel channel = eventChNode.getIOChannel(); + ChannelNode channelNode = controlFlowGraph.getDataFlowGraph().getChannelNode(channel); + + if (eventChNodeCells == null) continue; + srcNodeCell = eventChNodeCells.get(channelNode); + if (srcNodeCell == null) continue; srcOutPortCell = (mxCell) srcNodeCell.getChildAt(0); } else continue; @@ -727,19 +727,53 @@ CallEdgeAttribute callEdgeAttr = new CallEdgeAttribute(callEdge, (ObjectNode) callEdge.getSource(), srcNodeCell, dstNodeCell); - if (srcResNode instanceof ResourceNode) { - graph.insertEdge(layerCell, null, callEdgeAttr, srcNodeCell, dstNodeCell, "movable=false;"); - } - // イベントチャンネルはoutPortのセルの座標を参照. - else if (srcResNode instanceof EventChannelObjectNode) { - graph.insertEdge(layerCell, null, callEdgeAttr, srcOutPortCell, dstNodeCell, "movable=false;"); - } + mxCell parent = getLCA(srcNodeCell, dstNodeCell); + if (parent == null || parent == graph.getDefaultParent()) parent = layerCell; + if (srcResNode instanceof ResourceNode) { + graph.insertEdge(parent, null, callEdgeAttr, srcNodeCell, dstNodeCell, "movable=false;"); + } + // イベントチャンネルはoutPortのセルの座標を参照. + else if (srcResNode instanceof EventChannelObjectNode) { + graph.insertEdge(parent, null, callEdgeAttr, srcOutPortCell, dstNodeCell, "movable=false;"); + } } return graph; } /** + * Find the Lowest Common Ancestor of two cells. + * + * @param c1 First cell + * @param c2 Second cell + * @return The LCA cell or null + */ + private mxCell getLCA(mxCell c1, mxCell c2) { + List p1 = getPathToRoot(c1); + List p2 = getPathToRoot(c2); + for (mxCell p : p1) { + if (p2.contains(p)) return p; + } + return null; + } + + /** + * Get the path from the cell to the layer root. + * + * @param cell The start cell + * @return List of cells in the path + */ + private List getPathToRoot(mxCell cell) { + List path = new ArrayList<>(); + while (cell != null) { + path.add(cell); + if (cell.getParent() == graph.getDefaultParent()) break; + cell = (mxCell) cell.getParent(); + } + return path; + } + + /** * Add a stateless object node to the control-flow graph * * @param insertObjNode An object node to be added