package models.controlFlowModel;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import generators.CodeGenerator;
import models.Edge;
import models.Node;
import models.algebra.Expression;
import models.algebra.Term;
import models.algebra.Variable;
import models.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.DirectedGraph;
import models.dataFlowModel.*;
public class ControlFlowGraph extends DirectedGraph implements IFlowGraph {
private DataFlowGraph dataFlowGraph;
private CallGraph pushCallGraph;
private CallGraph pullCallGraph;
public ControlFlowGraph(DataFlowGraph dataFlowGraph, DataTransferModel model) {
this.dataFlowGraph = dataFlowGraph;
this.pushCallGraph = new CallGraph();
this.pullCallGraph = new CallGraph();
for (Edge resToCh: dataFlowGraph.getEdges()) {
if (!((DataFlowEdge) resToCh).isChannelToResource()) {
// A resource to channel edge
PushPullAttribute pushPull = ((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute());
ResourceNode srcResNode = (ResourceNode) resToCh.getSource();
ChannelNode directDstChNode = (ChannelNode) resToCh.getDestination();
// Should take into account the channel hierarchy.
Set<ChannelNode> ancestorDstChannels = directDstChNode.getAncestors();
Set<ChannelNode> descendantDstChannels = directDstChNode.getDescendants();
Set<Edge> outEdges = new HashSet<>();
outEdges.addAll(directDstChNode.getOutEdges());
for (ChannelNode ancestorDst: ancestorDstChannels) {
outEdges.addAll(ancestorDst.getOutEdges());
}
for (ChannelNode descendantDst: descendantDstChannels) {
outEdges.addAll(descendantDst.getOutEdges());
}
for (Edge chToRes: outEdges) {
ResourceNode dstResNode = (ResourceNode) chToRes.getDestination();
if (!CodeGenerator.generatesComponent(srcResNode.getResourceHierarchy())) {
srcResNode = srcResNode.getParent();
}
if (!CodeGenerator.generatesComponent(dstResNode.getResourceHierarchy())) {
dstResNode = dstResNode.getParent();
}
if (!srcResNode.getResourceHierarchy().equals(dstResNode.getResourceHierarchy())) {
if (pushPull.getOptions().get(0) == PushPullValue.PUSH) {
// same direction as the data flow
pushCallGraph.addEdge(srcResNode, dstResNode, PushPullValue.PUSH);
} else {
// reverse direction to the data flow
pullCallGraph.addEdge(dstResNode, srcResNode, PushPullValue.PULL);
}
}
}
}
}
for (Channel ch: model.getInputChannels()) {
DataTransferChannel evCh = (DataTransferChannel) ch;
EventChannelObjectNode srcNode = new EventChannelObjectNode(evCh);
for (ChannelMember cm: evCh.getChannelMembers()) {
if (srcNode.getName() == null) {
Expression exp = cm.getStateTransition().getMessageExpression();
if (exp instanceof Term) {
srcNode.setName(((Term) exp).getSymbol().getName());
} else if (exp instanceof Variable) {
srcNode.setName(((Variable) exp).getName());
}
}
for (ResourceNode dstResNode: dataFlowGraph.getResourceNodes(cm.getResource().getResourceHierarchy())) {
if (!CodeGenerator.generatesComponent(dstResNode.getResourceHierarchy())) {
dstResNode = dstResNode.getParent();
}
StatefulObjectNode dstNode = pushCallGraph.getStatefulObjectNode(dstResNode);
if (dstNode == null) {
pushCallGraph.addNode(dstResNode);
dstNode = pushCallGraph.getStatefulObjectNode(dstResNode);
}
// from an input event channel to a resource
pushCallGraph.insertEdge(srcNode, dstNode, PushPullValue.PUSH, 0);
}
}
}
}
public DataFlowGraph getDataFlowGraph() {
return dataFlowGraph;
}
public CallGraph getPushCallGraph() {
return pushCallGraph;
}
public CallGraph getPullCallGraph() {
return pullCallGraph;
}
@Override
public Map<Node, Set<Node>> getAllComponentNodes() {
Map<Node, Set<Node>> allNodeSets = new HashMap<>();
for (Node n: pushCallGraph.getNodes()) {
Set<Node> nodeSet = new HashSet<>();
nodeSet.add(n);
allNodeSets.put(n, nodeSet);
}
for (Node n: pullCallGraph.getNodes()) {
if (n instanceof StatefulObjectNode) {
ResourceNode resNode = ((StatefulObjectNode) n).getResource();
Set<Node> nodeSet = null;
if (pushCallGraph.getStatefulObjectNode(resNode) != null) {
// Merge a stateful object node in the push call graph and that in the pull call graph.
nodeSet = allNodeSets.get(pushCallGraph.getStatefulObjectNode(resNode));
} else {
nodeSet = new HashSet<>();
}
nodeSet.add(n);
allNodeSets.put(n, nodeSet);
} else {
Set<Node> nodeSet = new HashSet<>();
nodeSet.add(n);
allNodeSets.put(n, nodeSet);
}
}
return allNodeSets;
}
}