diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractEditorAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractEditorAction.java index 4354f6f..1a07e72 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractEditorAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractEditorAction.java @@ -16,5 +16,4 @@ public void setEditor(Editor editor) { this.editor = editor; } - } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractPopupAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractPopupAction.java new file mode 100644 index 0000000..95eb4c6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractPopupAction.java @@ -0,0 +1,27 @@ +package application.actions; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public abstract class AbstractPopupAction extends AbstractAction { + protected mxGraphComponent graphComponent = null; + protected mxCell targetCell = null; + + public AbstractPopupAction(final String propName, final mxCell targetCell, final mxGraphComponent graphComponent) { + super(propName); + this.graphComponent = graphComponent; + this.targetCell = targetCell; + } + + public void updateTargetCell(final mxCell targetCell) { + this.targetCell = targetCell; + } + + @Override + public void actionPerformed(ActionEvent e) { + // TODO: Logging + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ChangeCallOrderAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ChangeCallOrderAction.java new file mode 100644 index 0000000..14b3dc1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ChangeCallOrderAction.java @@ -0,0 +1,65 @@ +package application.actions; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; +import models.controlFlowModel.CallEdgeAttribute; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class ChangeCallOrderAction extends AbstractPopupAction { + public ChangeCallOrderAction(final mxGraphComponent graphComponent, final mxCell cell) { + super("changeCallOrder", cell, graphComponent); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + if (targetCell == null) return; + changeCallOrderOfCallEdge(targetCell); + } + + /** + * コントロールフローの呼び出し順を変更する + * + * @param cellObj 選択されたエッジ(コントロールフロー)のセル + */ + private void changeCallOrderOfCallEdge(Object cellObj) { + String input = ""; + int inputOrder = 0; + + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute) graphComponent.getGraph().getModel().getValue(cellObj); + if (callEdgeAttr == null) return; + + input = JOptionPane.showInputDialog("Call order"); + if (input == null) return; + + if (!isNumeric(input)) { + JOptionPane.showMessageDialog(graphComponent, "Input value must type of number."); + return; + } + + inputOrder = Integer.parseInt(input); + + final int endOfOrderOfSrc = callEdgeAttr.getSourceObjectNode().getOutdegree(); + if (inputOrder <= 0 || endOfOrderOfSrc < inputOrder) { + JOptionPane.showMessageDialog(graphComponent, "Input order must be between 1 and " + endOfOrderOfSrc + "."); + return; + } + + int curOrder = callEdgeAttr.getSourceObjectNode().getOutEdgeCallOrder(callEdgeAttr.getCallEdge()); + callEdgeAttr.getSourceObjectNode().sortOutEdgesByCallOrder(curOrder, inputOrder); + + graphComponent.refresh(); + } + + /** + * フォームに入力された文字列が数値か判定 + * + * @param str フォームに入力された文字列 + */ + private boolean isNumeric(final String str) { + if (str == null) return false; + return str.matches("[0-9.]+"); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/IntroduceMediatorObjectAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/IntroduceMediatorObjectAction.java new file mode 100644 index 0000000..ffa8a02 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/IntroduceMediatorObjectAction.java @@ -0,0 +1,82 @@ +package application.actions; + +import application.editor.stages.ControlFlowModelingStage; +import application.editor.stages.ControlFlowModelingStageStatus; +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; +import models.controlFlowModel.ObjectNodeAttribute; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class IntroduceMediatorObjectAction extends AbstractPopupAction { + + private ControlFlowModelingStage stage = null; + + public IntroduceMediatorObjectAction(final ControlFlowModelingStage stage, final mxGraphComponent graphComponent, final mxCell cell) { + super(/*propName*/"insertMediator", cell, graphComponent); + this.stage = stage; + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + insertObjectNode(targetCell); + this.stage.setState(ControlFlowModelingStageStatus.SELECTING_AN_EDGE); + } + + /** + * 仲介者オブジェクトの導入を実行する. + * + * @param cellObj 選択されたエッジのセル + */ + private void insertObjectNode(final Object cellObj) { + if (cellObj == null) return; + + mxCell edgeCell = null; + if (cellObj instanceof mxCell) edgeCell = (mxCell) cellObj; + else return; + + // Inputing name to the dialog. + String objName = JOptionPane.showInputDialog("Object Name:"); + if (objName == null) return; + + if (objName.isEmpty()) { + JOptionPane.showMessageDialog(graphComponent, "You must input a name. \nIt mustn't be empty."); + return; + } + + if (isDuplicatedName(objName)) { + JOptionPane.showMessageDialog(graphComponent, "The named object has already existed."); + return; + } + + stage.insertObjectNodeCellInControlFlowLayer(edgeCell, objName); + } + + /** + * 同名のノードがすでにあるか判定する + * + * @param name つけたいノード名 + */ + private boolean isDuplicatedName(final String name) { + mxCell root = (mxCell) graphComponent.getGraph().getDefaultParent(); + + for (int i = 0; i < root.getChildCount(); i++) { + mxCell layerCell = (mxCell) root.getChildAt(i); + + for (int j = 0; j < layerCell.getChildCount(); j++) { + mxCell cell = (mxCell) layerCell.getChildAt(j); + + ObjectNodeAttribute attr = null; + if (cell.getValue() instanceof ObjectNodeAttribute) + attr = (ObjectNodeAttribute) cell.getValue(); + else continue; + + if (!(attr.getObjectNode().getName().equals(name))) continue; + return true; + } + } + return false; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ShowDependentableMediatorAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowDependentableMediatorAction.java new file mode 100644 index 0000000..24f6631 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowDependentableMediatorAction.java @@ -0,0 +1,40 @@ +package application.actions; + +import application.editor.stages.ControlFlowModelingStage; +import application.editor.stages.ControlFlowModelingStageStatus; +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; +import models.controlFlowModel.CallEdgeAttribute; + +import java.awt.event.ActionEvent; + +public class ShowDependentableMediatorAction extends AbstractPopupAction { + private ControlFlowModelingStage stage = null; + + public ShowDependentableMediatorAction(final ControlFlowModelingStage stage, final mxGraphComponent graphComponent, final mxCell targetCell) { + super("dependsOnMediator", targetCell, graphComponent); + this.stage = stage; + } + + /** + * DoM適用可能範囲の表示まで実行する. + */ + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + showDependentableNodesBySelectedEdge(targetCell); + stage.setState(ControlFlowModelingStageStatus.SHOWING_DEPENDABLE_NODES); + // ⇒ 以降のDoM実行操作は, 【ControlFlowDelgationCellEditor】で実行される. + } + + /** + * 選択されたセルの, DoM実行な仲介者オブジェクトを表示する. + */ + private void showDependentableNodesBySelectedEdge(Object cellObj) { + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute) graphComponent.getGraph().getModel().getValue(cellObj); + if (callEdgeAttr == null) return; + + ControlFlowModelingStage cfdStage = (ControlFlowModelingStage) stage; + cfdStage.showDependentableNodes(callEdgeAttr); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java index 0dc3ed6..878ef60 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java @@ -1,5 +1,6 @@ package application.editor; +import application.editor.stages.ControlFlowModelingStage; import application.editor.stages.DataFlowModelingStage; import application.editor.stages.PushPullSelectionStage; import application.layouts.DAGLayout; @@ -16,6 +17,7 @@ import models.EdgeAttribute; import models.algebra.Symbol; import models.algebra.Variable; +import models.controlFlowModel.ControlFlowGraph; import models.dataConstraintModel.Channel; import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataFlowGraph; @@ -51,6 +53,7 @@ public static DataFlowModelingStage STAGE_DATA_FLOW_MODELING = null; public static PushPullSelectionStage STAGE_PUSH_PULL_SELECTION = null; + public static ControlFlowModelingStage STAGE_CONTROL_FLOW_DELEGATION = null; public Editor(mxGraphComponent graphComponent) { this.graphComponent = graphComponent; @@ -58,6 +61,7 @@ STAGE_DATA_FLOW_MODELING = new DataFlowModelingStage(graphComponent); STAGE_PUSH_PULL_SELECTION = new PushPullSelectionStage(graphComponent); + STAGE_CONTROL_FLOW_DELEGATION = new ControlFlowModelingStage(graphComponent); graphComponent.setCellEditor(STAGE_DATA_FLOW_MODELING.createCellEditor(graphComponent)); @@ -113,6 +117,15 @@ public DataFlowGraph getDataFlowGraph() { if (curStage instanceof PushPullSelectionStage) { return ((PushPullSelectionStage) curStage).getDataFlowGraph(); + } else if (curStage instanceof ControlFlowModelingStage) { + return ((ControlFlowModelingStage) curStage).getControlFlowGraph().getDataFlowGraph(); + } + return null; + } + + public ControlFlowGraph getControlFlowGraph() { + if (curStage instanceof ControlFlowModelingStage) { + return ((ControlFlowModelingStage) curStage).getControlFlowGraph(); } return null; } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingCellEditor.java new file mode 100644 index 0000000..cd1d7c5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingCellEditor.java @@ -0,0 +1,140 @@ +package application.editor.stages; + +import application.editor.FlowCellEditor; +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; +import models.controlFlowModel.*; + +import javax.swing.*; +import java.util.EventObject; + +public class ControlFlowModelingCellEditor extends FlowCellEditor { + private mxCell targetEdgeCell = null; + + public ControlFlowModelingCellEditor(ControlFlowModelingStage stage, mxGraphComponent graphComponent) { + super(stage, graphComponent); + } + + @Override + public void startEditing(Object cellObj, EventObject eventObj) { + if (editingCell != null) stopEditing(true); + ControlFlowModelingStage cfdStage = (ControlFlowModelingStage) stage; + + switch (cfdStage.getCurState()) { + case SELECTING_AN_EDGE: { + if (graphComponent.getGraph().getModel().isEdge(cellObj)) { + + targetEdgeCell = (mxCell) cellObj; + + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute) graphComponent.getGraph().getModel().getValue(targetEdgeCell); + if (callEdgeAttr == null) return; + + String pairNodeStr = "(" + callEdgeAttr.getSourceObjectNode().getName() + ", " + callEdgeAttr.getDestinationObjectNode().getName() + ")"; + + showDelegatableNodesBySelectedEdge(cellObj); + cfdStage.setState(ControlFlowModelingStageStatus.SHOWING_DELEGATABLE_NODES); + } + break; + } + case SHOWING_DELEGATABLE_NODES: { + if (graphComponent.getGraph().getModel().isVertex(cellObj)) { + mxCell dstCell = null; + if (cellObj instanceof mxCell) dstCell = (mxCell) cellObj; + else throw new ClassCastException(); + + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute) graphComponent.getGraph().getModel().getValue(targetEdgeCell); + if (callEdgeAttr == null) return; + + ObjectNode dstObjNode = ((ObjectNodeAttribute) dstCell.getValue()).getObjectNode(); + if (dstObjNode == null) throw new ClassCastException(); + + // dstObjNode はCFD適用可能なノードかどうか? + if (!cfdStage.isExecutableDelegation(callEdgeAttr, dstObjNode)) { + JOptionPane.showMessageDialog(graphComponent, "It's impossible for \"" + dstObjNode.getName() + "\" to delegate."); + return; + } + + // CFD実行 -> 実行後のグラフを表示. + cfdStage.showDelegatedGraph(graphComponent.getGraph(), targetEdgeCell, (mxCell) cellObj); + cfdStage.setState(ControlFlowModelingStageStatus.SELECTING_AN_EDGE); + } else { + cfdStage.resetAllStyleOfCells(); + cfdStage.setState(ControlFlowModelingStageStatus.SELECTING_AN_EDGE); + } + break; + } + case SHOWING_DEPENDABLE_NODES: { + if (cfdStage.getCachedCell() == null) return; + this.targetEdgeCell = cfdStage.getCachedCell(); + + if (graphComponent.getGraph().getModel().isVertex(cellObj)) { + mxCell targetObjCell = null; + if (cellObj instanceof mxCell) targetObjCell = (mxCell) cellObj; + else throw new ClassCastException(); + + ObjectNodeAttribute targetObjNodeAttr = (targetObjCell.getValue() instanceof ObjectNodeAttribute) ? (ObjectNodeAttribute) targetObjCell.getValue() : null; + if (targetObjNodeAttr == null) return; + + ObjectNode targetObjNode = targetObjNodeAttr.getObjectNode(); + + // 仲介者以外のオブジェクトを選択したときの例外処理 + if (isNotStatelessObject(targetObjNode)) { + JOptionPane.showMessageDialog(graphComponent, "It's impossible for \"" + targetObjNode.getName() + "\" to dependent."); + return; + } + + // DoMを実行 + dependingObjectNode(targetObjCell); + + cfdStage.resetAllStyleOfCells(); + cfdStage.setState(ControlFlowModelingStageStatus.SELECTING_AN_EDGE); + } else { + cfdStage.resetAllStyleOfCells(); + cfdStage.setState(ControlFlowModelingStageStatus.SELECTING_AN_EDGE); + } + break; + } + } + } + + @Override + public void stopEditing(boolean cancel) { + // Do nothing + } + + /** + * 選択されたエッジが, CFDによって呼び出し元にできるオブジェクトを表示する. + * + * @param cellObj コントロールフローのセル + */ + private void showDelegatableNodesBySelectedEdge(Object cellObj) { + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute) graphComponent.getGraph().getModel().getValue(cellObj); + if (callEdgeAttr == null) return; + + ControlFlowModelingStage cfdStage = (ControlFlowModelingStage) stage; + cfdStage.showDelegatableNodes(callEdgeAttr); + } + + /** + * DoMを実行する. + * + * @param targetMediatorCell 選択された仲介者オブジェクト + */ + private void dependingObjectNode(final mxCell targetMediatorCell) { + if (targetMediatorCell == null) return; + + ControlFlowModelingStage cfdStage = (ControlFlowModelingStage) stage; + cfdStage.dependingOnMediatorObject(targetEdgeCell, targetMediatorCell); + } + + /** + * あるノードが仲介者(状態を持たない)オブジェクトかどうかを判定する. + * + * @param targetObjNode 選択されたノード + */ + private boolean isNotStatelessObject(final ObjectNode targetObjNode) { + if (targetObjNode instanceof StatefulObjectNode) return true; + if (targetObjNode instanceof EventChannelObjectNode) return true; + return false; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java new file mode 100644 index 0000000..2f60a72 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java @@ -0,0 +1,769 @@ +package application.editor.stages; + +import application.editor.Editor; +import application.editor.FlowCellEditor; +import application.editor.Stage; +import application.views.PopupMenuBase; +import application.views.controlFlowDelegation.ControlFlowDelegationStagePopupMenu; +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.util.mxMouseAdapter; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.util.mxPoint; +import com.mxgraph.view.mxGraph; +import models.Edge; +import models.Node; +import models.controlFlowModel.*; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResourceNode; + +import javax.swing.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ControlFlowModelingStage extends Stage { + public final int PORT_DIAMETER = 8; + public final int PORT_RADIUS = PORT_DIAMETER / 2; + + private ControlFlowModelingStageStatus curState = null; + + private ControlFlowGraph controlFlowGraph = null; + private PopupMenuBase popupMenu = null; + + private mxCell selectedCellOnAnyEvent = null; + + public ControlFlowModelingStage(mxGraphComponent graphComponent) { + super(graphComponent); + this.curState = ControlFlowModelingStageStatus.SELECTING_AN_EDGE; + this.popupMenu = new ControlFlowDelegationStagePopupMenu(this, graphComponent); + } + + public ControlFlowGraph getControlFlowGraph() { + return controlFlowGraph; + } + + /** + * 今のコントロールフローの操作状態がどうなっているかを取得 + */ + public ControlFlowModelingStageStatus getCurState() { + return curState; + } + + /** + * 今のコントロールフローの操作状態をセット. + */ + public void setState(ControlFlowModelingStageStatus nextState) { + curState = nextState; + } + + /** + * Stage クラス以外の, 他のクラス上で取得されたセルをセット + */ + public void setCellOnAnyEvent(final mxCell selectedCell) { + this.selectedCellOnAnyEvent = selectedCell; + } + + /** + * 「Stage」クラス以外の, 他のクラス上で取得されたセルをget + * Action や CellEditor とセルのインスタンス情報を共有するために必要. + * (this.seletedCellOnAnyEvent == null) ならグラフ上のどのセルも選択されていないことになる. + */ + public mxCell getCachedCell() { + return this.selectedCellOnAnyEvent; + } + + @Override + public boolean canChangeFrom(Stage prevStage) { + if (prevStage instanceof PushPullSelectionStage) return true; + return false; + } + + @Override + public void init(Stage prevStage) { + if (prevStage instanceof PushPullSelectionStage) { + model = prevStage.getModel(); + + DataFlowGraph dataFlowGraph = ((PushPullSelectionStage) prevStage).getDataFlowGraph(); + + controlFlowGraph = new ControlFlowGraph(dataFlowGraph, model); + + clearControlFlowGraphCells(graph); + graph = constructGraph(graph, controlFlowGraph); + } + } + + @Override + public FlowCellEditor createCellEditor(mxGraphComponent graphComponent) { + return new ControlFlowModelingCellEditor(this, graphComponent); + } + + /** + * Create an event listener for changing the terminal of edges + * + * @param editor The editor instance + * @return An event listener for changing the terminal of edges + */ + @Override + public mxIEventListener createChangeEventListener(Editor editor) { + return new mxIEventListener() { + public void invoke(Object sender, mxEventObject evt) { + List terminals = new ArrayList<>(); + mxCell cell = null; + for (Object change : ((List) evt.getProperties().get("changes"))) { + if (change instanceof mxGraphModel.mxTerminalChange) { + mxGraphModel.mxTerminalChange terminalChange = (mxGraphModel.mxTerminalChange) change; + cell = (mxCell) terminalChange.getCell(); + mxCell terminal = (mxCell) terminalChange.getTerminal(); + terminals.add(terminal); + } + } + + if (curState != ControlFlowModelingStageStatus.SELECTING_AN_EDGE) return; + + if (terminals.size() == 2) { + graph.removeCells(new mxCell[]{cell}); + graph.clearSelection(); + } + } + }; + } + + /** + * Create a mouse event listener to handle clicks on the graph + * + * @param editor The editor instance + */ + @Override + public MouseListener createMouseEventListener(Editor editor) { + MouseListener listener = new mxMouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e)) { + if (graphComponent.getCellAt(e.getX(), e.getY()) != null) return; + if (curState.equals(ControlFlowModelingStageStatus.SELECTING_AN_EDGE)) return; + + resetAllStyleOfCells(); + curState = ControlFlowModelingStageStatus.SELECTING_AN_EDGE; + } else if (SwingUtilities.isRightMouseButton(e)) { + popupMenu.show(e.getX(), e.getY()); + } + } + }; + return listener; + } + + /** + * CFDが適用できるノードの表示 + * + * @param callEdgeAttr 選択された呼び出しエッジの情報 + */ + public void showDelegatableNodes(final CallEdgeAttribute callEdgeAttr) { + mxCell root = (mxCell) graph.getDefaultParent(); + + graph.getModel().beginUpdate(); + try { + ObjectNode delegatingNode = callEdgeAttr.getDestinationObjectNode(); + for (int layerNo = Stage.PUSH_FLOW_LAYER; layerNo <= PULL_FLOW_LAYER; layerNo++) { + + mxCell layerCell = (mxCell) root.getChildAt(layerNo); + for (Object node : graph.getChildVertices(layerCell)) { + if (!(node instanceof mxCell)) continue; + mxCell cell = (mxCell) node; + + ObjectNodeAttribute objNodeAttr = (ObjectNodeAttribute) cell.getValue(); + if (objNodeAttr == null) return; + + ObjectNode objNode = objNodeAttr.getObjectNode(); + ControlFlowDelegator delegator = new ControlFlowDelegator(controlFlowGraph); + List delegatableNodes = delegator.searchDelegatableNodes(callEdgeAttr.getCallEdge()); + + if (delegatableNodes.contains(objNode)) { + graph.getModel().setStyle(cell, objNodeAttr.getEnableStyle()); + } else { + graph.getModel().setStyle(cell, objNodeAttr.getDisableStyle()); + } + + if (delegatingNode.equals(objNodeAttr.getObjectNode())) { // Base node + graph.getModel().setStyle(cell, objNodeAttr.getDelegatingStyle()); + } + } + } + } finally { + graph.getModel().endUpdate(); + graph.refresh(); + } + } + + /** + * DoMを実行可能なノードの表示 + * + * @param callEdgeAttr 選択された呼び出しエッジの情報 + */ + public void showDependentableNodes(final CallEdgeAttribute callEdgeAttr) { + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layerCell = (mxCell) root.getChildAt(PULL_FLOW_LAYER); + graph.getModel().beginUpdate(); + try { + ObjectNode delegatingNode = callEdgeAttr.getDestinationObjectNode(); + + for (Object node : graph.getChildVertices(layerCell)) { + if (!(node instanceof mxCell)) continue; + mxCell cell = (mxCell) node; + + ObjectNodeAttribute objNodeAttr = (ObjectNodeAttribute) cell.getValue(); + if (objNodeAttr == null) return; + + ObjectNode objNode = objNodeAttr.getObjectNode(); + ControlFlowDelegator delegator = new ControlFlowDelegator(controlFlowGraph); + List delegatableNodes = delegator.searchDependentableMediatorNodes(callEdgeAttr.getCallEdge()); + + if (delegatableNodes.contains(objNode)) { + graph.getModel().setStyle(cell, objNodeAttr.getEnableStyle()); + } else { + graph.getModel().setStyle(cell, objNodeAttr.getDisableStyle()); + } + + if (delegatingNode.equals(objNodeAttr.getObjectNode())) { // Base node + graph.getModel().setStyle(cell, objNodeAttr.getDelegatingStyle()); + } + } + } finally { + graph.getModel().endUpdate(); + graph.refresh(); + } + } + + /** + * CFD実行後のグラフを表示 + * + * @param graph 被操作対象のグラフ + * @param targetEdgeCell CFD実行時に選択された呼び出しエッジ + * @param dstObjNodeCell CFDによって呼び出し元となるセル + */ + public void showDelegatedGraph(mxGraph graph, mxCell targetEdgeCell, final mxCell dstObjNodeCell) { + ObjectNode dstObjNode = ((ObjectNodeAttribute) dstObjNodeCell.getValue()).getObjectNode(); + if (dstObjNode == null) throw new ClassCastException(); + CallEdgeAttribute targetEdgeAttr = (CallEdgeAttribute) targetEdgeCell.getValue(); + if (targetEdgeAttr == null) throw new ClassCastException(); + + ControlFlowDelegator delegator = new ControlFlowDelegator(controlFlowGraph); + delegator.delegateCallEdge(targetEdgeAttr.getCallEdge(), dstObjNode); + + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layerCell = null; + switch (targetEdgeAttr.getSelectedOption()) { + case PUSH: + layerCell = (mxCell) root.getChildAt(Stage.PUSH_FLOW_LAYER); + break; + + case PULL: + case PUSHorPULL: + layerCell = (mxCell) root.getChildAt(Stage.PULL_FLOW_LAYER); + break; + } + + try { + mxCell dstNodeCell = targetEdgeAttr.getDestinationCell(); + + // 対象となるコントロールフローをグラフ上から削除. + if (graph.getModel().getValue(targetEdgeCell) != null) { + graph.getModel().remove(targetEdgeCell); + } + + CallEdgeAttribute newAttr = new CallEdgeAttribute(targetEdgeAttr.getCallEdge(), targetEdgeAttr.getOriginalSourceObjectNode(), dstObjNodeCell, dstNodeCell); + graph.insertEdge(layerCell, "", newAttr, dstObjNodeCell, dstNodeCell, "movable=false;"); + + resetAllStyleOfCells(); + } finally { + graph.getModel().endUpdate(); + } + } + + /** + * 仲介者オブジェクトへの依存(DoM)を実行し, mxGraphを変更する. + * + * @param targetEdgeCell DoMによって変更されるコントロールフローのセル + * @param targetMediatorObjNodeCell DoMの対象となる仲介者オブジェクトのセル + */ + public void dependingOnMediatorObject(mxCell targetEdgeCell, mxCell targetMediatorObjNodeCell) { + CallEdgeAttribute targetCallEdgeAttr = (CallEdgeAttribute) targetEdgeCell.getValue(); + if (targetCallEdgeAttr == null) return; + + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell pullFlowLayerCell = (mxCell) root.getChildAt(Stage.PULL_FLOW_LAYER); + + graph.getModel().beginUpdate(); + + try { + // 各ノードの情報を取得. + ObjectNode srcObjNode = targetCallEdgeAttr.getSourceObjectNode(); + ObjectNode dstObjNode = targetCallEdgeAttr.getDestinationObjectNode(); + if (srcObjNode == null || dstObjNode == null) throw new NullPointerException(); + if (!(srcObjNode instanceof ObjectNode && dstObjNode instanceof ObjectNode)) throw new ClassCastException(); + + ObjectNodeAttribute targetObjNodeAttr = (ObjectNodeAttribute) targetMediatorObjNodeCell.getValue(); + if (targetObjNodeAttr == null) throw new NullPointerException(); + + ObjectNode targetMediatorObjNode = targetObjNodeAttr.getObjectNode(); + if (targetMediatorObjNode == null) throw new NullPointerException(); + if (targetMediatorObjNode instanceof StatefulObjectNode || targetMediatorObjNode instanceof EventChannelObjectNode) + return; + + // 新たに接続するエッジ情報を生成. + CallEdge srcToMediatorEdge = new CallEdge(targetCallEdgeAttr.getSourceObjectNode(), targetMediatorObjNode, targetCallEdgeAttr.getSelectedOption()); + CallEdge mediatorToDstEdge = new CallEdge(targetMediatorObjNode, dstObjNode, targetCallEdgeAttr.getSelectedOption()); + if (srcToMediatorEdge == null || mediatorToDstEdge == null) throw new NullPointerException(); + + // ノード同士の接続情報を更新 + srcObjNode.addOutEdge(srcToMediatorEdge); + srcObjNode.removeOutEdge(targetCallEdgeAttr.getCallEdge()); + + dstObjNode.removeInEdge(targetCallEdgeAttr.getCallEdge()); + + targetMediatorObjNode.addInEdge(srcToMediatorEdge); + + // 既に仲介者に接続されているエッジの情報を CompositeCallEdgeAttr に保持させる. + CompositeCallEdgeAttribute compositeEdgeAttr = null; + + for (int i = 0; i < pullFlowLayerCell.getChildCount(); i++) { + mxCell edgeCell = (mxCell) pullFlowLayerCell.getChildAt(i); + if (!edgeCell.isEdge()) continue; + if (!(edgeCell.getValue() instanceof CallEdgeAttribute)) continue; + + CallEdgeAttribute edgeAttr = (CallEdgeAttribute) edgeCell.getValue(); + + if (!(edgeAttr.getSourceObjectNode().equals(targetMediatorObjNode))) continue; + if (!(edgeAttr.getDestinationObjectNode().equals(dstObjNode))) continue; + + compositeEdgeAttr = new CompositeCallEdgeAttribute(edgeAttr); + + break; + } + + if (compositeEdgeAttr != null) compositeEdgeAttr.mergeCallEdgeAttribute(targetCallEdgeAttr); + + // mxGraph へ接続状態を反映する. + for (int i = 0; i < pullFlowLayerCell.getChildCount(); i++) { + mxCell nodeCell = (mxCell) pullFlowLayerCell.getChildAt(i); + if (!nodeCell.isVertex()) continue; + + ObjectNodeAttribute cellObjNodeAttr = (ObjectNodeAttribute) nodeCell.getValue(); + if (cellObjNodeAttr == null) + throw new ClassCastException("dosen't have the value of "); + + // nodeCell が呼び出し元のノードか? + if (nodeCell.equals(targetCallEdgeAttr.getSourceCell())) { + mxCell srcNodeCell = targetCallEdgeAttr.getSourceCell(); + + // mxGraph に変更対象のコントロールフローが存在するなら削除する. + if (graph.getModel().getValue(targetEdgeCell) != null) graph.getModel().remove(targetEdgeCell); + + graph.insertEdge(pullFlowLayerCell, null, compositeEdgeAttr, srcNodeCell, targetMediatorObjNodeCell, "movable=false;"); + continue; + } + } + } finally { + graph.getModel().endUpdate(); + } + } + + /** + * グラフの選択状態をクリアする. + */ + public void resetAllStyleOfCells() { + mxCell root = (mxCell) graph.getDefaultParent(); + + graph.getModel().beginUpdate(); + try { + for (int layerNo = Stage.PUSH_FLOW_LAYER; layerNo <= Stage.PULL_FLOW_LAYER; layerNo++) { + + mxCell layerCell = (mxCell) root.getChildAt(layerNo); + for (Object node : graph.getChildVertices(layerCell)) { + mxCell cell = null; + if (node instanceof mxCell) cell = (mxCell) node; + else continue; + + ObjectNodeAttribute objNodeAttr = (ObjectNodeAttribute) (cell.getValue()); + if (objNodeAttr == null) throw new NullPointerException(""); + + graph.getModel().setStyle(cell, objNodeAttr.getDefaultStyle()); + } + } + } finally { + graph.getModel().endUpdate(); + graph.refresh(); + } + } + + /** + * コントロールフローに対して, 状態を持たないノードを挿入する. + * + * @param targetEdge CFDによって変更されるコントロールフロー. + * @param insertObjName 挿入するノードの名前 + */ + public void insertObjectNodeCellInControlFlowLayer(mxCell targetEdge, final String insertObjName) { + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute) targetEdge.getValue(); + if (callEdgeAttr == null) throw new NullPointerException(); + + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layerCell = null; + switch (callEdgeAttr.getSelectedOption()) { + case PUSH: + layerCell = (mxCell) root.getChildAt(Stage.PUSH_FLOW_LAYER); + break; + + case PULL: + case PUSHorPULL: + layerCell = (mxCell) root.getChildAt(Stage.PULL_FLOW_LAYER); + break; + } + + graph.getModel().beginUpdate(); + + try { + // オブジェクトは ObjectNode として挿入する. + ObjectNode insertObjNode = new ObjectNode(insertObjName); + ObjectNodeAttribute objNodeAttr = new ObjectNodeAttribute(insertObjNode); + + mxPoint srcPoint = new mxPoint(callEdgeAttr.getSourceCell().getGeometry().getX(), callEdgeAttr.getSourceCell().getGeometry().getY()); + mxPoint dstPoint = new mxPoint(callEdgeAttr.getDestinationCell().getGeometry().getX(), callEdgeAttr.getDestinationCell().getGeometry().getY()); + mxPoint insertPoint = new mxPoint((srcPoint.getX() + dstPoint.getX()) / 2, (srcPoint.getY() + dstPoint.getY()) / 2); + + mxCell insertObjNodeCell = (mxCell) graph.insertVertex(layerCell, null, objNodeAttr, + /* 座標 */ insertPoint.getX(), insertPoint.getY(), + /* スケール*/ 40, 40, objNodeAttr.getDefaultStyle()); + insertObjNodeCell.setValue(objNodeAttr); + + addObjectNodeToCallGraph(insertObjNode, callEdgeAttr.getSelectedOption()); + + ObjectNode srcObjNode = callEdgeAttr.getSourceObjectNode(); + ObjectNode dstObjNode = callEdgeAttr.getDestinationObjectNode(); + if (srcObjNode == null || dstObjNode == null) throw new NullPointerException(); + if (!(srcObjNode instanceof ObjectNode && dstObjNode instanceof ObjectNode)) throw new ClassCastException(); + + CallEdge srcToInsertEdge = callEdgeAttr.getCallEdge(); + CallEdge insertToDstEdge = new CallEdge(insertObjNode, dstObjNode, callEdgeAttr.getSelectedOption()); + if (srcToInsertEdge == null || insertToDstEdge == null) throw new NullPointerException(); + + // ノードとエッジを再接続する + dstObjNode.removeInEdge(srcToInsertEdge); + dstObjNode.addInEdge(insertToDstEdge); + + srcToInsertEdge.setDestination(insertObjNode); // 呼び出し先オブジェクトを変更する. + + insertObjNode.addInEdge(srcToInsertEdge); + insertObjNode.addOutEdge(insertToDstEdge); + + // mxGraphを更新する. + for (int i = 0; i < layerCell.getChildCount(); i++) { + mxCell nodeCell = (mxCell) layerCell.getChildAt(i); + if (!nodeCell.isVertex()) continue; + + ObjectNodeAttribute cellObjNodeAttr = (ObjectNodeAttribute) nodeCell.getValue(); + if (cellObjNodeAttr == null) + throw new ClassCastException("dosen't have the value of "); + + // nodeCell が呼び出し元のオブジェクトか? + if (nodeCell.equals(callEdgeAttr.getSourceCell())) { + mxCell srcNodeCell = callEdgeAttr.getSourceCell(); + CallEdgeAttribute newInEdgeAttr = new CallEdgeAttribute(srcToInsertEdge, srcNodeCell, insertObjNodeCell); + + // mxGraph上にtargetEdgeが残っているなら削除する. + if (graph.getModel().getValue(targetEdge) != null) graph.getModel().remove(targetEdge); + + mxCell outPortCell = (mxCell) srcNodeCell.getChildAt(0); + if (outPortCell != null) { + graph.insertEdge(layerCell, null, newInEdgeAttr, outPortCell, insertObjNodeCell, "movable=false;"); + } else { + graph.insertEdge(layerCell, null, newInEdgeAttr, srcNodeCell, insertObjNodeCell, "movable=false;"); + } + continue; + } + // nodeCell は呼び出し先オブジェクトか? + else if (nodeCell.equals(callEdgeAttr.getDestinationCell())) { + mxCell dstNodeCell = callEdgeAttr.getDestinationCell(); + CallEdgeAttribute newOutEdgeAttr = new CallEdgeAttribute(insertToDstEdge, insertObjNodeCell, dstNodeCell); + + // mxGraph上にtargetEdgeが残っているなら削除する. + if (graph.getModel().getValue(targetEdge) != null) graph.getModel().remove(targetEdge); + + graph.insertEdge(layerCell, null, newOutEdgeAttr, insertObjNodeCell, dstNodeCell, "movable=false;"); + + continue; + } + } + } finally { + graph.getModel().endUpdate(); + } + } + + /** + * CFD適用可能かどうかを判定する + * + * @param targetEdgeAttr CFDによって呼び出し元が変更されるコントロールフロー + * @param dstObjNode CFDによって呼び出し元となるオブジェクト + */ + public boolean isExecutableDelegation(final CallEdgeAttribute targetEdgeAttr, final ObjectNode dstObjNode) { + ControlFlowDelegator delegator = new ControlFlowDelegator(controlFlowGraph); + List delegatableNodes = delegator.searchDelegatableNodes(targetEdgeAttr.getCallEdge()); + + return delegatableNodes.contains(dstObjNode); + } + + /** + * GUI上のグラフを生成する + * + * @param graph 初期化対象のグラフ + * @param controlFlowGraph The ControlFlowGraph instance + */ + private mxGraph constructGraph(mxGraph graph, ControlFlowGraph controlFlowGraph) { + showLayers(PUSH_FLOW_LAYER, PULL_FLOW_LAYER); + + graph.getModel().beginUpdate(); + try { + // PUSH/PULLごとに ResourceNode, mxCellのインスタンスを対応させる. + Map pushResNodeCells = createCellsOfResourceMap(PUSH_FLOW_LAYER); + Map pullResNodeCells = createCellsOfResourceMap(PULL_FLOW_LAYER); + + Map pushFlowEventNodeCells = createEventChannelCells(PUSH_FLOW_LAYER); + + // PUSH/PULLのmxGraph上の頂点をエッジでつなぐ. + graph = insertControlFlowEdges(PUSH_FLOW_LAYER, pushResNodeCells, pushFlowEventNodeCells); + graph = insertControlFlowEdges(PULL_FLOW_LAYER, pullResNodeCells, null); + + } finally { + graph.getModel().endUpdate(); + } + return graph; + } + + /** + * データフローモデリングステージへ戻るとき, GUI上のコントローフローグラフを削除する. + * + * @param graph 変更対象のグラフ + */ + private void clearControlFlowGraphCells(mxGraph graph) { + mxCell root = (mxCell) graph.getDefaultParent(); + + graph.getModel().beginUpdate(); + try { + // Rootのレイヤー以下のPUSH/PULLレイヤーを削除. + root.remove(root.getChildAt(PULL_FLOW_LAYER)); + root.remove(root.getChildAt(PUSH_FLOW_LAYER)); + + root.insert(new mxCell()); + root.insert(new mxCell()); + } finally { + graph.getModel().endUpdate(); + graph.refresh(); + } + } + + + /** + * mxGraphの指定したレイヤー上にリソースのノードを追加する. + * また, mxGraph上のセルにリソースの情報をマッピングする. + * + * @param layerNumber mxCellを追加する, PUSH/PULLのレイヤー番号 + * @return ノード(リソース)の情報と, 対応するセルのインスタンスのマップ + */ + private Map createCellsOfResourceMap(final int layerNumber) { + Map resNodeCells = new HashMap<>(); + + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell nodeLayerCell = (mxCell) root.getChildAt(NODE_LAYER); + mxCell layerCell = (mxCell) root.getChildAt(layerNumber); + + // データフローグラフ上に存在するリソースの情報をコントロールフローグラフに引き継ぐ. + 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; + } + } + + if (objNode == null) continue; + + 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); + } + } + + return resNodeCells; + } + + /** + * mxGraphの指定したレイヤー上にイベントチャンネルのノードを追加する. + * また, mxGraph上のセルにイベントチャンネルの情報をマッピングする. + * + * @param layerNumber 指定したレイヤー(現状PULLは例外扱いしている) + * @return イベントチャンネルの情報と, 対応するセルのインスタンスのマップ + */ + 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); + + 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); + } + } + } finally { + graph.getModel().endUpdate(); + } + + return eventChannelCells; + } + + /** + * コントロールフローグラフの各頂点セルの間に, エッジのセルを追加する. + * + * @param layerNumber エッジを追加したいmxGraphのレイヤー + * @param resNodeCells リソースと, その頂点セルのインスタンスのマップ + * @param eventChNodeCells イベントチャンネルと, その頂点セルのインスタンスのマップ + */ + private mxGraph insertControlFlowEdges(final int layerNumber, final Map resNodeCells, final Map eventChNodeCells) { + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layerCell = (mxCell) root.getChildAt(layerNumber); + + CallGraph callGraph = (layerNumber == PUSH_FLOW_LAYER) ? controlFlowGraph.getPushCallGraph() : controlFlowGraph.getPullCallGraph(); + + for (Edge callGraphEdge : callGraph.getEdges()) { + if (!(callGraphEdge instanceof CallEdge)) continue; + CallEdge callEdge = (CallEdge) callGraphEdge; + + // エッジがノードと接続しているか? + if (callEdge.getSource() == null || callEdge.getDestination() == null) continue; + + Node srcResNode = null; + ResourceNode dstResNode = ((StatefulObjectNode) callEdge.getDestination()).getResource(); + + mxCell srcNodeCell = null; + mxCell srcOutPortCell = null; + mxCell dstNodeCell = resNodeCells.get(dstResNode); + + if (callEdge.getSource() instanceof StatefulObjectNode) { + srcResNode = ((StatefulObjectNode) callEdge.getSource()).getResource(); + srcNodeCell = resNodeCells.get(srcResNode); + } else if (callEdge.getSource() instanceof EventChannelObjectNode) { + srcResNode = (EventChannelObjectNode) callEdge.getSource(); + srcNodeCell = eventChNodeCells.get(srcResNode); + srcOutPortCell = (mxCell) srcNodeCell.getChildAt(0); + } else continue; + + if (srcNodeCell == null || dstNodeCell == null) continue; + + 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;"); + } + + } + return graph; + } + + /** + * 状態を持たないオブジェクトをコントロールフローグラフに追加する. + * + * @param insertObjNode 追加したいオブジェクトの情報 + * @param selectedOption コントロールフローのデータ転送方式 + */ + private void addObjectNodeToCallGraph(final ObjectNode insertObjNode, final PushPullValue selectedOption) { + switch (selectedOption) { + case PUSH: { + if (controlFlowGraph.getPushCallGraph().getNodes().contains(insertObjNode)) { + return; + } + controlFlowGraph.getPushCallGraph().addNode(insertObjNode); + break; + } + case PULL: + case PUSHorPULL: + if (controlFlowGraph.getPullCallGraph().getNodes().contains(insertObjNode)) { + return; + } + controlFlowGraph.getPullCallGraph().addNode(insertObjNode); + break; + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java index 9012cfb..d857953 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java @@ -38,6 +38,9 @@ dataFlowGraph = analyzeDataTransferModel(model); showLayers(DATA_FLOW_LAYER); } + if (prevStage instanceof ControlFlowModelingStage) { + showLayers(DATA_FLOW_LAYER); + } } @Override @@ -76,6 +79,7 @@ if (prevStage instanceof DataFlowModelingStage) { return ((DataFlowModelingStage) prevStage).isValid(); } + if (prevStage instanceof ControlFlowModelingStage) return true; return false; } diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/FlowLayerWindow.java b/AlgebraicDataflowArchitectureModel/src/application/views/FlowLayerWindow.java index 2d6a769..7f62af4 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/views/FlowLayerWindow.java +++ b/AlgebraicDataflowArchitectureModel/src/application/views/FlowLayerWindow.java @@ -24,7 +24,7 @@ setTitle(title); setDefaultCloseOperation(HIDE_ON_CLOSE); - stage = Editor.STAGE_PUSH_PULL_SELECTION; + stage = Editor.STAGE_CONTROL_FLOW_DELEGATION; // Add check boxes. dataFlowCheckBox = new JCheckBox("Data-Flow", false); diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/NavigationWindow.java b/AlgebraicDataflowArchitectureModel/src/application/views/NavigationWindow.java index f21d9df..0a4c9ee 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/views/NavigationWindow.java +++ b/AlgebraicDataflowArchitectureModel/src/application/views/NavigationWindow.java @@ -4,6 +4,7 @@ import application.editor.Editor; import application.editor.IStageChangeListener; import application.editor.Stage; +import application.editor.stages.ControlFlowModelingStage; import application.editor.stages.DataFlowModelingStage; import application.editor.stages.PushPullSelectionStage; @@ -13,12 +14,12 @@ import java.awt.event.ActionListener; public class NavigationWindow extends JDialog implements IStageChangeListener { - private static final String TITLE = "Navigation"; private final Editor editor; private final JToggleButton dataFlowModelingButton; private final JToggleButton pushPullSelectionButton; + private final JToggleButton controlFlowDelegationButton; private boolean forbidReentry = false; @@ -27,10 +28,13 @@ this.editor = editor; dataFlowModelingButton = new JToggleButton("Data Flow Modeling"); pushPullSelectionButton = new JToggleButton("Push Pull Selection"); + controlFlowDelegationButton = new JToggleButton("Control Flow Modeling"); dataFlowModelingButton.addActionListener(new DataFlowModelingButtonListener()); pushPullSelectionButton.addActionListener(new PushPullSelectionButtonListener()); - pushPullSelectionButton.setEnabled(false); + controlFlowDelegationButton.addActionListener(new ControlFlowDelegationButtonListener()); dataFlowModelingButton.setSelected(true); + pushPullSelectionButton.setEnabled(false); + controlFlowDelegationButton.setEnabled(false); setTitle(TITLE); setDefaultCloseOperation(HIDE_ON_CLOSE); @@ -40,8 +44,10 @@ ButtonGroup group = new ButtonGroup(); group.add(dataFlowModelingButton); group.add(pushPullSelectionButton); + group.add(controlFlowDelegationButton); panel.add(dataFlowModelingButton); panel.add(pushPullSelectionButton); + panel.add(controlFlowDelegationButton); pack(); @@ -58,10 +64,43 @@ } if (newStage instanceof DataFlowModelingStage) { dataFlowModelingButton.setSelected(true); - pushPullSelectionButton.setEnabled(editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)); + + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } } else if (newStage instanceof PushPullSelectionStage) { pushPullSelectionButton.setSelected(true); - dataFlowModelingButton.setEnabled(editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)); + + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } + } else if (newStage instanceof ControlFlowModelingStage) { + controlFlowDelegationButton.setSelected(true); + + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } } } @@ -71,7 +110,17 @@ forbidReentry = true; editor.changeStage(Editor.STAGE_DATA_FLOW_MODELING); forbidReentry = false; - pushPullSelectionButton.setEnabled(editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)); + + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } } } @@ -81,7 +130,37 @@ forbidReentry = true; editor.changeStage(Editor.STAGE_PUSH_PULL_SELECTION); forbidReentry = false; - dataFlowModelingButton.setEnabled(editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)); + + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } + } + } + + private class ControlFlowDelegationButtonListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + forbidReentry = true; + editor.changeStage(Editor.STAGE_CONTROL_FLOW_DELEGATION); + forbidReentry = false; + + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } } } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/PopupMenuBase.java b/AlgebraicDataflowArchitectureModel/src/application/views/PopupMenuBase.java new file mode 100644 index 0000000..8bb0b1d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/PopupMenuBase.java @@ -0,0 +1,28 @@ +package application.views; + +import com.mxgraph.swing.mxGraphComponent; + +import javax.swing.*; + +public abstract class PopupMenuBase { + protected JPopupMenu popupMenu = null; + protected mxGraphComponent graphComponent = null; + + public PopupMenuBase(final mxGraphComponent graphComponent) { + this.graphComponent = graphComponent; + this.popupMenu = new JPopupMenu(); + } + + /** + * ポップアップを開く. + * + * @param x, y ポップアップを開いた座標 + */ + public void show(int x, int y) { + popupMenu.show(graphComponent, x, y); + } + + protected void addMenuItem(JMenuItem menuItem) { + popupMenu.add(menuItem); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/ControlFlowDelegationStagePopupMenu.java b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/ControlFlowDelegationStagePopupMenu.java new file mode 100644 index 0000000..14a15d6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/ControlFlowDelegationStagePopupMenu.java @@ -0,0 +1,98 @@ +package application.views.controlFlowDelegation; + +import application.actions.AbstractPopupAction; +import application.actions.ChangeCallOrderAction; +import application.actions.IntroduceMediatorObjectAction; +import application.actions.ShowDependentableMediatorAction; +import application.editor.stages.ControlFlowModelingStage; +import application.views.PopupMenuBase; +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; +import models.controlFlowModel.CallEdgeAttribute; +import models.dataFlowModel.PushPullValue; + +import javax.swing.*; +import java.awt.*; + +public class ControlFlowDelegationStagePopupMenu extends PopupMenuBase { + private ControlFlowModelingStage stage = null; + private mxCell selectedCell = null; + + public ControlFlowDelegationStagePopupMenu(final ControlFlowModelingStage stage, mxGraphComponent graphComponent) { + super(graphComponent); + this.stage = stage; + + addMenuItem(new JMenuItem(new IntroduceMediatorObjectAction(stage, graphComponent, selectedCell))); + addMenuItem(new JMenuItem(new ChangeCallOrderAction(graphComponent, selectedCell))); + addMenuItem(new JMenuItem(new ShowDependentableMediatorAction(stage, graphComponent, selectedCell))); + } + + @Override + public void show(int x, int y) { + if (graphComponent.getCellAt(x, y) instanceof mxCell) { + selectedCell = (mxCell) graphComponent.getCellAt(x, y); + } else { + selectedCell = null; + } + + if (this.selectedCell == null) return; + + notifyCellCached(selectedCell); + stage.setCellOnAnyEvent(selectedCell); + + boolean isEnable = (graphComponent.getCellAt(x, y) != null) + ? true + : false; + + setEnableMenuItems(isEnable); + + super.show(x, y); + } + + /** + * ポップアップ表示時に選択したセルを, Stageに保存させる. + * + * @param cell 選択したセル + */ + private void notifyCellCached(final mxCell cell) { + if (cell == null) return; + + for (Component component : popupMenu.getComponents()) { + JMenuItem menuItem = null; + if (component instanceof JMenuItem) menuItem = (JMenuItem) component; + else return; + + AbstractPopupAction action = null; + if (menuItem.getAction() instanceof AbstractPopupAction) { + action = (AbstractPopupAction) menuItem.getAction(); + } else return; + + action.updateTargetCell(cell); // 選択したセルをキャッシュする. + } + } + + private void setEnableMenuItems(final boolean isEnable) { + if (this.selectedCell == null) return; + + for (Component component : popupMenu.getComponents()) { + component.setEnabled(isEnable); + + // pull only + if (!isSelectedPullCallEdge(selectedCell)) { + JMenuItem menuItem = null; + if (component instanceof JMenuItem) menuItem = (JMenuItem) component; + + if (menuItem.getAction() instanceof ShowDependentableMediatorAction) { + component.setEnabled(false); + continue; + } + } + + } + } + + private boolean isSelectedPullCallEdge(final mxCell selectedCell) { + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute) selectedCell.getValue(); + return callEdgeAttr.getSelectedOption().equals(PushPullValue.PULL); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerValue.java b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerValue.java new file mode 100644 index 0000000..7008a40 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerValue.java @@ -0,0 +1,7 @@ +package application.views.controlFlowDelegation; + +public enum FlowLayerValue { + DATA_FLOW, + PUSH_FLOW, + PULL_FLOW +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerWindow.java b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerWindow.java new file mode 100644 index 0000000..98bb5e0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerWindow.java @@ -0,0 +1,98 @@ +package application.views.controlFlowDelegation; + +import application.ApplicationWindow; +import application.editor.Editor; +import application.editor.IStageChangeListener; +import application.editor.Stage; +import application.editor.stages.ControlFlowModelingStage; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * レイヤー表示 + */ +public class FlowLayerWindow extends JDialog implements IStageChangeListener { + private final String title = "Flow Layer"; + private JCheckBox dataFlowCheckBox = null; + private JCheckBox pushFlowCheckBox = null; + private JCheckBox pullFlowCheckBox = null; + + private ControlFlowModelingStage stage = null; + + public FlowLayerWindow(final ApplicationWindow owner) { + super(owner); + + setTitle(title); + setDefaultCloseOperation(HIDE_ON_CLOSE); + + stage = Editor.STAGE_CONTROL_FLOW_DELEGATION; + + // ボタンの追加 + dataFlowCheckBox = new JCheckBox("Data-Flow", false); + pushFlowCheckBox = new JCheckBox("Push-Flow", true); + pullFlowCheckBox = new JCheckBox("Pull-Flow", true); + + // 各Viewにイベントハンドラを追加 + dataFlowCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + stage.setLayerEnabled(Stage.DATA_FLOW_LAYER, dataFlowCheckBox.isSelected()); + } + }); + + pushFlowCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + stage.setLayerEnabled(Stage.PUSH_FLOW_LAYER, pushFlowCheckBox.isSelected()); + } + }); + + pullFlowCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + stage.setLayerEnabled(Stage.PULL_FLOW_LAYER, pullFlowCheckBox.isSelected()); + } + }); + + dataFlowCheckBox.setEnabled(false); + pushFlowCheckBox.setEnabled(false); + pullFlowCheckBox.setEnabled(false); + + // レイヤーのパネルレイアウトの初期化. + Container panel = getContentPane(); + panel.setLayout(new GridLayout(/*low*/3, /*col*/1)); + panel.add(dataFlowCheckBox); + panel.add(pushFlowCheckBox); + panel.add(pullFlowCheckBox); + + pack(); + setResizable(false); + } + + /** + * ステージが変わったら, レイヤー表示のボタンのアクティブ状態を切り替える. + * + * @param newStage 現在のステージ + */ + @Override + public void stageChanged(Stage newStage) { + if (newStage instanceof ControlFlowModelingStage) { + dataFlowCheckBox.setEnabled(true); + pushFlowCheckBox.setEnabled(true); + pullFlowCheckBox.setEnabled(true); + + newStage.setLayerEnabled(Stage.PUSH_FLOW_LAYER, pushFlowCheckBox.isSelected()); + newStage.setLayerEnabled(Stage.PULL_FLOW_LAYER, pullFlowCheckBox.isSelected()); + } else { + dataFlowCheckBox.setEnabled(false); + pushFlowCheckBox.setEnabled(false); + pullFlowCheckBox.setEnabled(false); + + newStage.setLayerEnabled(Stage.PUSH_FLOW_LAYER, false); + newStage.setLayerEnabled(Stage.PULL_FLOW_LAYER, false); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdgeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdgeAttribute.java new file mode 100644 index 0000000..0dd0992 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdgeAttribute.java @@ -0,0 +1,84 @@ +package models.controlFlowModel; + +import com.mxgraph.model.mxCell; +import models.EdgeAttribute; +import models.dataFlowModel.PushPullValue; + +/** + * {@link CallEdgeAttribute} represents a set of attributes for a call edge. + */ +public class CallEdgeAttribute extends EdgeAttribute { + private CallEdge callEdge = null; + private ObjectNode originalSrcObjNode = null; + private mxCell srcCell = null; + private mxCell dstCell = null; + + public CallEdgeAttribute(CallEdge callEdge, ObjectNode originalSrcObjNode, final mxCell srcCell, final mxCell dstCell) { + this.callEdge = callEdge; + this.callEdge.setAttribute(this); + + this.originalSrcObjNode = originalSrcObjNode; + + this.srcCell = srcCell; + this.dstCell = dstCell; + } + + public CallEdgeAttribute(CallEdge callEdge, final mxCell srcCell, final mxCell dstCell) { + this.callEdge = callEdge; + this.callEdge.setAttribute(this); + + this.srcCell = srcCell; + this.dstCell = dstCell; + } + + public CallEdge getCallEdge() { + return callEdge; + } + + public ObjectNode getOriginalSourceObjectNode() { + return originalSrcObjNode; + } + + public PushPullValue getSelectedOption() { + return callEdge.getSelectedOption(); + } + + public ObjectNode getSourceObjectNode() { + if (!(callEdge.getSource() instanceof ObjectNode)) + throw new ClassCastException("sourceNode isn't type of "); + return (ObjectNode) callEdge.getSource(); + } + + public ObjectNode getDestinationObjectNode() { + if (!(callEdge.getDestination() instanceof ObjectNode)) + throw new ClassCastException("destinationNode isn't type of "); + return (ObjectNode) callEdge.getDestination(); + } + + public mxCell getSourceCell() { + return srcCell; + } + + public mxCell getDestinationCell() { + return dstCell; + } + + public void setDestinationCell(final mxCell dstCell) { + this.dstCell = dstCell; + } + + /** + * Return a calling order of the call edge + */ + @Override + public String toString() { + String value = ""; + + ObjectNode srcObjNode = (ObjectNode) callEdge.getSource(); + if (srcObjNode.getOutEdges().size() >= 2) { + int order = srcObjNode.getOutEdgeCallOrder(callEdge) + 1; + value += "[" + order + "]"; + } + return value; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CompositeCallEdgeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CompositeCallEdgeAttribute.java new file mode 100644 index 0000000..f509408 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CompositeCallEdgeAttribute.java @@ -0,0 +1,24 @@ +package models.controlFlowModel; + +import java.util.ArrayList; +import java.util.List; + +/** + * 制御フローを表す2本以上のエッジを統合したときの情報
+ * e.g.) "仲介者への依存"等で2本の制御フローのエッジが1本になったときなど + */ +public class CompositeCallEdgeAttribute extends CallEdgeAttribute { + private CallEdgeAttribute currentEdgeAttr = null; + private List mergedCallEdgeAttrs = null; + + public CompositeCallEdgeAttribute(final CallEdgeAttribute callEdgeAttr) { + super(callEdgeAttr.getCallEdge(), callEdgeAttr.getOriginalSourceObjectNode(), callEdgeAttr.getSourceCell(), callEdgeAttr.getDestinationCell()); + currentEdgeAttr = (currentEdgeAttr == null) ? callEdgeAttr : null; + } + + public void mergeCallEdgeAttribute(final CallEdgeAttribute mergedCallEdgeAttr) { + if (currentEdgeAttr == null) return; + if (mergedCallEdgeAttrs == null) mergedCallEdgeAttrs = new ArrayList(); + mergedCallEdgeAttrs.add(mergedCallEdgeAttr); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowDelegator.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowDelegator.java new file mode 100644 index 0000000..b9b9424 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowDelegator.java @@ -0,0 +1,219 @@ +package models.controlFlowModel; + +import models.Edge; +import models.dataFlowModel.PushPullValue; + +import java.util.ArrayList; +import java.util.List; + +public class ControlFlowDelegator { + private ControlFlowGraph controlFlowGraph = null; + + public ControlFlowDelegator(final ControlFlowGraph controlFlowGraph) { + this.controlFlowGraph = controlFlowGraph; + } + + /** + * CFD適用可能範囲を探索する + * + * @param callEdge + * @return CFD適用可能なノードのリスト + */ + public List searchDelegatableNodes(final CallEdge callEdge) { + List nodes = new ArrayList<>(); + + // 1. adding parentNode + ObjectNode delegatingNode = (ObjectNode) callEdge.getDestination(); + ObjectNode parentNode = (ObjectNode) callEdge.getSource(); + + if (parentNode == null || delegatingNode == null) + throw new NullPointerException("parentNode is null."); + if (!(parentNode instanceof ObjectNode && delegatingNode instanceof ObjectNode)) + throw new ClassCastException("callEdge.getSource() is not ObjectNode"); + + // if the relation of "delegatingNode" to "parentNode" is 1 : 1 ? + // then return an empty list. + if (isRootNode(parentNode) && !hasChildrenNode(parentNode)) return nodes; + + // 2. collecting for each transfer method has nodes in the common area. + collectCommonTransferNodes(nodes, parentNode, delegatingNode); + + // 3. if the transfer method is PUSH-style, + // then search delegatable area. + collectParentNodesOnPushTransfer(nodes, delegatingNode, parentNode); + + return nodes; + } + + /** + * DoMが適用可能な仲介者オブジェクトの探索 + * + * @param callEdge + */ + public List searchDependentableMediatorNodes(final CallEdge callEdge) { + List nodes = new ArrayList<>(); + + // If the transfer method is PULL-style, + // then search dependable mediator object. + collectDependableObjectNodeOnPullTransfer(nodes, callEdge); + + return nodes; + } + + /** + * CFDを実行する. + * + * @param delegatingEdge 委譲するコントロールフロー + * @param dstObjNode 呼び出し元となるノード + */ + public void delegateCallEdge(CallEdge delegatingEdge, final ObjectNode dstObjNode) { + ObjectNode srcObjNode = (ObjectNode) delegatingEdge.getDestination(); + if (srcObjNode == null) throw new ClassCastException(); + + delegatingEdge.getSource().removeOutEdge(delegatingEdge); + srcObjNode.removeInEdge(delegatingEdge); + + // Reconnecting the edge to the new source object. + delegatingEdge.setDestination(srcObjNode); + delegatingEdge.setSource(dstObjNode); + + srcObjNode.addInEdge(delegatingEdge); + dstObjNode.addOutEdge(delegatingEdge); + } + + /** + * PUSH/PULLで共通するCFD適用可能なノードを再帰的に探索する + * + * @param nodes 適用可能なノードのリスト + * @param curObjNode + * @param delegatingObjNode + */ + private void collectCommonTransferNodes(List nodes, ObjectNode curObjNode, final ObjectNode delegatingObjNode) { + if (!hasChildrenNode(curObjNode)) return; + + for (Edge e : curObjNode.getOutEdges()) { + ObjectNode foundNode = (ObjectNode) e.getDestination(); + + if (foundNode.equals(delegatingObjNode)) continue; + + nodes.add(foundNode); + collectCommonTransferNodes(nodes, foundNode, delegatingObjNode); + } + } + + /** + * 順序が必要なPUSHのコントロールフローグラフ上のCFD適用可能範囲を再帰的に探索する. + * (自身の親となるノードを再帰的に探索する) + * + * @param nodes + * @param curObjNode + * @param parentDelegatingNode + */ + private void collectParentNodesOnPushTransfer(List nodes, ObjectNode curObjNode, final ObjectNode parentDelegatingNode) { + if (isRootNode(curObjNode)) return; + if (isInEdgesConversingToNode(curObjNode)) return; + + ObjectNode parentObjNode = (ObjectNode) curObjNode.getInEdge(0).getSource(); + if (parentObjNode == null) return; + + if (!parentDelegatingNode.equals(parentObjNode)) + nodes.add(parentObjNode); + + int inEdgeCallOrder = parentObjNode.getOutEdgeCallOrder(curObjNode.getInEdge(0)); + for (Edge edge : parentObjNode.getOutEdges()) { + if (!(edge instanceof CallEdge)) continue; + + int callOrder = parentObjNode.getOutEdgeCallOrder((CallEdge) edge); + if (inEdgeCallOrder < callOrder) collectChildrenNodesOnPushTransfer(nodes, (CallEdge) edge); + } + + collectParentNodesOnPushTransfer(nodes, parentObjNode, parentDelegatingNode); + } + + /** + * (あるコントロールフローについて, その呼び出し順よりも若いノードを, 呼び出し先を辿ることで再帰的に探索する) + * + * @param nodes + * @param callEdge + */ + private void collectChildrenNodesOnPushTransfer(List nodes, CallEdge callEdge) { + ObjectNode dstObjNode = (ObjectNode) callEdge.getDestination(); + if (dstObjNode == null) return; + + nodes.add(dstObjNode); + + if (!hasChildrenNode(dstObjNode)) return; + + for (Edge e : dstObjNode.getOutEdges()) { + CallEdge edge = (CallEdge) e; + if (edge == null) continue; + + ObjectNode foundNode = (ObjectNode) e.getDestination(); + if (foundNode == null) continue; + if (nodes.contains(foundNode)) continue; + + collectChildrenNodesOnPushTransfer(nodes, edge); + } + } + + /** + * PULLのコントロールフローにおいて, DoM適用可能な仲介者オブジェクトを探索する. + * + * @param nodes + * @param callEdge + */ + private void collectDependableObjectNodeOnPullTransfer(List nodes, CallEdge callEdge) { + if (callEdge.getSelectedOption() != PushPullValue.PULL) return; + + // エッジの両端が状態を持つノード(リソース)か? + StatefulObjectNode srcObjNode = (callEdge.getSource() instanceof StatefulObjectNode) + ? (StatefulObjectNode) callEdge.getSource() + : null; + + StatefulObjectNode dstObjNode = (callEdge.getDestination() instanceof StatefulObjectNode) + ? (StatefulObjectNode) callEdge.getDestination() + : null; + + if (srcObjNode == null || dstObjNode == null) return; + + // 呼び出し先が仲介者オブジェクトか? + for (Edge inEdge : dstObjNode.getInEdges()) { + CallEdge inCallEdge = (CallEdge) inEdge; + if (inCallEdge == null) continue; + + // 状態を持たないオブジェクトか?(仲介者オブジェクトか?) + ObjectNode inSrcObjNode = (inCallEdge.getSource() instanceof ObjectNode) + ? (ObjectNode) inCallEdge.getSource() + : null; + if (inSrcObjNode == null) continue; + + if (inSrcObjNode instanceof StatefulObjectNode) continue; + if (inSrcObjNode instanceof EventChannelObjectNode) continue; + + nodes.add(inSrcObjNode); + } + } + + /** + * あるノードがルートかどうか + * + * @param node + */ + private boolean isRootNode(final ObjectNode node) { + return node.getInEdges().isEmpty(); + } + + /** + * 呼び出し先があるかどうか + * + * @param node + */ + private boolean hasChildrenNode(final ObjectNode node) { + if (node.getOutEdges().size() < 1) return false; + return true; + } + + private boolean isInEdgesConversingToNode(final ObjectNode node) { + return (1 < node.getInEdges().size()); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowGraph.java index 4837cd6..1e75630 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowGraph.java @@ -71,6 +71,13 @@ } } } + + for (Node node : pushCallGraph.getNodes()) { + System.out.println(node.toString()); + } + for (Node node : pullCallGraph.getNodes()) { + System.out.println(node.toString()); + } } public DataFlowGraph getDataFlowGraph() {