Newer
Older
AlgebraicDataflowArchitectureModel / AlgebraicDataflowArchitectureModel / src / graphicalrefactor / editor / Editor.java
okinakina on 17 Apr 2023 22 KB in progress
package graphicalrefactor.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.ArrayList;
import java.util.Collection;
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.view.mxCellState;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;

import algorithms.NecessityOfStoringResourceStates;
import algorithms.SelectableDataTransfers;
import algorithms.UpdateConflictCheck;
import code.ast.CompilationUnit;
import graphicalrefactor.layouts.*;
import models.Edge;
import models.EdgeAttribute;
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 models.visualModel.FormulaChannelGenerator;
import parser.Parser;
import parser.Parser.TokenStream;
import parser.exceptions.ExpectedAssignment;
import parser.exceptions.ExpectedChannel;
import parser.exceptions.ExpectedChannelName;
import parser.exceptions.ExpectedEquals;
import parser.exceptions.ExpectedFormulaChannel;
import parser.exceptions.ExpectedGeometry;
import parser.exceptions.ExpectedInOrOutOrRefKeyword;
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.WrongLHSExpression;
import parser.exceptions.WrongRHSExpression;
import parser.ParserDTRAM;

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

	private String curFileName = null;
	private String curFilePath = null;
	private ArrayList<CompilationUnit> codes = null;
	
	protected DataFlowModel model = null;
	protected mxGraph graph = null;

	protected ResourceDependencyGraph resourceDependencyGraph = null;

	
	public Editor(mxGraph graph) {
		this.graph = graph;
	}

	public mxGraph getGraph() {
		return graph;
	}

	public void setGraph(mxGraph graph) {
		this.graph = graph;
	}

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

	public ResourceDependencyGraph getResourceDependencyGraph() {
		if (resourceDependencyGraph == null) {
			updateResourceDependencyGraph(getModel());
		}
		return resourceDependencyGraph;
	}

	public ResourceDependencyGraph updateResourceDependencyGraph(DataFlowModel model) {
		ResourceDependencyGraph resourceGraph = NecessityOfStoringResourceStates.doDecide(model);
		resourceDependencyGraph = SelectableDataTransfers.init(resourceGraph);
		updateEdgeAttiributes(resourceDependencyGraph);
		return resourceDependencyGraph;
	}

	public void resetResourceDependencyGraph() {
		resourceDependencyGraph = null;
	}

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

	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();
		resourceDependencyGraph = 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 DataFlowModel open(File file) {
		try {

			String extension ="";
			if(file != null && file.exists()) {
				//�t�@�C�������擾
				String name = file.getName();

				//�g���q���擾
				extension = name.substring(name.lastIndexOf("."));
			}
			if(extension.contains(".model")) {
				openModel(file);
			} else {

				ParserDTRAM parserDTRAM = new ParserDTRAM(new BufferedReader(new FileReader(file)));

				try {	
					model = parserDTRAM.doParseModel();
					graph = constructGraph(model);
					parserDTRAM.doParseGeometry(graph);
					curFilePath = file.getAbsolutePath();
					curFileName = file.getName();
					if (!UpdateConflictCheck.run(model)) return null;
					updateResourceDependencyGraph(model);
					return model;
				} catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword
						| ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression
						| WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedModel | ExpectedGeometry | ExpectedNode | ExpectedResource | ExpectedFormulaChannel | ExpectedIoChannel e) {
					e.printStackTrace();
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		return null;
	}

	public DataFlowModel openModel(File file) {
		try {

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

			try {	
				model = parser.doParse();
				curFilePath = file.getAbsolutePath();
				curFileName = file.getName();
				if (!UpdateConflictCheck.run(model)) return null;
				graph = constructGraph(model);
				updateResourceDependencyGraph(model);
				return model;
			} catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword
					| ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression
					| WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}
	

	public void save() {
		if (curFilePath != null) {
			try {
				File file = new File(curFilePath);
				String extension = "";
				if(file != null && file.exists()) {
					//�t�@�C�������擾
					String name = file.getName();

					//�g���q���擾
					extension = name.substring(name.lastIndexOf("."));
				}
				if(extension.contains(".model")) {
					saveModel(file);
				} else {

					FileWriter filewriter = new FileWriter(file);		        
					filewriter.write("model {\n");
					filewriter.write(model.getSourceText());
					filewriter.write("}\n");
					filewriter.write("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(ChannelGenerator ch: model.getChannelGenerators()) {
								if(ch instanceof FormulaChannelGenerator && state.getLabel().equals(ch.getChannelName())) {
									filewriter.write("node fc " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h+"\n");		
								} else if(ch instanceof ChannelGenerator && state.getLabel().equals(ch.getChannelName())) {
									filewriter.write("node c " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h+"\n");
								}
							}

							for (IdentifierTemplate res: model.getIdentifierTemplates()){
								if(res instanceof IdentifierTemplate && state.getLabel().equals(res.getResourceName()))
									filewriter.write("node r " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n");
							}

							for (ChannelGenerator ioC: model.getIOChannelGenerators()) {
								if(ioC instanceof ChannelGenerator && state.getLabel().equals(ioC.getChannelName())) {
									filewriter.write("node ioc " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n");
								}
							}
						}
					}

					filewriter.write("}\n");
					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();
			}
		}
	}

	/**
	 * Construct a mxGraph from DataFlowModel and DataFlowModel
	 * @param model
	 * @param resourceDependencyGraph
	 * @return constructed mxGraph
	 */
	public mxGraph constructGraph(DataFlowModel model) {
		((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<>();

			// create channel vertices
			for (ChannelGenerator c: model.getChannelGenerators()) {
				DataflowChannelGenerator channelGen = (DataflowChannelGenerator) c;
				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 (IdentifierTemplate res: model.getIdentifierTemplates()) {
				Object resource = graph.insertVertex(parent, null,
						res.getResourceName(), 20, 20, 80, 30,
						"shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex
				resources.put(res, resource);
			}

			// add input, output and reference edges
			for (ChannelGenerator ch: model.getChannelGenerators()) {
				DataflowChannelGenerator channelGen = (DataflowChannelGenerator) ch;
				// input edge
				for (IdentifierTemplate srcRes: channelGen.getInputIdentifierTemplates()) {
					graph.insertEdge(parent, null, new SrcDstAttribute(srcRes, channelGen), resources.get(srcRes), channelsIn.get(channelGen), "movable=false");
				}
				// output edge
				for (IdentifierTemplate dstRes: channelGen.getOutputIdentifierTemplates()) {
					graph.insertEdge(parent, null, new SrcDstAttribute(channelGen, dstRes), channelsOut.get(channelGen), resources.get(dstRes), "movable=false");
				}
				// reference edges
				for (IdentifierTemplate refRes: channelGen.getReferenceIdentifierTemplates()) {
					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");
					}
				}
			}
		} 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 updateEdgeAttiributes(ResourceDependencyGraph resourceDependencyGraph) {
		Object parent = graph.getDefaultParent();
		graph.getModel().beginUpdate();
		try {
			// add input, output and reference edges
			for (Edge e : resourceDependencyGraph.getEdges()) {
				if (e instanceof ResourceDependency) {
					ResourceDependency dependency = (ResourceDependency) e;
					DataflowChannelGenerator channelGen = dependency.getChannelGenerator();
					ResourceNode srcRes = (ResourceNode) dependency.getSource();
					// input edge
					for (Object edge: graph.getChildEdges(parent)) {
						mxCell edgeCell = (mxCell) edge;
						if (edgeCell.getValue() instanceof SrcDstAttribute) {
							SrcDstAttribute edgeAttr = (SrcDstAttribute) edgeCell.getValue();
							if (edgeAttr.getSrouce() == srcRes.getIdentifierTemplate() && edgeAttr.getDestination() == channelGen) {
								edgeCell.setValue(dependency.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 void addIdentifierTemplate(IdentifierTemplate res) {
		getModel().addIdentifierTemplate(res);
		resetResourceDependencyGraph();
		graph.getModel().beginUpdate();
		Object parent = graph.getDefaultParent();
		try {
			graph.insertVertex(parent, null, res.getResourceName(), 20, 20, 80, 30,
					"shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex
		} finally {
			graph.getModel().endUpdate();
		}
	}

	public void addChannelGenerator(DataflowChannelGenerator channelGen) {
		getModel().addChannelGenerator(channelGen);
		resetResourceDependencyGraph();
		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 addIOChannelGenerator(DataflowChannelGenerator ioChannelGen) {
		getModel().addIOChannelGenerator(ioChannelGen);
		resetResourceDependencyGraph();
		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 addFormulaChannelGenerator(FormulaChannelGenerator formulaChannelGen) {
		getModel().addChannelGenerator(formulaChannelGen);
		resetResourceDependencyGraph();
		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) {
		DataFlowModel model = getModel();
		ChannelGenerator srcCh = model.getChannelGenerator((String) src.getValue());
		if (srcCh == null) {
			srcCh = model.getIOChannelGenerator((String) src.getValue());
			if (srcCh == null) {
				IdentifierTemplate srcRes = model.getIdentifierTemplate((String) src.getValue());
				ChannelGenerator dstCh = model.getChannelGenerator((String) dst.getValue());
				if (srcRes == null || dstCh == null) return false;
				// resource to channel edge
				ChannelMember srcCm = new ChannelMember(srcRes);
				((DataflowChannelGenerator ) dstCh).addChannelMemberAsInput(srcCm);
				edge.setValue(new SrcDstAttribute(srcRes, dstCh));
				resetResourceDependencyGraph();
				return true;
			}
		}
		IdentifierTemplate dstRes = model.getIdentifierTemplate((String) dst.getValue());
		if (dstRes == null) return false;
		// channel to resource edge
		ChannelMember dstCm = new ChannelMember(dstRes);
		((DataflowChannelGenerator) srcCh).addChannelMemberAsOutput(dstCm);
		edge.setValue(new SrcDstAttribute(srcCh, dstRes));
		resetResourceDependencyGraph();
		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.getIdentifierTemplate(srcName) != null) {
					// resource to channel edge
					ChannelGenerator ch = model.getChannelGenerator(dstName);
					ch.removeChannelMember(model.getIdentifierTemplate(srcName));
				} else if (model.getIdentifierTemplate(dstName) != null) {
					// channel to resource edge
					ChannelGenerator ch = model.getChannelGenerator(srcName);
					if (ch == null) {
						ch = model.getIOChannelGenerator(srcName);
					}
					ch.removeChannelMember(model.getIdentifierTemplate(dstName));
				}
			} else if (cell.isVertex()) {
				String name = (String) cell.getValue();
				if (model.getChannelGenerator(name) != null) {
					model.removeChannelGenerator(name);
				} else if (model.getIOChannelGenerator(name) != null) {
					model.removeIOChannelGenerator(name);
				} else if (model.getIdentifierTemplate(name) != null) {
					model.removeIdentifierTemplate(name);
				}
			}
		}
		graph.removeCells(graph.getSelectionCells());
		resetResourceDependencyGraph();
	}

	public void setChannelCode(DataflowChannelGenerator ch, String code) {
		ch.setSourceText(code);
		TokenStream stream = new TokenStream();
		for (String line: code.split("\n")) {
			stream.addLine(line);
		}
		try {
			DataflowChannelGenerator ch2 = Parser.parseChannel(stream, getModel());
			for (ChannelMember chm2: ch2.getInputChannelMembers()) {
				for (ChannelMember chm: ch.getInputChannelMembers()) {
					if (chm2.getIdentifierTemplate() == chm.getIdentifierTemplate()) {
						chm.setStateTransition(chm2.getStateTransition());
						break;
					}
				}
			}
			for (ChannelMember chm2: ch2.getOutputChannelMembers()) {
				for (ChannelMember chm: ch.getOutputChannelMembers()) {
					if (chm2.getIdentifierTemplate() == chm.getIdentifierTemplate()) {
						chm.setStateTransition(chm2.getStateTransition());
						break;
					}
				}
			}
			for (ChannelMember chm2: ch2.getReferenceChannelMembers()) {
				for (ChannelMember chm: ch.getReferenceChannelMembers()) {
					if (chm2.getIdentifierTemplate() == chm.getIdentifierTemplate()) {
						chm.setStateTransition(chm2.getStateTransition());
						break;
					}
				}
			}
			resetResourceDependencyGraph();
		} catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket
				| ExpectedInOrOutOrRefKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression
				| WrongLHSExpression | WrongRHSExpression | ExpectedAssignment e) {
			e.printStackTrace();
		}
	}

	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 getSrouce() {
			return src;
		}

		public Object getDestination() {
			return dst;
		}

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