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

import application.editor.stages.ControlFlowModelingStage;
import application.editor.stages.DataFlowModelingStage;
import application.editor.stages.PushPullSelectionStage;
import application.layouts.DAGLayout;
import code.ast.CompilationUnit;
import com.mxgraph.layout.mxCircleLayout;
import com.mxgraph.layout.mxCompactTreeLayout;
import com.mxgraph.model.mxCell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;
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;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.ResourceNode;
import models.visualModel.FormulaChannel;
import parser.Parser;
import parser.ParserDTRAM;
import parser.exceptions.*;

import java.awt.event.MouseListener;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class Editor {
	
	protected DataTransferModel model = null;
	
	protected mxGraph graph;
	private final mxGraphComponent graphComponent;
	
	protected Stage curStage;
	private final List<IStageChangeListener> stageChangeListeners;
	
	private mxIEventListener curChangeEventListener = null;
	private MouseListener curMouseEventListener = null;
	
	protected String curFileName = null;
	protected String curFilePath = null;
	protected ArrayList<CompilationUnit> codes = null;
	
	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;
		this.graph = graphComponent.getGraph();
		
		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));
		
		curStage = STAGE_DATA_FLOW_MODELING;
		
		stageChangeListeners = new ArrayList<>();
	}
	
	public boolean canChange(Stage nextStage) {
		return nextStage.canChangeFrom(curStage);
	}
	
	public boolean changeStage(Stage nextStage) {
		if (!nextStage.canChangeFrom(curStage)) {
			return false;
		}
		nextStage.init(curStage);
		graphComponent.setCellEditor(nextStage.createCellEditor(graphComponent));
		
		// add listeners
		// "curChangeEventListener" will be called when updating the mxGraph.
		if (curChangeEventListener != null) {
			graph.getModel().removeListener(curChangeEventListener);
		}
		curChangeEventListener = nextStage.createChangeEventListener(this);
		if (curChangeEventListener != null) {
			graph.getModel().addListener(mxEvent.CHANGE, curChangeEventListener);
		}
		
		// A handler of a mouse event.
		if (curMouseEventListener != null) {
			graphComponent.getGraphControl().removeMouseListener(curMouseEventListener);
		}
		curMouseEventListener = nextStage.createMouseEventListener(this);
		if (curMouseEventListener != null) {
			graphComponent.getGraphControl().addMouseListener(curMouseEventListener);
		}
		curStage = nextStage;
		notifyStageChangeListeners();
		return true;
	}
	
	public void addStageChangeListener(IStageChangeListener stageChangeListener) {
		stageChangeListeners.add(stageChangeListener);
	}
	
	private void notifyStageChangeListeners() {
		for (IStageChangeListener l : stageChangeListeners) {
			l.stageChanged(curStage);
		}
	}
	
	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;
	}
	
	public void setCurFilePath(String curFilePath) {
		this.curFilePath = curFilePath;
		this.curFileName = new File(curFilePath).getName();
	}
	
	public void clear() {
		// Force to change to the data-flow modeling stage
		boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING);
		if (!stageChanged) {
			return;
		}
		((DataFlowModelingStage) curStage).clear();
		
		model = null;
		curFilePath = null;
		curFileName = null;
		codes = null;
	}
	
	/**
	 * Open a given file, parse the file, construct a DataFlowModel and a mxGraph
	 *
	 * @param file given file
	 */
	public void open(File file) {
		// Force to change to data-modeling stage
		boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING);
		if (!stageChanged) {
			return;
		}
		if (file == null || !file.exists()) {
			return;
		}
		// get a file's extension
		String extension = file.getName().substring(file.getName().lastIndexOf("."));
		if (extension.contains(".model")) {
			openModel(file);
		}
		if (extension.contains(".dtram")) {
			openDTRAM(file);
		}
	}
	
	private void openModel(File file) {
		try {
			Parser parser = new Parser(new BufferedReader(new FileReader(file)));
			try {
				// Parse the .model file.
				model = parser.doParse();
				
				// Update stage's model to new parsed one
				if (curStage instanceof DataFlowModelingStage) {
					((DataFlowModelingStage) curStage).setModel(model);
				}
				
				// Force to change PushPullSelectionStage to construct mxGraph
				boolean stageChanged = changeStage(STAGE_PUSH_PULL_SELECTION);
				
				// Set layout
				setDAGLayout();
				
				// Update current file info
				curFilePath = file.getAbsolutePath();
				curFileName = file.getName();
			} catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket |
					 ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals |
					 ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedRightBracket |
					 ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression |
					 ExpectedColon | ExpectedDoubleQuotation e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	private void openDTRAM(File file) {
		try {
			ParserDTRAM parser = new ParserDTRAM(new BufferedReader(new FileReader(file)));
			try {
				// Parse the .dtram file.
				model = parser.doParseModel();
				
				// Update stage's model to new parsed one
				if (curStage instanceof DataFlowModelingStage) {
					((DataFlowModelingStage) curStage).setModel(model);
				}
				
				// Force to change PushPullSelectionStage to construct mxGraph
				boolean stageChanged = changeStage(STAGE_PUSH_PULL_SELECTION);
				
				// Restore the geometry
				parser.doParseGeometry(graph);
				
				// Update current file info
				curFilePath = file.getAbsolutePath();
				curFileName = file.getName();
			} catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket |
					 ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals |
					 ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedRightBracket |
					 ExpectedAssignment | ExpectedModel | ExpectedGeometry | ExpectedNode | ExpectedResource |
					 ExpectedFormulaChannel | ExpectedIoChannel | ExpectedRightCurlyBracket | WrongPathExpression |
					 WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public void save() {
		if (curFilePath == null) {
			return;
		}
		File file = new File(curFilePath);
		// get a file's extension
		String extension = file.getName().substring(file.getName().lastIndexOf("."));
		if (extension.contains(".model")) {
			saveModel(file);
		} else if (extension.contains(".dtram")) {
			saveDTRAM(file);
		}
	}
	
	private void saveModel(File file) {
		try {
			FileWriter filewriter = new FileWriter(file);
			filewriter.write(model.getSourceText());
			filewriter.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void saveDTRAM(File file) {
		try {
			FileWriter filewriter = new FileWriter(file);
			filewriter.write(toOutputString());
			filewriter.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * get writing texts "dtram" file  information is written.
	 *
	 * @return formatted "dtram" info texts.
	 */
	private String toOutputString() {
		StringBuilder fileString = new StringBuilder();
		
		fileString.append("model {\n");
		fileString.append(model.getSourceText());
		fileString.append("}\n");
		
		fileString.append("geometry {\n");
		
		Object root = graph.getDefaultParent();
		mxCell dataFlowLayer = (mxCell) ((mxCell) root).getChildAt(Stage.DATA_FLOW_LAYER);
		
		for (int i = 0; i < graph.getModel().getChildCount(dataFlowLayer); i++) {
			Object cell = graph.getModel().getChildAt(dataFlowLayer, 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 (ResourcePath res : model.getResourcePaths()) {
					if (res != null && state.getLabel().equals(res.getLeafResourceName()))
						fileString.append("\tnode r ").append(state.getLabel()).append(":").append(x).append(",").append(y).append(",").append(w).append(",").append(h).append("\n");
				}
				
				for (Channel ioC : model.getInputChannels()) {
					if (ioC != null && state.getLabel().equals(ioC.getChannelName())) {
						fileString.append("\tnode ioc ").append(state.getLabel()).append(":").append(x).append(",").append(y).append(",").append(w).append(",").append(h).append("\n");
					}
				}
				for (Channel ch : model.getChannels()) {
					if (ch instanceof FormulaChannel && state.getLabel().equals(ch.getChannelName())) {
						fileString.append("\tnode fc ").append(state.getLabel()).append(":").append(x).append(",").append(y).append(",").append(w).append(",").append(h).append("\n");
					} else if (ch != null && state.getLabel().equals(ch.getChannelName())) {
						fileString.append("\tnode c ").append(state.getLabel()).append(":").append(x).append(",").append(y).append(",").append(w).append(",").append(h).append("\n");
					}
				}
			}
		}
		fileString.append("}\n");
		return fileString.toString();
	}
	
	public void setDAGLayout() {
		mxCell root = (mxCell) graph.getDefaultParent();
		mxCell dataFlowLayer = (mxCell) root.getChildAt(Stage.DATA_FLOW_LAYER);
		graph.getModel().beginUpdate();
		try {
			DAGLayout ctl = new DAGLayout(graph);
			ctl.execute(dataFlowLayer);
//            for(int i = 0; i < dataFlowLayer.getChildCount(); i++){
//                ctl.execute(dataFlowLayer.getChildAt(i));
//            }
		} finally {
			graph.getModel().endUpdate();
		}
	}
	
	public void setTreeLayout() {
		mxCell root = (mxCell) graph.getDefaultParent();
		graph.getModel().beginUpdate();
		try {
			mxCompactTreeLayout ctl = new mxCompactTreeLayout(graph);
			ctl.setLevelDistance(100);
			//		ctl.setHorizontal(false);
			ctl.setEdgeRouting(false);
			for (int i = 0; i < root.getChildCount(); i++) {
				ctl.execute(root.getChildAt(i));
			}
		} finally {
			graph.getModel().endUpdate();
		}
	}
	
	public void setCircleLayout() {
		mxCell root = (mxCell) graph.getDefaultParent();
		graph.getModel().beginUpdate();
		try {
			mxCircleLayout ctl = new mxCircleLayout(graph);
			for (int i = 0; i < root.getChildCount(); i++) {
				ctl.execute(root.getChildAt(i));
			}
		} finally {
			graph.getModel().endUpdate();
		}
	}
	
	public List<ResourceNode> getSelectedResourceNodes() {
		Object[] sels = graph.getSelectionCells();
		List<ResourceNode> resNodes = new ArrayList<>();
		if (curStage instanceof DataFlowModelingStage) {
			for (Object sel : sels) {
				if (sel instanceof mxCell && ((mxCell) sel).isVertex()) {
					mxCell cell = ((mxCell) sel);
					ResourceNode resNode = ((DataFlowModelingStage) curStage).getResourceNode(cell);
					if (resNode != null) {
						resNodes.add(resNode);
					}
				}
			}
		}
		return resNodes;
	}
	
	public List<Channel> getSelectedChannels() {
		Object[] sels = graph.getSelectionCells();
		List<Channel> channels = new ArrayList<>();
		for (Object sel : sels) {
			if (sel instanceof mxCell && ((mxCell) sel).isVertex()) {
				mxCell cell = ((mxCell) sel);
				Channel channel = model.getChannel((String) cell.getValue());
				if (channel != null) {
					channels.add(channel);
				} else {
					channel = model.getInputChannel((String) cell.getValue());
					if (channel != null) {
						channels.add(channel);
					}
				}
			}
		}
		return channels;
	}
	
	public void delete() {
		boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING);
		if (!stageChanged) {
			return;
		}
		((DataFlowModelingStage) curStage).delete();
	}
	
	public void addResourceNode(ResourceNode parentNode, String resName) {
		// Force to change to data-flow modeling stage
		boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING);
		if (!stageChanged) {
			return;
		}
		((DataFlowModelingStage) curStage).addResourceNode(parentNode, resName);
		model = curStage.getModel();
	}
	
	public void addChannel(String channelName) {
		// Force to change to data-flow modeling stage
		boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING);
		if (!stageChanged) {
			return;
		}
		DataTransferChannel channel = null;
		if (channelName.contains(Parser.LEFT_BRACKET) && channelName.contains(Parser.RIGHT_BRACKET)) {
			channel = new DataTransferChannel(channelName.substring(0, channelName.indexOf(Parser.LEFT_BRACKET)));
			Parser.TokenStream stream = new Parser.TokenStream();
			Parser parser = new Parser(stream);
			stream.addLine(channelName.substring(channelName.indexOf(Parser.LEFT_BRACKET), channelName.length()));
			try {
				String leftBracket = stream.next();
				if (leftBracket.equals(Parser.LEFT_BRACKET)) {
					// has selectors
					String rightBracket = null;
					do {
						String selector = stream.next();
						Variable var = parser.parseVariable(stream, model, selector);
						channel.addSelector(var);
						rightBracket = stream.next();
					} while (rightBracket.equals(Parser.COMMA));
					if (!rightBracket.equals(Parser.RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine());
					leftBracket = stream.next();
				}
			} catch (ExpectedRightBracket e) {
				e.printStackTrace();
			}
		} else {
			channel = new DataTransferChannel(channelName);
		}
		((DataFlowModelingStage) curStage).addChannel(channel);
		model = curStage.getModel();
	}
	
	public void addEventChannel(String channelName) {
		// Force to change to data-flow modeling stage
		boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING);
		if (!stageChanged) {
			return;
		}
		DataTransferChannel eventChannel = null;
		if (channelName.contains(Parser.LEFT_BRACKET) && channelName.contains(Parser.RIGHT_BRACKET)) {
			eventChannel = new DataTransferChannel(channelName.substring(0, channelName.indexOf(Parser.LEFT_BRACKET)));
			Parser.TokenStream stream = new Parser.TokenStream();
			Parser parser = new Parser(stream);
			stream.addLine(channelName.substring(channelName.indexOf(Parser.LEFT_BRACKET), channelName.length()));
			try {
				String leftBracket = stream.next();
				if (leftBracket.equals(Parser.LEFT_BRACKET)) {
					// has selectors
					String rightBracket = null;
					do {
						String selector = stream.next();
						Variable var = parser.parseVariable(stream, model, selector);
						eventChannel.addSelector(var);
						rightBracket = stream.next();
					} while (rightBracket.equals(Parser.COMMA));
					if (!rightBracket.equals(Parser.RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine());
					leftBracket = stream.next();
				}
			} catch (ExpectedRightBracket e) {
				e.printStackTrace();
			}
		} else {
			eventChannel = new DataTransferChannel(channelName);
		}
		((DataFlowModelingStage) curStage).addEventChannel(eventChannel);
		model = curStage.getModel();
	}
	
	public void addFormulaChannel(String channelName, Symbol op) {
		// Force to change to data-flow modeling stage
		boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING);
		if (!stageChanged) {
			return;
		}
		FormulaChannel formulaChannel = new FormulaChannel(channelName, op);
		((DataFlowModelingStage) curStage).addFormulaChannel(formulaChannel);
		model = curStage.getModel();
	}
	
	public boolean connectEdge(mxCell edge, mxCell src, mxCell dst) {
		boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING);
		if (!stageChanged) {
			return false;
		}
		return ((DataFlowModelingStage) curStage).connectEdge(edge, src, dst);
	}
	
	public mxGraph getGraph() {
		return graph;
	}
	
	public mxGraphComponent getGraphComponent() {
		return graphComponent;
	}
	
	public DataTransferModel getModel() {
		model = curStage.getModel();
		return model;
	}
	
	public Stage getCurStage() {
		return curStage;
	}
	
	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 static class SrcDstAttribute extends EdgeAttribute {
		private final Object src;
		private final 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 "";
		}
	}
}