Newer
Older
AlgebraicDataflowArchitectureModel / AlgebraicDataflowArchitectureModel / src / graphicalrefactor / editor / Editor.java
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();
		}
	}
}