package graphicalrefactor.editor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.mxgraph.layout.mxCircleLayout;
import com.mxgraph.layout.mxCompactTreeLayout;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.view.mxGraph;
import algorithms.NecessityOfStoringResourceStates;
import algorithms.SelectableDataTransfers;
import algorithms.UpdateConflictCheck;
import code.ast.CompilationUnit;
import graphicalrefactor.layouts.*;
import models.Edge;
import models.Node;
import models.dataConstraintModel.ChannelGenerator;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.IdentifierTemplate;
import models.dataFlowModel.DataFlowModel;
import models.dataFlowModel.DataflowChannelGenerator;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.ResourceDependency;
import models.dataFlowModel.ResourceDependencyGraph;
import models.dataFlowModel.ResourceNode;
import parser.ExpectedAssignment;
import parser.ExpectedChannel;
import parser.ExpectedChannelName;
import parser.ExpectedEquals;
import parser.ExpectedInOrOutOrRefKeyword;
import parser.ExpectedLeftCurlyBracket;
import parser.ExpectedRHSExpression;
import parser.ExpectedRightBracket;
import parser.ExpectedStateTransition;
import parser.Parser;
import parser.WrongLHSExpression;
import parser.WrongRHSExpression;
public class Editor {
final int PORT_DIAMETER = 8;
final int PORT_RADIUS = PORT_DIAMETER / 2;
private mxGraph graph = null;
private String curFileName = null;
private DataFlowModel model = null;
private ResourceDependencyGraph resourceDependencyGraph = null;
private ArrayList<CompilationUnit> codes = null;
public Editor(mxGraph graph) {
this.graph = graph;
}
public mxGraph getGraph() {
return graph;
}
public void setGraph(mxGraph graph) {
this.graph = graph;
}
public DataFlowModel getModel() {
return model;
}
public void setModel(DataFlowModel model) {
this.model = model;
}
public ResourceDependencyGraph getResourceDependencyGraph() {
return resourceDependencyGraph;
}
public void setResourceDependencyGraph(ResourceDependencyGraph resourceDependencyGraph) {
this.resourceDependencyGraph = resourceDependencyGraph;
}
public ArrayList<CompilationUnit> getCodes() {
return codes;
}
public void setCodes(ArrayList<CompilationUnit> codes) {
this.codes = codes;
}
public String getCurFileName() {
return curFileName;
}
/**
* Open a given file, parse the file, construct a DataFlowModel and a mxGraph
* @param file given file
* @return a constructed DataFlowModel
*/
public DataFlowModel open(File file) {
try {
Parser parser = new Parser(new BufferedReader(new FileReader(file)));
try {
model = parser.doParse();
curFileName = file.getName();
if (!UpdateConflictCheck.run(model)) return null;
ResourceDependencyGraph resourceGraph = NecessityOfStoringResourceStates.doDecide(model);
resourceDependencyGraph = SelectableDataTransfers.init(resourceGraph);
graph = constructGraph(model, resourceDependencyGraph);
return model;
} catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword
| ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression
| WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* Construct a mxGraph from DataFlowModel and DataFlowModel
* @param model
* @param resourceDependencyGraph
* @return constructed mxGraph
*/
public mxGraph constructGraph(DataFlowModel model, ResourceDependencyGraph resourceDependencyGraph) {
((mxGraphModel) graph.getModel()).clear();
Object parent = graph.getDefaultParent();
graph.getModel().beginUpdate();
try {
mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER);
geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS));
geo1.setRelative(true);
mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER);
geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS));
geo2.setRelative(true);
Map<DataflowChannelGenerator, Object> channelsIn = new HashMap<>();
Map<DataflowChannelGenerator, Object> channelsOut = new HashMap<>();
Map<IdentifierTemplate, Object> resources = new HashMap<>();
Map<ResourceNode, Map<DataflowChannelGenerator, mxCell>> resourceToChannels = new HashMap<>();
Map<DataflowChannelGenerator, Set<ResourceNode>> channelToResources = new HashMap<>();
Set<DataflowChannelGenerator> refEdgeCreated = new HashSet<>();
// create channel vertices
for (Edge e : resourceDependencyGraph.getEdges()) {
if (e instanceof ResourceDependency) {
ResourceDependency dependency = (ResourceDependency) e;
DataflowChannelGenerator channelGen = dependency.getChannelGenerator();
if (channelsIn.get(channelGen) == null || channelsOut.get(channelGen) == null) {
Object channel = graph.insertVertex(parent, null, channelGen.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex
mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter");
port_in.setVertex(true);
graph.addCell(port_in, channel); // insert the input port of a channel
mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter");
port_out.setVertex(true);
graph.addCell(port_out, channel); // insert the output port of a channel
channelsIn.put(channelGen, port_in);
channelsOut.put(channelGen, port_out);
}
}
}
// create resource vertices
for (Node n : resourceDependencyGraph.getNodes()) {
if (n instanceof ResourceNode) {
ResourceNode resourceNode = (ResourceNode) n;
Object resource = graph.insertVertex(parent, null,
resourceNode.getIdentifierTemplate().getResourceName(), 20, 20, 80, 30,
"shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex
resources.put(resourceNode.getIdentifierTemplate(), resource);
}
}
// add input, output and reference edges
for (Edge e : resourceDependencyGraph.getEdges()) {
if (e instanceof ResourceDependency) {
ResourceDependency dependency = (ResourceDependency) e;
DataflowChannelGenerator channelGen = dependency.getChannelGenerator();
ResourceNode srcResource = (ResourceNode) dependency.getSource();
ResourceNode dstResource = (ResourceNode) dependency.getDestination();
// input edge
Map<DataflowChannelGenerator, mxCell> resToChannelEdges = resourceToChannels.get(srcResource);
if (resToChannelEdges == null) {
resToChannelEdges = new HashMap<>();
resourceToChannels.put(srcResource, resToChannelEdges);
}
if (resToChannelEdges.get(channelGen) == null) {
mxCell edge = (mxCell) graph.insertEdge(parent, null, dependency.getAttribute(), resources.get(srcResource.getIdentifierTemplate()), channelsIn.get(channelGen), "movable=false");
resToChannelEdges.put(channelGen, edge);
} else {
mxCell edge = resToChannelEdges.get(channelGen);
((PushPullAttribute) edge.getValue()).intersectOptions(((PushPullAttribute) dependency.getAttribute()).getOptions());
}
// output edge
Set<ResourceNode> resSet = channelToResources.get(channelGen);
if (resSet == null) {
resSet = new HashSet<>();
channelToResources.put(channelGen, resSet);
}
if (!resSet.contains(dstResource)) {
graph.insertEdge(parent, null, null, channelsOut.get(channelGen), resources.get(dstResource.getIdentifierTemplate()), "movable=false");
resSet.add(dstResource);
}
// reference edges
if (!refEdgeCreated.contains(channelGen)) {
refEdgeCreated.add(channelGen);
for (IdentifierTemplate refRes: channelGen.getReferenceIdentifierTemplates()) {
mxCell edge = (mxCell) graph.insertEdge(parent, null, null, resources.get(refRes), channelsIn.get(channelGen), "dashed=true;movable=false");
}
}
}
}
for (ChannelGenerator ioChannelGen: model.getIOChannelGenerators()) {
if (channelsOut.get(ioChannelGen) == null) {
Object channel = graph.insertVertex(parent, null, ioChannelGen.getChannelName(), 150, 20, 30, 30); // insert an I/O channel as a vertex
mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter");
port_out.setVertex(true);
graph.addCell(port_out, channel); // insert the output port of a channel
channelsOut.put((DataflowChannelGenerator) ioChannelGen, port_out);
for (IdentifierTemplate outRes: ((DataflowChannelGenerator) ioChannelGen).getOutputIdentifierTemplates()) {
graph.insertEdge(parent, null, null, port_out, resources.get(outRes), "movable=false");
}
}
}
graph.setAllowDanglingEdges(false);
graph.setCellsDisconnectable(false);
} finally {
graph.getModel().endUpdate();
}
setTreeLayout();
return graph;
}
public void setDAGLayout() {
Object parent = graph.getDefaultParent();
graph.getModel().beginUpdate();
try {
DAGLayout ctl = new DAGLayout(graph);
ctl.execute(parent);
} finally {
graph.getModel().endUpdate();
}
}
public void setTreeLayout() {
Object parent = graph.getDefaultParent();
graph.getModel().beginUpdate();
try {
mxCompactTreeLayout ctl = new mxCompactTreeLayout(graph);
ctl.setLevelDistance(100);
// ctl.setHorizontal(false);
ctl.setEdgeRouting(false);
ctl.execute(parent);
} finally {
graph.getModel().endUpdate();
}
}
public void setCircleLayout() {
Object parent = graph.getDefaultParent();
graph.getModel().beginUpdate();
try {
mxCircleLayout ctl = new mxCircleLayout(graph);
ctl.execute(parent);
} finally {
graph.getModel().endUpdate();
}
}
}