Newer
Older
AlgebraicDataflowArchitectureModel / AlgebraicDataflowArchitectureModel / src / application / editor / Editor.java
package application.editor;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.AbstractMap;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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.mxICell;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.view.mxCellState;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;

import algorithms.DataTransferModelAnalyzer;
import algorithms.Validation;
import application.layouts.*;
import code.ast.CompilationUnit;
import generators.JavaCodeGenerator;
import models.Edge;
import models.EdgeAttribute;
import models.Node;
import models.algebra.Expression;
import models.algebra.InvalidMessage;
import models.algebra.ParameterizedIdentifierIsFutureWork;
import models.algebra.UnificationFailed;
import models.algebra.ValueUndefined;
import models.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
import models.dataConstraintModel.ResourceHierarchy;
import models.dataConstraintModel.ResourcePath;
import models.dataConstraintModel.Selector;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork;
import models.dataFlowModel.ChannelNode;
import models.dataFlowModel.DataFlowEdge;
import models.dataFlowModel.DataFlowGraph;
import models.dataFlowModel.ResourceNode;
import models.visualModel.FormulaChannel;
import parser.Parser;
import parser.Parser.TokenStream;
import parser.exceptions.ExpectedAssignment;
import parser.exceptions.ExpectedChannel;
import parser.exceptions.ExpectedChannelName;
import parser.exceptions.ExpectedColon;
import parser.exceptions.ExpectedEquals;
import parser.exceptions.ExpectedFormulaChannel;
import parser.exceptions.ExpectedGeometry;
import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword;
import parser.exceptions.ExpectedIoChannel;
import parser.exceptions.ExpectedLeftCurlyBracket;
import parser.exceptions.ExpectedModel;
import parser.exceptions.ExpectedNode;
import parser.exceptions.ExpectedRHSExpression;
import parser.exceptions.ExpectedResource;
import parser.exceptions.ExpectedRightBracket;
import parser.exceptions.ExpectedStateTransition;
import parser.exceptions.WrongJsonExpression;
import parser.exceptions.WrongLHSExpression;
import parser.exceptions.WrongRHSExpression;
import parser.exceptions.ExpectedRightCurlyBracket;
import parser.exceptions.WrongPathExpression;
import parser.ParserDTRAM;

public class Editor {
	final int PORT_DIAMETER = 8;
	final int PORT_RADIUS = PORT_DIAMETER / 2;

	protected DataTransferModel model = null;
	protected mxGraph graph = null;
	private mxGraphComponent  graphComponent = null;

	protected DataFlowGraph dataFlowGraph = null;

	protected String curFileName = null;
	protected String curFilePath = null;
	protected ArrayList<CompilationUnit> codes = null;
	
	private boolean bReflectingArchitectureModel = false;

	public Editor(mxGraphComponent graphComponent) {
		this.graphComponent = graphComponent;
		this.graph = graphComponent.getGraph();
		
		graphComponent.setCellEditor(new DataTransferModelingCellEditor(graphComponent, this));
	}

	public mxGraph getGraph() {
		return graph;
	}

	public mxGraphComponent getGraphComponent() {
		return this.graphComponent;
	}

	public DataTransferModel getModel() {
		if (model == null) {
			model = new DataTransferModel();
		}
		return model;
	}

	public DataFlowGraph getDataFlowGraph() {
		if (dataFlowGraph == null) {
			analyzeDataTransferModel(getModel());
			updateEdgeAttiributes(dataFlowGraph);
		}
		return dataFlowGraph;
	}

	public DataFlowGraph analyzeDataTransferModel(DataTransferModel model) {
		DataFlowGraph flowGraph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model);
		dataFlowGraph = DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(flowGraph);
		return dataFlowGraph;
	}

	public void resetDataFlowGraph() {
		dataFlowGraph = null;
	}

	public void setDataFlowGraph(DataFlowGraph dataFlowGraph) {
		this.dataFlowGraph = dataFlowGraph;
	}

	public ArrayList<CompilationUnit> getCodes() {
		return codes;
	}

	public void setCodes(ArrayList<CompilationUnit> codes) {
		this.codes = codes;
	}

	public String getCurFileName() {
		return curFileName;
	}

	public String getCurFilePath() {
		return curFilePath;
	}

	public void setCurFilePath(String curFilePath) {
		this.curFilePath = curFilePath;
		this.curFileName = new File(curFilePath).getName();
	}

	public void clear() {
		model = null;
		((mxGraphModel) graph.getModel()).clear();
		dataFlowGraph = null;
		curFilePath = null;
		curFileName = null;
		codes = null;
	}

	/**
	 * Open a given file, parse the file, construct a DataFlowModel and a mxGraph
	 * @param file given file
	 * @return a constructed DataFlowModel
	 */
	public DataTransferModel open(File file) {
		try {

			String extension ="";
			if(file != null && file.exists()) {
				// get a file's name
				String name = file.getName();

				// get a file's extension
				extension = name.substring(name.lastIndexOf("."));
			}
			if(extension.contains(".model")) {
				openModel(file);
			} else {
				ParserDTRAM parserDTRAM = new ParserDTRAM(new BufferedReader(new FileReader(file)));
				try {	
					// Parse the .dtram file.
					model = parserDTRAM.doParseModel();
					
					// Analyze the model.
					if (!Validation.checkUpdateConflict(model)) return null;
					DataFlowGraph dataFlowGraph = analyzeDataTransferModel(model);
					
					// Visualize the model.
					graph = constructGraph(model, dataFlowGraph);
					parserDTRAM.doParseGeometry(graph);
					updateEdgeAttiributes(dataFlowGraph);
					
					curFilePath = file.getAbsolutePath();
					curFileName = file.getName();
					return model;
				} catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword
						| ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression
						| WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedModel 
						| ExpectedGeometry | ExpectedNode | ExpectedResource | ExpectedFormulaChannel | ExpectedIoChannel 
						| ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) {
					e.printStackTrace();
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		return null;
	}

	public DataTransferModel openModel(File file) {
		try {

			Parser parser = new Parser(new BufferedReader(new FileReader(file)));

			try {	
				// Parse the .model file.
				model = parser.doParse();
				
				// Analyze the model.
				if (!Validation.checkUpdateConflict(model)) return null;
				DataFlowGraph dataFlowGraph = analyzeDataTransferModel(model);
				
				// Visualize the model.
				graph = constructGraph(model, dataFlowGraph);
				updateEdgeAttiributes(dataFlowGraph);
				
				// Set DAG layout.
//				setDAGLayout();
				
				curFilePath = file.getAbsolutePath();
				curFileName = file.getName();
				return model;
			} catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword
					| ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression
					| WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**--------------------------------------------------------------------------------
	 * save
	/**--------------------------------------------------------------------------------
	 * 
	 */
	public void save() {
		if (curFilePath != null) {
			try {
				File file = new File(curFilePath);
				String extension = "";
				if(file != null && file.exists()) {
					// get a file's name
					String name = file.getName();

					// get a file's extension
					extension = name.substring(name.lastIndexOf("."));
				}
				if(extension.contains(".model")) {
					saveModel(file);
				} else {
					FileWriter filewriter = new FileWriter(file);		        
					filewriter.write(toOutputString());
					filewriter.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public void saveModel(File file) {
		if (curFilePath != null) {
			try {
				FileWriter filewriter = new FileWriter(file);			     
				filewriter.write(model.getSourceText());
				filewriter.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**--------------------------------------------------------------------------------
	 * get writing texts "dtram" file  information is written.
	 * 
	 * @return formatted "dtram" info texts.
	 */
	protected String toOutputString() {
		String fileString = "";
		
		fileString += "model {\n";
		fileString += this.model.getSourceText();
		fileString += "}\n";

		fileString += "geometry {\n";

		Object root = graph.getDefaultParent();
		for (int i = 0; i < graph.getModel().getChildCount(root); i++) {
			Object cell = graph.getModel().getChildAt(root, i);
			if (graph.getModel().isVertex(cell)) {
				mxGraphView view = graph.getView();
				mxCellState state = view.getState(cell);
				int x = (int) state.getX();
				int y = (int) state.getY();
				int w = (int) state.getWidth();
				int h = (int) state.getHeight();

				for(Channel ch: model.getChannels()) {
					if(ch instanceof FormulaChannel && state.getLabel().equals(ch.getChannelName())) {
						fileString += "\tnode fc " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h+"\n";		
					} else if(ch instanceof Channel && state.getLabel().equals(ch.getChannelName())) {
						fileString +="\tnode c " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h+"\n";
					}
				}

				for (ResourcePath res: model.getResourcePaths()){
					if(res instanceof ResourcePath && state.getLabel().equals(res.getResourceName()))
						fileString += "\tnode r " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n";
				}

				for (Channel ioC: model.getIOChannels()) {
					if(ioC instanceof Channel && state.getLabel().equals(ioC.getChannelName())) {
						fileString += "\tnode ioc " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n";
					}
				}
			}
		}		
		fileString += "}\n";
	
		return fileString;
	}
	
	/**
	 * Construct a mxGraph from DataFlowModel and DataFlowModel
	 * @param model
	 * @param dataFlowGraph 
	 * @return constructed mxGraph
	 */
	public mxGraph constructGraph(DataTransferModel model, DataFlowGraph dataFlowGraph) {
		bReflectingArchitectureModel = true;
		((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<DataTransferChannel, Object> channelsIn = new HashMap<>();
			Map<DataTransferChannel, Object> channelsOut = new HashMap<>();
			Map<Integer, Object> resources = new HashMap<>();

			// create channel vertices
			for (ChannelNode c: dataFlowGraph.getRootChannelNodes()) {
				DataTransferChannel channel = (DataTransferChannel) c.getChannel();
				if (channel.getInputResources().size() > 0) {
					// Normal channel
					if (channelsIn.get(channel) == null || channelsOut.get(channel) == null) {
						if(channel.getSelectors().toString() == "[]") {
							Object chCell = graph.insertVertex(parent, null, channel.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, chCell);		// 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, chCell);		// insert the output port of a channel
							channelsIn.put(channel, port_in);
							channelsOut.put(channel, port_out);
						}else {

							for (Selector s: channel.getSelectors()) {
								Expression exp = s.getExpression();
								String selName = exp.toString();	
								String cName = channel.getChannelName();
								String channelName = cName + " (" + selName + ")";
								Object chCell = graph.insertVertex(parent, null, channelName, 150, 20, 60, 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, chCell);		// 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, chCell);		// insert the output port of a channel
								channelsIn.put(channel, port_in);
								channelsOut.put(channel, port_out);
							}
						}
					}
				} else {
					// I/O channel
					if (channelsOut.get(channel) == null) {
						if(channel.getSelectors().toString() == "[]") {
							Object chCell = graph.insertVertex(parent, null, channel.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, chCell);		// 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, chCell);		// insert the output port of a channel
							channelsIn.put(channel, port_in);
							channelsOut.put(channel, port_out);
						}else {
							
							for (Selector s: channel.getSelectors()) {
								Expression exp = s.getExpression();
								String selName = exp.toString();
								String cName = channel.getChannelName();
								String channelName = cName + " (" + selName + ")";
								Object chCell = graph.insertVertex(parent, null, channelName, 150, 20, 60, 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, chCell);		// 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, chCell);		// insert the output port of a channel
								channelsIn.put(channel, port_in);
								channelsOut.put(channel, port_out);
							}
						}
					}
				}
			}

			// create resource vertices
			for (ResourceNode resNode: dataFlowGraph.getRootResourceNodes()) {
				int w = 80;
				int h = 30;
				ResourcePath res = resNode.getOutSideResource();
				Object resource = graph.insertVertex(parent, null,
						res.getResourceName(), 20, 20, w, h,
						"shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex
				resources.put(System.identityHashCode(res), resource);
				getChildResource(resource, resNode, resources, w, h);
			}
			
			// add input, output and reference edges
			for (Edge edge: dataFlowGraph.getEdges()) {
				DataFlowEdge dfEdge = (DataFlowEdge) edge;
				if (dfEdge.isChannelToResource()) {
					// output edge
					DataTransferChannel channel = ((ChannelNode) dfEdge.getSource()).getChannel();
					ResourcePath dstRes = ((ResourceNode) dfEdge.getDestination()).getOutSideResource();
					graph.insertEdge(parent, null, new SrcDstAttribute(channel, dstRes), channelsOut.get(channel), resources.get(System.identityHashCode(dstRes)), "movable=false");
				} else {
					// input edge
					ResourcePath srcRes = ((ResourceNode) dfEdge.getSource()).getOutSideResource();
					DataTransferChannel channel = ((ChannelNode) dfEdge.getDestination()).getChannel();	
					Set<Map.Entry<ResourceNode,ResourceNode>> toRes = getResourceDependencyForChannel(channel, model, dataFlowGraph);
					for(Map.Entry<ResourceNode, ResourceNode> RtoR :  toRes) {
						graph.insertEdge(parent, null, null,
								resources.get(System.identityHashCode(RtoR.getValue().getOutSideResource())), 
								resources.get(System.identityHashCode(RtoR.getKey().getOutSideResource())), "dashed=true;movable=false");
					}
					
					graph.insertEdge(parent, null, new SrcDstAttribute(srcRes, channel), resources.get(System.identityHashCode(srcRes)), channelsIn.get(channel), "movable=false");
				}
			}
			
			for (Channel ch: model.getChannels()) {
				// reference edges
				DataTransferChannel channel = (DataTransferChannel) ch;
				for (ResourcePath refRes: channel.getReferenceResources()) {
					graph.insertEdge(parent, null, null, resources.get(System.identityHashCode(refRes)), channelsIn.get(channel), "dashed=true;movable=false");
				}
			}
		} finally {
			graph.getModel().endUpdate();
		}
		setDAGLayout();
		
		bReflectingArchitectureModel = false;
		return graph;
	}

	public void getChildResource(Object resource, ResourceNode resNode, Map<Integer, Object> resources, int w, int h) {
		
		for (ResourceNode c: resNode.getChildren()) {
//			w = w - 100;
//			if(w <= 0) {
//				w = 100;
//			}
//			h = (2 * w) / 3;
			ResourcePath chRes = c.getOutSideResource();
			Object chResource = graph.insertVertex(resource, null,
					chRes.getName(), 0, 0, w, h,
					"shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex
			resources.put(System.identityHashCode(chRes), chResource);
			getChildResource(chResource, c, resources, w, h);
		}
	}
	
	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 updateEdgeAttiributes(DataFlowGraph dataFlowGraph) {
		Object parent = graph.getDefaultParent();
		graph.getModel().beginUpdate();
		try {
			// add input, output and reference edges
			for (Edge e : dataFlowGraph.getEdges()) {
				if (e instanceof DataFlowEdge) {
					DataFlowEdge dataFlow = (DataFlowEdge) e;
					if (!dataFlow.isChannelToResource()) {
						ResourceNode srcRes = (ResourceNode) dataFlow.getSource();
						DataTransferChannel channel = ((ChannelNode) dataFlow.getDestination()).getChannel();
						// input edge
						for (Object edge: graph.getChildEdges(parent)) {
							mxCell edgeCell = (mxCell) edge;
							if (edgeCell.getValue() instanceof SrcDstAttribute) {
								SrcDstAttribute edgeAttr = (SrcDstAttribute) edgeCell.getValue();
								if (srcRes.getOutSideResource().equals(edgeAttr.getSource())  && channel.equals(edgeAttr.getDestination())) {
									edgeCell.setValue(dataFlow.getAttribute());
									break;
								}
							}
						}
					}
				}
			}
		} finally {
			graph.getModel().endUpdate();
		}
		graph.refresh();
	}

	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();
		}
	}

	public ResourcePath addResourcePath(ResourcePath parentPath, String resName) {
		ResourcePath resourcePath = null;
		if (parentPath == null) {
			resourcePath = new ResourcePath(resName);
			getModel().addResourcePath(resourcePath);
		} else {
			if (resName.startsWith(Parser.LEFT_CURLY_BRACKET) && resName.endsWith(Parser.RIGHT_CURLY_BRACKET)) {
				TokenStream stream = new Parser.TokenStream();
				Parser parser = new Parser(stream);
				stream.addLine(resName.substring(1, resName.length() - 1));
				try {
					Expression exp = parser.parseTerm(stream, getModel());
					resourcePath = new ResourcePath(parentPath, exp);
					getModel().addResourcePath(resourcePath);
				} catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon e) {
					e.printStackTrace();
					return null;
				}
			} else {
				resourcePath = new ResourcePath(parentPath, resName);
				getModel().addResourcePath(resourcePath);
			}
		}
		resetDataFlowGraph();
		graph.getModel().beginUpdate();
		Object parent = graph.getDefaultParent();
		try {
			graph.insertVertex(parent, null, resName, 20, 20, 80, 30,
					"shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex
		} finally {
			graph.getModel().endUpdate();
		}
		return resourcePath;
	}

	public void addChannel(DataTransferChannel channelGen) {
		getModel().addChannel(channelGen);
		resetDataFlowGraph();
		graph.getModel().beginUpdate();
		Object parent = graph.getDefaultParent();
		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);

			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
		} finally {
			graph.getModel().endUpdate();
		}
	}

	public void addIOChannel(DataTransferChannel ioChannelGen) {
		getModel().addIOChannel(ioChannelGen);
		resetDataFlowGraph();
		graph.getModel().beginUpdate();
		Object parent = graph.getDefaultParent();
		try {
			mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER);
			geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS));
			geo2.setRelative(true);

			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
		} finally {
			graph.getModel().endUpdate();
		}
	}

	public void addFormulaChannel(FormulaChannel formulaChannelGen) {
		getModel().addChannel(formulaChannelGen);
		resetDataFlowGraph();
		graph.getModel().beginUpdate();
		Object parent = graph.getDefaultParent();
		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);

			Object channel = graph.insertVertex(parent, null, formulaChannelGen.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
		} finally {
			graph.getModel().endUpdate();
		}
	}

	public boolean connectEdge(mxCell edge, mxCell src, mxCell dst) {
		if (bReflectingArchitectureModel) return false;
		DataTransferModel model = getModel();
		Channel srcCh = model.getChannel((String) src.getValue());
		if (srcCh == null) {
			srcCh = model.getIOChannel((String) src.getValue());
			if (srcCh == null) {
				ResourcePath srcRes = model.getResourcePath((String) src.getValue());
				Channel dstCh = model.getChannel((String) dst.getValue());
				if (srcRes == null || dstCh == null) return false;
				// resource to channel edge
				ChannelMember srcCm = new ChannelMember(srcRes);
				((DataTransferChannel ) dstCh).addChannelMemberAsInput(srcCm);
				edge.setValue(new SrcDstAttribute(srcRes, dstCh));
				resetDataFlowGraph();
				return true;
			}
		}
		ResourcePath dstRes = model.getResourcePath((String) dst.getValue());
		if (dstRes == null) return false;
		// channel to resource edge
		ChannelMember dstCm = new ChannelMember(dstRes);
		((DataTransferChannel) srcCh).addChannelMemberAsOutput(dstCm);
		edge.setValue(new SrcDstAttribute(srcCh, dstRes));
		resetDataFlowGraph();
		return true;
	}

	public void delete() {
		for (Object obj: graph.getSelectionCells()) {
			mxCell cell = (mxCell) obj;
			if (cell.isEdge()) {
				String srcName = (String) cell.getSource().getValue();
				String dstName = (String) cell.getTarget().getValue();
				if (model.getResourcePath(srcName) != null) {
					// resource to channel edge
					Channel ch = model.getChannel(dstName);
					ch.removeChannelMember(model.getResourcePath(srcName));
				} else if (model.getResourcePath(dstName) != null) {
					// channel to resource edge
					Channel ch = model.getChannel(srcName);
					if (ch == null) {
						ch = model.getIOChannel(srcName);
					}
					ch.removeChannelMember(model.getResourcePath(dstName));
				}
			} else if (cell.isVertex()) {
				String name = (String) cell.getValue();
				if (model.getChannel(name) != null) {
					model.removeChannel(name);
				} else if (model.getIOChannel(name) != null) {
					model.removeIOChannel(name);
				} else if (model.getResourcePath(name) != null) {
					model.removeResourcePath(name);
				}
			}
		}
		graph.removeCells(graph.getSelectionCells());
		resetDataFlowGraph();
	}

	public void setChannelCode(DataTransferChannel ch, String code) {
		ch.setSourceText(code);
		TokenStream stream = new Parser.TokenStream();
		Parser parser = new Parser(stream);
		
		for (String line: code.split("\n")) {
			stream.addLine(line);
		}
		try {
			DataTransferChannel ch2 = parser.parseChannel(getModel());
			for (ChannelMember chm2: ch2.getInputChannelMembers()) {
				for (ChannelMember chm: ch.getInputChannelMembers()) {
					if (chm2.getResource() == chm.getResource()) {
						chm.setStateTransition(chm2.getStateTransition());
						break;
					}
				}
			}
			for (ChannelMember chm2: ch2.getOutputChannelMembers()) {
				for (ChannelMember chm: ch.getOutputChannelMembers()) {
					if (chm2.getResource() == chm.getResource()) {
						chm.setStateTransition(chm2.getStateTransition());
						break;
					}
				}
			}
			for (ChannelMember chm2: ch2.getReferenceChannelMembers()) {
				for (ChannelMember chm: ch.getReferenceChannelMembers()) {
					if (chm2.getResource() == chm.getResource()) {
						chm.setStateTransition(chm2.getStateTransition());
						break;
					}
				}
			}
			resetDataFlowGraph();
		} catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket
				| ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression
				| WrongLHSExpression | WrongRHSExpression | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) {
			e.printStackTrace();
		}
	}
	
	public Set<Map.Entry<ResourceNode, ResourceNode>> getResourceDependencyForChannel(DataTransferChannel ch, DataTransferModel model, DataFlowGraph dataFlowGraph) {
		Set<Map.Entry<ResourceNode, ResourceNode>> resourceDpendency = new HashSet<>();
		try {
			Map<ChannelMember, Map.Entry<ResourcePath, Set<ChannelMember>>> dependency = ch.fillOutsideResourcePaths(ch.getOutputChannelMembers().iterator().next(), JavaCodeGenerator.pullAccessor);
			for (ChannelMember srcMem: dependency.keySet()) {
				ResourceNode srcNode = dataFlowGraph.getResourceNode(srcMem.getResource());
				if (srcNode != null) {
					for (ChannelMember dstMem: dependency.get(srcMem).getValue()) {
						ResourceNode dstNode = dataFlowGraph.getResourceNode(dstMem.getResource());
						resourceDpendency.add(new AbstractMap.SimpleEntry<>(srcNode, dstNode));
					}
				}
			}
		} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage
				| UnificationFailed | ValueUndefined e) {
			e.printStackTrace();
		}
		return resourceDpendency;
	}

	private class SrcDstAttribute extends EdgeAttribute {
		private Object src;
		private Object dst;

		public SrcDstAttribute(Object src, Object dst) {
			this.src = src;
			this.dst = dst;
		}

		public Object getSource() {
			return src;
		}

		public Object getDestination() {
			return dst;
		}

		public String toString() {
			return "";
		}
	}
}