You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/03/20 15:22:44 UTC

[30/51] [abbrv] [partial] incubator-taverna-workbench git commit: taverna-workbench-* -> taverna-*

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphNode.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphNode.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphNode.java
new file mode 100644
index 0000000..004b3f6
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphNode.java
@@ -0,0 +1,611 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg;
+
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphSettings.COMPLETED_COLOUR;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphSettings.ERROR_COLOUR;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.animate;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.calculatePoints;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createAnimationElement;
+import static org.apache.batik.util.CSSConstants.CSS_ALL_VALUE;
+import static org.apache.batik.util.CSSConstants.CSS_BLACK_VALUE;
+import static org.apache.batik.util.CSSConstants.CSS_DISPLAY_PROPERTY;
+import static org.apache.batik.util.CSSConstants.CSS_HIDDEN_VALUE;
+import static org.apache.batik.util.CSSConstants.CSS_INLINE_VALUE;
+import static org.apache.batik.util.CSSConstants.CSS_NONE_VALUE;
+import static org.apache.batik.util.CSSConstants.CSS_POINTER_EVENTS_PROPERTY;
+import static org.apache.batik.util.CSSConstants.CSS_VISIBILITY_PROPERTY;
+import static org.apache.batik.util.CSSConstants.CSS_VISIBLE_VALUE;
+import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TAG;
+import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TRANSFORM_TAG;
+import static org.apache.batik.util.SVGConstants.SVG_CLICK_EVENT_TYPE;
+import static org.apache.batik.util.SVGConstants.SVG_CX_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_CY_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_D_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_END_VALUE;
+import static org.apache.batik.util.SVGConstants.SVG_FILL_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_FONT_FAMILY_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_FONT_SIZE_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_HEIGHT_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_MIDDLE_VALUE;
+import static org.apache.batik.util.SVGConstants.SVG_MOUSEDOWN_EVENT_TYPE;
+import static org.apache.batik.util.SVGConstants.SVG_MOUSEMOVE_EVENT_TYPE;
+import static org.apache.batik.util.SVGConstants.SVG_MOUSEOUT_EVENT_TYPE;
+import static org.apache.batik.util.SVGConstants.SVG_MOUSEOVER_EVENT_TYPE;
+import static org.apache.batik.util.SVGConstants.SVG_NONE_VALUE;
+import static org.apache.batik.util.SVGConstants.SVG_POINTS_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_RX_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_RY_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_STROKE_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_TRANSFORM_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_WIDTH_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_X_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_Y_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.TRANSFORM_TRANSLATE;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Point;
+
+import net.sf.taverna.t2.workbench.models.graph.Graph;
+import net.sf.taverna.t2.workbench.models.graph.GraphNode;
+import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseClickEventListener;
+import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseDownEventListener;
+import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseMovedEventListener;
+import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOutEventListener;
+import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOverEventListener;
+
+import org.apache.batik.dom.svg.SVGOMAnimationElement;
+import org.apache.batik.dom.svg.SVGOMEllipseElement;
+import org.apache.batik.dom.svg.SVGOMGElement;
+import org.apache.batik.dom.svg.SVGOMPathElement;
+import org.apache.batik.dom.svg.SVGOMPolygonElement;
+import org.apache.batik.dom.svg.SVGOMRectElement;
+import org.apache.batik.dom.svg.SVGOMTextElement;
+import org.apache.batik.util.CSSConstants;
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Text;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+import org.w3c.dom.svg.SVGElement;
+
+/**
+ * SVG representation of a graph node.
+ * 
+ * @author David Withers
+ */
+public class SVGGraphNode extends GraphNode {
+	private SVGGraphController graphController;
+	private SVGGraphElementDelegate delegate;
+	private SVGMouseClickEventListener mouseClickAction;
+	private SVGMouseMovedEventListener mouseMovedAction;
+	private SVGMouseDownEventListener mouseDownAction;
+	@SuppressWarnings("unused")
+	private SVGMouseOverEventListener mouseOverAction;
+	@SuppressWarnings("unused")
+	private SVGMouseOutEventListener mouseOutAction;
+	private SVGOMGElement mainGroup, labelGroup, portsGroup;
+	private SVGElement expandedElement, contractedElement;
+	private SVGOMPolygonElement polygon, completedPolygon;
+	private SVGOMEllipseElement ellipse;
+	private SVGOMTextElement label, iteration, error;
+	private Text labelText, iterationText, errorsText;
+	private SVGElement deleteButton;
+	private SVGOMAnimationElement animateShape, animatePosition, animateLabel, animateIteration,
+			animateErrors;
+
+	public SVGGraphNode(SVGGraphController graphController) {
+		super(graphController);
+		this.graphController = graphController;
+		mouseClickAction = new SVGMouseClickEventListener(this);
+		mouseDownAction = new SVGMouseDownEventListener(this);
+		mouseMovedAction = new SVGMouseMovedEventListener(this);
+		mouseOverAction = new SVGMouseOverEventListener(this);
+		mouseOutAction = new SVGMouseOutEventListener(this);
+
+		mainGroup = graphController.createGElem();
+		mainGroup.setAttribute("alignment-baseline", SVG_MIDDLE_VALUE);
+		mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, CSS_BLACK_VALUE);
+		mainGroup.setAttribute(SVG_STROKE_DASHARRAY_ATTRIBUTE, CSS_NONE_VALUE);
+		mainGroup.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "1");
+		mainGroup.setAttribute(SVG_FILL_ATTRIBUTE, CSS_NONE_VALUE);
+
+		EventTarget t = (EventTarget) mainGroup;
+		t.addEventListener(SVG_CLICK_EVENT_TYPE, mouseClickAction, false);
+		t.addEventListener(SVG_MOUSEMOVE_EVENT_TYPE, mouseMovedAction, false);
+		t.addEventListener(SVG_MOUSEDOWN_EVENT_TYPE, mouseDownAction, false);
+//		t.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, mouseOverAction, false);
+//		t.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, mouseOutAction, false);
+
+		expandedElement = graphController.createGElem();
+		contractedElement = graphController.createGElem();
+
+		portsGroup = graphController.createGElem();
+		portsGroup.setAttribute(CSS_DISPLAY_PROPERTY, CSS_NONE_VALUE);
+		contractedElement.appendChild(portsGroup);
+
+		mainGroup.appendChild(contractedElement);
+
+		polygon = graphController.createPolygon();
+		contractedElement.appendChild(polygon);
+
+		ellipse = graphController.createEllipse();
+		ellipse.setAttribute(CSS_DISPLAY_PROPERTY, CSS_NONE_VALUE);
+		ellipse.setAttribute(SVG_RX_ATTRIBUTE, String.valueOf(2));
+		ellipse.setAttribute(SVG_CX_ATTRIBUTE, String.valueOf(0));
+		ellipse.setAttribute(SVG_RY_ATTRIBUTE, String.valueOf(2));
+		ellipse.setAttribute(SVG_CY_ATTRIBUTE, String.valueOf(0));
+		contractedElement.appendChild(ellipse);
+
+		completedPolygon = graphController.createPolygon();
+		completedPolygon.setAttribute(SVG_POINTS_ATTRIBUTE,
+				calculatePoints(getShape(), 0, 0));
+		completedPolygon.setAttribute(SVG_FILL_ATTRIBUTE, COMPLETED_COLOUR);
+		completedPolygon.setAttribute(SVG_FILL_OPACITY_ATTRIBUTE, "0.8");
+		contractedElement.appendChild(completedPolygon);
+
+		labelText = graphController.createText("");
+		label = graphController.createText(labelText);
+		label.setAttribute(SVG_TEXT_ANCHOR_ATTRIBUTE, SVG_MIDDLE_VALUE);
+		label.setAttribute("baseline-shift", "-35%");
+		label.setAttribute(SVG_FILL_ATTRIBUTE, CSS_BLACK_VALUE);
+		label.setAttribute(SVG_STROKE_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE);
+		labelGroup = graphController.createGElem();
+		labelGroup.appendChild(label);
+		contractedElement.appendChild(labelGroup);
+
+		iterationText = graphController.createText("");
+		iteration = graphController.createText(iterationText);
+		iteration.setAttribute(SVG_TEXT_ANCHOR_ATTRIBUTE, SVG_END_VALUE);
+		iteration.setAttribute(SVG_FONT_SIZE_ATTRIBUTE, "6");
+		iteration.setAttribute(SVG_FONT_FAMILY_ATTRIBUTE, "sans-serif");
+		iteration.setAttribute(SVG_FILL_ATTRIBUTE, CSS_BLACK_VALUE);
+		iteration.setAttribute(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE);
+		contractedElement.appendChild(iteration);
+
+		errorsText = graphController.createText("");
+		error = graphController.createText(errorsText);
+		error.setAttribute(SVG_TEXT_ANCHOR_ATTRIBUTE, SVG_END_VALUE);
+		error.setAttribute(SVG_FONT_SIZE_ATTRIBUTE, "6");
+		error.setAttribute(SVG_FONT_FAMILY_ATTRIBUTE, "sans-serif");
+		error.setAttribute(SVG_FILL_ATTRIBUTE, CSSConstants.CSS_BLACK_VALUE);
+		error.setAttribute(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE);
+		contractedElement.appendChild(error);
+
+		// deleteButton = createDeleteButton();
+		// g.appendChild(deleteButton);
+
+		animateShape = createAnimationElement(graphController, SVG_ANIMATE_TAG,
+				SVG_POINTS_ATTRIBUTE, null);
+
+		animatePosition = createAnimationElement(graphController,
+				SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE,
+				TRANSFORM_TRANSLATE);
+
+		animateLabel = SVGUtil.createAnimationElement(graphController,
+				SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE,
+				TRANSFORM_TRANSLATE);
+
+		animateIteration = createAnimationElement(graphController,
+				SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE,
+				TRANSFORM_TRANSLATE);
+
+		animateErrors = createAnimationElement(graphController,
+				SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE,
+				TRANSFORM_TRANSLATE);
+
+		delegate = new SVGGraphElementDelegate(graphController, this, mainGroup);
+	}
+
+	@SuppressWarnings("unused")
+	private SVGElement createDeleteButton() {
+		final SVGOMGElement button = graphController.createGElem();
+		button.setAttribute(CSS_VISIBILITY_PROPERTY, CSS_HIDDEN_VALUE);
+		button.setAttribute(CSS_POINTER_EVENTS_PROPERTY, CSS_ALL_VALUE);
+
+		SVGOMRectElement rect = graphController.createRect();
+		rect.setAttribute(SVG_X_ATTRIBUTE, "4");
+		rect.setAttribute(SVG_Y_ATTRIBUTE, "4");
+		rect.setAttribute(SVG_WIDTH_ATTRIBUTE, "13");
+		rect.setAttribute(SVG_HEIGHT_ATTRIBUTE, "13");
+		rect.setAttribute(SVG_FILL_ATTRIBUTE, "none");
+		button.appendChild(rect);
+
+		final SVGOMPathElement path = graphController.createPath();
+		path.setAttribute(SVG_STROKE_ATTRIBUTE, "white");
+		path.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "2");
+		path.setAttribute(SVG_D_ATTRIBUTE, "M5,5L12,12M5,12L12,5");
+		button.appendChild(path);
+
+		EventTarget t = (EventTarget) button;
+		t.addEventListener(SVG_MOUSEOVER_EVENT_TYPE, new EventListener() {
+			@Override
+			public void handleEvent(Event evt) {
+				if (isInteractive()) {
+					deleteButton.setAttribute(CSS_VISIBILITY_PROPERTY,
+							CSS_VISIBLE_VALUE);
+					path.setAttribute(SVG_STROKE_ATTRIBUTE, "red");
+					evt.stopPropagation();
+				}
+			}
+		}, false);
+		t.addEventListener(SVG_MOUSEOUT_EVENT_TYPE, new EventListener() {
+			@Override
+			public void handleEvent(Event evt) {
+				if (isInteractive()) {
+					path.setAttribute(SVG_STROKE_ATTRIBUTE, "white");
+					evt.stopPropagation();
+				}
+			}
+		}, false);
+
+		return button;
+	}
+
+	public SVGElement getSVGElement() {
+		return mainGroup;
+	}
+
+	@Override
+	public void setActive(final boolean active) {
+		super.setActive(active);
+		if (isInteractive())
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					if (active) {
+						deleteButton.setAttribute(CSS_VISIBILITY_PROPERTY,
+								CSS_VISIBLE_VALUE);
+						// deleteButton.setAttribute(CSSConstants.CSS_DISPLAY_PROPERTY,
+						// CSSConstants.CSS_INLINE_VALUE);
+					} else {
+						deleteButton.setAttribute(CSS_VISIBILITY_PROPERTY,
+								CSS_HIDDEN_VALUE);
+						// button.setAttribute(CSSConstants.CSS_DISPLAY_PROPERTY,
+						// CSSConstants.CSS_NONE_VALUE);
+					}
+				}
+			});
+	}
+
+	@Override
+	public void setGraph(Graph graph) {
+		super.setGraph(graph);
+		if (graph instanceof SVGGraph) {
+			SVGGraph svgGraph = (SVGGraph) graph;
+			final SVGElement graphElement = svgGraph.getSVGElement();
+			if (isExpanded())
+				graphController.updateSVGDocument(new Runnable() {
+					@Override
+					public void run() {
+						mainGroup.replaceChild(expandedElement, graphElement);
+					}
+				});
+			expandedElement = graphElement;
+		}
+	}
+
+	@Override
+	public void setExpanded(final boolean expanded) {
+		if (isExpanded() != expanded)
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					if (expanded)
+						mainGroup.replaceChild(expandedElement, contractedElement);
+					else
+						mainGroup.replaceChild(contractedElement, expandedElement);
+				}
+			});
+		super.setExpanded(expanded);
+	}
+
+	@Override
+	public void addSourceNode(final GraphNode sourceNode) {
+		super.addSourceNode(sourceNode);
+		if (sourceNode instanceof SVGGraphNode)
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					SVGGraphNode svgGraphNode = (SVGGraphNode) sourceNode;
+					portsGroup.appendChild(svgGraphNode.getSVGElement());
+				}
+			});
+	}
+
+	@Override
+	public boolean removeSourceNode(final GraphNode sourceNode) {
+		if (sourceNode instanceof SVGGraphNode)
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					SVGGraphNode svgGraphNode = (SVGGraphNode) sourceNode;
+					portsGroup.removeChild(svgGraphNode.getSVGElement());
+				}
+			});
+		return super.removeSourceNode(sourceNode);
+	}
+
+	@Override
+	public void addSinkNode(final GraphNode sinkNode) {
+		super.addSinkNode(sinkNode);
+		if (sinkNode instanceof SVGGraphNode)
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					SVGGraphNode svgGraphNode = (SVGGraphNode) sinkNode;
+					portsGroup.appendChild(svgGraphNode.getSVGElement());
+				}
+			});
+	}
+
+	@Override
+	public boolean removeSinkNode(final GraphNode sinkNode) {
+		if (sinkNode instanceof SVGGraphNode)
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					SVGGraphNode svgGraphNode = (SVGGraphNode) sinkNode;
+					portsGroup.removeChild(svgGraphNode.getSVGElement());
+				}
+			});
+		return super.removeSinkNode(sinkNode);
+	}
+
+	@Override
+	public void setPosition(final Point position) {
+		final Point oldPosition = getPosition();
+		if (position != null && !position.equals(oldPosition)) {
+			super.setPosition(position);
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					if (graphController.isAnimatable())
+						animate(animatePosition, mainGroup,
+								graphController.getAnimationSpeed(),
+								oldPosition.x + ", " + oldPosition.y,
+								position.x + ", " + position.y);
+					else
+						mainGroup.setAttribute(SVG_TRANSFORM_ATTRIBUTE,
+								"translate(" + position.x + " " + position.y
+										+ ")");
+				}
+			});
+		}
+	}
+
+	@Override
+	public void setSize(final Dimension size) {
+		final Dimension oldSize = getSize();
+		if (size != null && !size.equals(oldSize)) {
+			super.setSize(size);
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					adjustSize(size, oldSize);
+				}
+			});
+		}
+	}
+
+	/** core of implementation of {@link #setSize(Dimension)} */
+	private void adjustSize(Dimension size, Dimension oldSize) {
+		int oldWidth = oldSize.width;
+		int oldHeight = oldSize.height;
+		if (graphController.isAnimatable()) {
+			if (Shape.CIRCLE.equals(getShape())) {
+				ellipse.setAttribute(SVG_RX_ATTRIBUTE,
+						String.valueOf(size.width / 2f));
+				ellipse.setAttribute(SVG_CX_ATTRIBUTE,
+						String.valueOf(size.width / 2f));
+				ellipse.setAttribute(SVG_RY_ATTRIBUTE,
+						String.valueOf(size.height / 2f));
+				ellipse.setAttribute(SVG_CY_ATTRIBUTE,
+						String.valueOf(size.height / 2f));
+			} else
+				animate(animateShape, polygon,
+						graphController.getAnimationSpeed(),
+						calculatePoints(getShape(), oldWidth, oldHeight),
+						calculatePoints(getShape(), getWidth(), getHeight()));
+
+			if (getLabel() != null && !getLabel().isEmpty())
+				animate(animateLabel, labelGroup,
+						graphController.getAnimationSpeed(), (oldWidth / 2f)
+								+ ", " + (oldHeight / 2f), (getWidth() / 2f)
+								+ ", " + (getHeight() / 2f));
+			else
+				labelGroup.setAttribute(SVG_TRANSFORM_ATTRIBUTE,
+						"translate(" + getWidth() / 2f + " " + getHeight() / 2f + ")");
+
+			if (getIteration() > 0)
+				animate(animateIteration, iteration,
+						graphController.getAnimationSpeed(), (oldWidth - 1.5)
+								+ ", 5.5", (getWidth() - 1.5) + ", 5.5");
+			else
+				iteration.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+						+ (getWidth() - 1.5) + " 5.5)");
+
+			if (getErrors() > 0)
+				animate(animateErrors, error,
+						graphController.getAnimationSpeed(), (oldWidth - 1.5)
+								+ ", " + (oldHeight - 1), (getWidth() - 1.5)
+								+ ", " + (getHeight() - 1));
+			else
+				error.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+						+ (getWidth() - 1.5) + " " + (getHeight() - 1) + ")");
+		} else {
+			if (Shape.CIRCLE.equals(getShape())) {
+				ellipse.setAttribute(SVG_RX_ATTRIBUTE,
+						String.valueOf(size.width / 2f));
+				ellipse.setAttribute(SVG_CX_ATTRIBUTE,
+						String.valueOf(size.width / 2f));
+				ellipse.setAttribute(SVG_RY_ATTRIBUTE,
+						String.valueOf(size.height / 2f));
+				ellipse.setAttribute(SVG_CY_ATTRIBUTE,
+						String.valueOf(size.height / 2f));
+			} else
+				polygon.setAttribute(SVG_POINTS_ATTRIBUTE,
+						calculatePoints(getShape(), getWidth(), getHeight()));
+
+			labelGroup.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+					+ getWidth() / 2f + " " + getHeight() / 2f + ")");
+			iteration.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+					+ (getWidth() - 1.5) + " 5.5)");
+			error.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+					+ (getWidth() - 1.5) + " " + (getHeight() - 1) + ")");
+		}
+	}
+
+	@Override
+	public void setShape(final Shape shape) {
+		final Shape currentShape = getShape();
+		if (shape != null && !shape.equals(currentShape)) {
+			super.setShape(shape);
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					if (Shape.CIRCLE.equals(shape)) {
+						ellipse.setAttribute(CSS_DISPLAY_PROPERTY,
+								CSS_INLINE_VALUE);
+						polygon.setAttribute(CSS_DISPLAY_PROPERTY,
+								CSS_NONE_VALUE);
+					} else if (Shape.CIRCLE.equals(currentShape)) {
+						ellipse.setAttribute(CSS_DISPLAY_PROPERTY,
+								CSS_NONE_VALUE);
+						polygon.setAttribute(CSS_DISPLAY_PROPERTY,
+								CSS_INLINE_VALUE);
+					}
+					if (Shape.RECORD.equals(shape))
+						portsGroup.setAttribute(CSS_DISPLAY_PROPERTY,
+								CSS_INLINE_VALUE);
+					else if (Shape.RECORD.equals(currentShape))
+						portsGroup.setAttribute(CSS_DISPLAY_PROPERTY,
+								CSS_NONE_VALUE);
+				}
+			});
+		}
+	}
+
+	@Override
+	public void setLabel(final String label) {
+		super.setLabel(label);
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				labelText.setData(label);
+			}
+		});
+	}
+
+	@Override
+	public void setIteration(final int iteration) {
+		super.setIteration(iteration);
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				if (iteration > 0)
+					iterationText.setData(String.valueOf(iteration));
+				else
+					iterationText.setData("");
+			}
+		});
+	}
+
+	@Override
+	public void setErrors(final int errors) {
+		super.setErrors(errors);
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				if (errors > 0) {
+					errorsText.setData(String.valueOf(errors));
+					completedPolygon.setAttribute(SVG_FILL_ATTRIBUTE,
+							ERROR_COLOUR);
+				} else {
+					errorsText.setData("");
+					completedPolygon.setAttribute(SVG_FILL_ATTRIBUTE,
+							COMPLETED_COLOUR);
+				}
+			}
+		});
+	}
+
+	@Override
+	public void setCompleted(final float complete) {
+		super.setCompleted(complete);
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				completedPolygon.setAttribute(
+						SVG_POINTS_ATTRIBUTE,
+						calculatePoints(getShape(),
+								(int) (getWidth() * complete), getHeight()));
+			}
+		});
+	}
+
+	@Override
+	public void setSelected(final boolean selected) {
+		delegate.setSelected(selected);
+		super.setSelected(selected);
+	}
+
+	@Override
+	public void setLineStyle(final LineStyle lineStyle) {
+		delegate.setLineStyle(lineStyle);
+		super.setLineStyle(lineStyle);
+	}
+
+	@Override
+	public void setColor(final Color color) {
+		delegate.setColor(color);
+		super.setColor(color);
+	}
+
+	@Override
+	public void setFillColor(final Color fillColor) {
+		delegate.setFillColor(fillColor);
+		super.setFillColor(fillColor);
+	}
+
+	@Override
+	public void setVisible(final boolean visible) {
+		delegate.setVisible(visible);
+		super.setVisible(visible);
+	}
+
+	@Override
+	public void setFiltered(final boolean filtered) {
+		delegate.setFiltered(filtered);
+		super.setFiltered(filtered);
+	}
+
+	@Override
+	public void setOpacity(final float opacity) {
+		delegate.setOpacity(opacity);
+		super.setOpacity(opacity);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphSettings.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphSettings.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphSettings.java
new file mode 100644
index 0000000..777102e
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphSettings.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg;
+
+public interface SVGGraphSettings {
+	String COMPLETED_COLOUR = "grey";
+	String ERROR_COLOUR = "#dd3131";
+	String SELECTED_COLOUR = "#4377d3";
+	String NORMAL_COLOUR = "black";
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGMonitorShape.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGMonitorShape.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGMonitorShape.java
new file mode 100644
index 0000000..5aab55f
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGMonitorShape.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg;
+
+import org.apache.batik.dom.svg.SVGOMPolygonElement;
+
+public interface SVGMonitorShape extends SVGShape {
+	/**
+	 * Returns the polygon used to display the completed value.
+	 * 
+	 * @return the polygon used to display the completed value
+	 */
+	SVGOMPolygonElement getCompletedPolygon();
+
+	/**
+	 * Sets the polygon used to display the completed value.
+	 * 
+	 * @param polygon
+	 *            the new polygon used to display the completed value
+	 */
+	void setCompletedPolygon(SVGOMPolygonElement polygon);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGShape.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGShape.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGShape.java
new file mode 100644
index 0000000..8ebc338
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGShape.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg;
+
+public interface SVGShape {
+	public void setIteration(final int iteration);
+
+	// public void setErrors(final int errors);
+
+	// public void setCompleted(final float complete);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGUtil.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGUtil.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGUtil.java
new file mode 100644
index 0000000..f2e4247
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGUtil.java
@@ -0,0 +1,477 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg;
+
+import static java.lang.Float.parseFloat;
+import static java.lang.Math.PI;
+import static java.lang.Math.atan2;
+import static org.apache.batik.dom.svg.SVGDOMImplementation.getDOMImplementation;
+import static org.apache.batik.util.SMILConstants.SMIL_ATTRIBUTE_NAME_ATTRIBUTE;
+import static org.apache.batik.util.SMILConstants.SMIL_DUR_ATTRIBUTE;
+import static org.apache.batik.util.SMILConstants.SMIL_FILL_ATTRIBUTE;
+import static org.apache.batik.util.SMILConstants.SMIL_FREEZE_VALUE;
+import static org.apache.batik.util.SMILConstants.SMIL_FROM_ATTRIBUTE;
+import static org.apache.batik.util.SMILConstants.SMIL_TO_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_TYPE_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_X1_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_X2_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_Y1_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_Y2_ATTRIBUTE;
+import static org.apache.batik.util.XMLResourceDescriptor.getXMLParserClassName;
+
+import java.awt.Color;
+import java.awt.Point;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.util.List;
+
+import net.sf.taverna.t2.lang.io.StreamDevourer;
+import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
+import net.sf.taverna.t2.workbench.models.graph.GraphShapeElement.Shape;
+
+import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
+import org.apache.batik.dom.svg.SVGDOMImplementation;
+import org.apache.batik.dom.svg.SVGOMAnimationElement;
+import org.apache.batik.dom.svg.SVGOMPoint;
+import org.apache.log4j.Logger;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Element;
+import org.w3c.dom.svg.SVGDocument;
+import org.w3c.dom.svg.SVGElement;
+import org.w3c.dom.svg.SVGLocatable;
+import org.w3c.dom.svg.SVGMatrix;
+//import org.apache.batik.transcoder.TranscoderException;
+//import org.apache.batik.transcoder.svg2svg.PrettyPrinter;
+
+/**
+ * Utility methods.
+ *
+ * @author David Withers
+ */
+public class SVGUtil {
+	private static final String C = "C";
+	private static final String M = "M";
+	private static final String SPACE = " ";
+	private static final String COMMA = ",";
+	public static final String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;
+	private static final String SVG = "svg";
+	private static final Logger logger = Logger.getLogger(SVGUtil.class);
+
+	private static SAXSVGDocumentFactory docFactory;
+
+	static {
+		String parser = getXMLParserClassName();
+		logger.info("Using XML parser " + parser);
+		docFactory = new SAXSVGDocumentFactory(parser);
+	}
+
+	/**
+	 * Creates a new SVGDocument.
+	 * 
+	 * @return a new SVGDocument
+	 */
+	public static SVGDocument createSVGDocument() {
+		DOMImplementation impl = getDOMImplementation();
+		return (SVGDocument) impl.createDocument(svgNS, SVG, null);
+	}
+
+	/**
+	 * Converts a point in screen coordinates to a point in document
+	 * coordinates.
+	 * 
+	 * @param locatable
+	 * @param screenPoint
+	 *            the point in screen coordinates
+	 * @return the point in document coordinates
+	 */
+	public static SVGOMPoint screenToDocument(SVGLocatable locatable,
+			SVGOMPoint screenPoint) {
+		SVGMatrix mat = ((SVGLocatable) locatable.getFarthestViewportElement())
+				.getScreenCTM().inverse();
+		return (SVGOMPoint) screenPoint.matrixTransform(mat);
+	}
+
+	/**
+	 * Writes SVG to the console. For debugging only.
+	 *
+	 * @param svgDocument
+	 *            the document to output
+	 */
+//	public static void writeSVG(SVGDocument svgDocument) {
+//		writeSVG(svgDocument, new OutputStreamWriter(System.out));
+//	}
+
+	/**
+	 * Writes SVG to an output stream.
+	 *
+	 * @param svgDocument
+	 *            the document to output
+	 * @param writer
+	 *            the stream to write the document to
+	 */
+//	public static void writeSVG(SVGDocument svgDocument, Writer writer) {
+//		StringWriter sw = new StringWriter();
+//		try {
+//			Transformer transformer = TransformerFactory.newInstance().newTransformer();
+//			Source src = new DOMSource(svgDocument.getDocumentElement());
+//			transformer.transform(src, new StreamResult(sw));
+//
+//			PrettyPrinter pp = new PrettyPrinter();
+//			pp.print(new StringReader(sw.toString()), writer);
+//		} catch (TransformerException | TranscoderException | IOException e) {
+//			e.printStackTrace(new PrintWriter(writer));
+//		}
+//	}
+
+	/**
+	 * Generates an SVGDocument from DOT text by calling out to GraphViz.
+	 * 
+	 * @param dotText
+	 * @return an SVGDocument
+	 * @throws IOException
+	 */
+	public static SVGDocument getSVG(String dotText,
+			WorkbenchConfiguration workbenchConfiguration) throws IOException {
+		String dotLocation = (String) workbenchConfiguration
+				.getProperty("taverna.dotlocation");
+		if (dotLocation == null)
+			dotLocation = "dot";
+		logger.debug("Invoking dot...");
+		Process dotProcess = exec(dotLocation, "-Tsvg");
+		StreamDevourer devourer = new StreamDevourer(
+				dotProcess.getInputStream());
+		devourer.start();
+		try (PrintWriter out = new PrintWriter(dotProcess.getOutputStream(),
+				true)) {
+			out.print(dotText);
+			out.flush();
+		}
+
+		String svgText = devourer.blockOnOutput();
+		/*
+		 * Avoid TAV-424, replace buggy SVG outputted by "modern" GraphViz
+		 * versions. http://www.graphviz.org/bugs/b1075.html
+		 * 
+		 * Contributed by Marko Ullgren
+		 */
+		svgText = svgText.replaceAll("font-weight:regular",
+				"font-weight:normal");
+		logger.info(svgText);
+		// Fake URI, just used for internal references like #fish
+		return docFactory.createSVGDocument(
+				"http://taverna.sf.net/diagram/generated.svg",
+				new StringReader(svgText));
+	}
+
+	/**
+	 * Generates DOT text with layout information from DOT text by calling out
+	 * to GraphViz.
+	 * 
+	 * @param dotText
+	 *            dot text
+	 * @return dot text with layout information
+	 * @throws IOException
+	 */
+	public static String getDot(String dotText,
+			WorkbenchConfiguration workbenchConfiguration) throws IOException {
+		String dotLocation = (String) workbenchConfiguration
+				.getProperty("taverna.dotlocation");
+		if (dotLocation == null)
+			dotLocation = "dot";
+		logger.debug("Invoking dot...");
+		Process dotProcess = exec(dotLocation, "-Tdot", "-Glp=0,0");
+		StreamDevourer devourer = new StreamDevourer(
+				dotProcess.getInputStream());
+		devourer.start();
+		try (PrintWriter out = new PrintWriter(dotProcess.getOutputStream(),
+				true)) {
+			out.print(dotText);
+			out.flush();
+		}
+
+		String dot = devourer.blockOnOutput();
+		// logger.info(dot);
+		return dot;
+	}
+
+	private static Process exec(String...args) throws IOException {
+		Process p = Runtime.getRuntime().exec(args);
+		/*
+		 * Must create an error devourer otherwise stderr fills up and the
+		 * process stalls!
+		 */
+		new StreamDevourer(p.getErrorStream()).start();
+		return p;
+	}
+
+	/**
+	 * Returns the hex value for a <code>Color</code>. If color is null "none"
+	 * is returned.
+	 *
+	 * @param color
+	 *            the <code>Color</code> to convert to hex code
+	 * @return the hex value
+	 */
+	public static String getHexValue(Color color) {
+		if (color == null)
+			return "none";
+
+		return String.format("#%02x%02x%02x", color.getRed(), color.getGreen(),
+				color.getBlue());
+	}
+
+	/**
+	 * Calculates the angle to rotate an arrow head to be placed on the end of a
+	 * line.
+	 *
+	 * @param line
+	 *            the line to calculate the arrow head angle from
+	 * @return the angle to rotate an arrow head
+	 */
+	public static double calculateAngle(Element line) {
+		float x1 = parseFloat(line.getAttribute(SVG_X1_ATTRIBUTE));
+		float y1 = parseFloat(line.getAttribute(SVG_Y1_ATTRIBUTE));
+		float x2 = parseFloat(line.getAttribute(SVG_X2_ATTRIBUTE));
+		float y2 = parseFloat(line.getAttribute(SVG_Y2_ATTRIBUTE));
+		return calculateAngle(x1, y1, x2, y2);
+	}
+
+	/**
+	 * Calculates the angle to rotate an arrow head to be placed on the end of a
+	 * line.
+	 *
+	 * @param pointList
+	 *            the list of <code>Point</code>s to calculate the arrow head
+	 *            angle from
+	 * @return the angle to rotate an arrow head
+	 */
+	public static double calculateAngle(List<Point> pointList) {
+		double angle = 0d;
+		if (pointList.size() > 1) {
+			int listSize = pointList.size();
+			Point a = pointList.get(listSize - 2);
+			Point b = pointList.get(listSize - 1);
+			/*
+			 * dot sometimes generates paths with the same point repeated at the
+			 * end of the path, so move back along the path until two different
+			 * points are found
+			 */
+			while (a.equals(b) && listSize > 2) {
+				b = a;
+				a = pointList.get(--listSize - 2);
+			}
+			angle = calculateAngle(a.x, a.y, b.x, b.y);
+		}
+		return angle;
+	}
+
+	/**
+	 * Calculates the angle to rotate an arrow head to be placed on the end of a
+	 * line.
+	 * 
+	 * @param x1
+	 *            the x coordinate of the start of the line
+	 * @param y1
+	 *            the y coordinate of the start of the line
+	 * @param x2
+	 *            the x coordinate of the end of the line
+	 * @param y2
+	 *            the y coordinate of the end of the line
+	 * @return the angle to rotate an arrow head
+	 */
+	public static double calculateAngle(float x1, float y1, float x2, float y2) {
+		return atan2(y2 - y1, x2 - x1) * 180 / PI;
+	}
+
+	/**
+	 * Calculates the points that make up the polygon for the specified
+	 * {@link Shape}.
+	 *
+	 * @param shape
+	 *            the <code>Shape</code> to calculate points for
+	 * @param width
+	 *            the width of the <code>Shape</code>
+	 * @param height
+	 *            the height of the <code>Shape</code>
+	 * @return the points that make up the polygon for the specified
+	 *         <code>Shape</code>
+	 */
+	public static String calculatePoints(Shape shape, int width, int height) {
+		StringBuilder sb = new StringBuilder();
+		switch (shape) {
+		case BOX:
+		case RECORD:
+			addPoint(sb, 0, 0);
+			addPoint(sb, width, 0);
+			addPoint(sb, width, height);
+			addPoint(sb, 0, height);
+			break;
+		case HOUSE:
+			addPoint(sb, width / 2f, 0);
+			addPoint(sb, width, height / 3f);
+			addPoint(sb, width, height - 3);
+			addPoint(sb, 0, height - 3);
+			addPoint(sb, 0, height / 3f);
+			break;
+		case INVHOUSE:
+			addPoint(sb, 0, 3);
+			addPoint(sb, width, 3);
+			addPoint(sb, width, height / 3f * 2f);
+			addPoint(sb, width / 2f, height);
+			addPoint(sb, 0, height / 3f * 2f);
+			break;
+		case TRIANGLE:
+			addPoint(sb, width / 2f, 0);
+			addPoint(sb, width, height);
+			addPoint(sb, 0, height);
+			break;
+		case INVTRIANGLE:
+			addPoint(sb, 0, 0);
+			addPoint(sb, width, 0);
+			addPoint(sb, width / 2f, height);
+			break;
+		default:
+			// Nothing to do for the others
+			break;
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Appends x y coordinates to a <code>StringBuilder</code> in the format
+	 * "x,y ".
+	 * 
+	 * @param stringBuilder
+	 *            the <code>StringBuilder</code> to append the point to
+	 * @param x
+	 *            the x coordinate
+	 * @param y
+	 *            the y coordinate
+	 */
+	public static void addPoint(StringBuilder stringBuilder, float x, float y) {
+		stringBuilder.append(x).append(COMMA).append(y).append(SPACE);
+	}
+
+	/**
+	 * Converts a list of points into a string format for a cubic Bezier curve.
+	 *
+	 * For example, "M100,200 C100,100 250,100 250,200". See
+	 * http://www.w3.org/TR/SVG11/paths.html#PathDataCubicBezierCommands.
+	 *
+	 * @param pointList
+	 *            a list of points that describes a cubic Bezier curve
+	 * @return a string that describes a cubic Bezier curve
+	 */
+	public static String getPath(List<Point> pointList) {
+		StringBuilder sb = new StringBuilder();
+		if (pointList != null && pointList.size() > 1) {
+			Point firstPoint = pointList.get(0);
+			sb.append(M).append(firstPoint.x).append(COMMA)
+					.append(firstPoint.y);
+			sb.append(SPACE);
+			Point secontPoint = pointList.get(1);
+			sb.append(C).append(secontPoint.x).append(COMMA)
+					.append(secontPoint.y);
+			for (int i = 2; i < pointList.size(); i++) {
+				Point point = pointList.get(i);
+				sb.append(SPACE).append(point.x).append(COMMA).append(point.y);
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Creates an animation element.
+	 *
+	 * @param graphController
+	 *            the SVGGraphController to use to create the animation element
+	 * @param elementType
+	 *            the type of animation element to create
+	 * @param attribute
+	 *            the attribute that the animation should affect
+	 * @param transformType
+	 *            the type of transform - use null not creating a transform
+	 *            animation
+	 * @return an new animation element
+	 */
+	public static SVGOMAnimationElement createAnimationElement(
+			SVGGraphController graphController, String elementType,
+			String attribute, String transformType) {
+		SVGOMAnimationElement animationElement = (SVGOMAnimationElement) graphController
+				.createElement(elementType);
+		animationElement.setAttribute(SMIL_ATTRIBUTE_NAME_ATTRIBUTE, attribute);
+		if (transformType != null)
+			animationElement.setAttribute(SVG_TYPE_ATTRIBUTE, transformType);
+		animationElement.setAttribute(SMIL_FILL_ATTRIBUTE, SMIL_FREEZE_VALUE);
+		return animationElement;
+	}
+
+	/**
+	 * Adds an animation to the SVG element and starts the animation.
+	 *
+	 * @param animate
+	 *            that animation element
+	 * @param element
+	 *            the element to animate
+	 * @param duration
+	 *            the duration of the animation in milliseconds
+	 * @param from
+	 *            the starting point for the animation, can be null
+	 * @param to
+	 *            the end point for the animation, cannot be null
+	 */
+	public static void animate(SVGOMAnimationElement animate, SVGElement element, int duration,
+			String from, String to) {
+		animate.setAttribute(SMIL_DUR_ATTRIBUTE, duration + "ms");
+		if (from != null)
+			animate.setAttribute(SMIL_FROM_ATTRIBUTE, from);
+		animate.setAttribute(SMIL_TO_ATTRIBUTE, to);
+		element.appendChild(animate);
+		try {
+			animate.beginElement();
+		} catch (NullPointerException e) {
+		}
+	}
+
+	/**
+	 * Adjusts the length of <code>pointList</code> by adding or removing points
+	 * to make the length equal to <code>size</code>. If <code>pointList</code>
+	 * is shorter than <code>size</code> the last point is repeated. If
+	 * <code>pointList</code> is longer than <code>size</code> points at the end
+	 * of the list are removed.
+	 *
+	 * @param pointList
+	 *            the path to adjust
+	 * @param size
+	 *            the required size for <code>pointList</code>
+	 */
+	public static void adjustPathLength(List<Point> pointList, int size) {
+		if (pointList.size() < size) {
+			Point lastPoint = pointList.get(pointList.size() - 1);
+			for (int i = pointList.size(); i < size; i++)
+				pointList.add(lastPoint);
+		} else if (pointList.size() > size) {
+			for (int i = pointList.size(); i > size; i--)
+				pointList.remove(i - 1);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGEventListener.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGEventListener.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGEventListener.java
new file mode 100644
index 0000000..95b4181
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGEventListener.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg.event;
+
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.screenToDocument;
+import net.sf.taverna.t2.workbench.models.graph.GraphElement;
+
+import org.apache.batik.dom.svg.SVGOMPoint;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.MouseEvent;
+import org.w3c.dom.svg.SVGLocatable;
+
+/**
+ * Abstract superclass for SVG event listeners.
+ * 
+ * @author David Withers
+ */
+public abstract class SVGEventListener implements EventListener {
+	protected GraphElement graphElement;
+
+	public SVGEventListener(GraphElement graphElement) {
+		this.graphElement = graphElement;
+	}
+
+	protected abstract void event(SVGOMPoint point, MouseEvent evt);
+
+	@Override
+	public final void handleEvent(Event evt) {
+		if (evt instanceof MouseEvent) {
+			MouseEvent me = (MouseEvent) evt;
+			SVGOMPoint point = screenToDocument((SVGLocatable) me.getTarget(),
+					new SVGOMPoint(me.getClientX(), me.getClientY()));
+			event(point, me);
+			evt.stopPropagation();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseClickEventListener.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseClickEventListener.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseClickEventListener.java
new file mode 100644
index 0000000..0c13be3
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseClickEventListener.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg.event;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphElement;
+
+import org.apache.batik.dom.svg.SVGOMPoint;
+import org.w3c.dom.events.MouseEvent;
+
+/**
+ * SVG event listener for handling mouse click events.
+ * 
+ * @author David Withers
+ */
+public class SVGMouseClickEventListener extends SVGEventListener {
+	public SVGMouseClickEventListener(GraphElement graphElement) {
+		super(graphElement);
+	}
+
+	@Override
+	protected void event(SVGOMPoint point, MouseEvent evt) {
+		graphElement.getEventManager().mouseClicked(graphElement,
+				evt.getButton(), evt.getAltKey(), evt.getCtrlKey(),
+				evt.getMetaKey(), (int) point.getX(), (int) point.getY(),
+				evt.getScreenX(), evt.getScreenY());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseDownEventListener.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseDownEventListener.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseDownEventListener.java
new file mode 100644
index 0000000..bd69506
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseDownEventListener.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg.event;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphElement;
+
+import org.apache.batik.dom.svg.SVGOMPoint;
+import org.w3c.dom.events.MouseEvent;
+
+/**
+ * SVG event listener for handling mouse button down events.
+ * 
+ * @author David Withers
+ */
+public class SVGMouseDownEventListener extends SVGEventListener {
+	public SVGMouseDownEventListener(GraphElement graphElement) {
+		super(graphElement);
+	}
+
+	@Override
+	protected void event(SVGOMPoint point, MouseEvent evt) {
+		graphElement.getEventManager().mouseDown(graphElement, evt.getButton(),
+				evt.getAltKey(), evt.getCtrlKey(), evt.getMetaKey(),
+				(int) point.getX(), (int) point.getY(), evt.getScreenX(),
+				evt.getScreenY());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseMovedEventListener.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseMovedEventListener.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseMovedEventListener.java
new file mode 100644
index 0000000..6ae5d50
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseMovedEventListener.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg.event;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphElement;
+
+import org.apache.batik.dom.svg.SVGOMPoint;
+import org.w3c.dom.events.MouseEvent;
+
+/**
+ * SVG event listener for handling mouse movement events.
+ * 
+ * @author David Withers
+ */
+public class SVGMouseMovedEventListener extends SVGEventListener {
+	public SVGMouseMovedEventListener(GraphElement graphElement) {
+		super(graphElement);
+	}
+
+	@Override
+	protected void event(SVGOMPoint point, MouseEvent mouseEvent) {
+		graphElement.getEventManager().mouseMoved(graphElement,
+				mouseEvent.getButton(), mouseEvent.getAltKey(),
+				mouseEvent.getCtrlKey(), mouseEvent.getMetaKey(),
+				(int) point.getX(), (int) point.getY(),
+				mouseEvent.getScreenX(), mouseEvent.getScreenY());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOutEventListener.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOutEventListener.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOutEventListener.java
new file mode 100644
index 0000000..32714a6
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOutEventListener.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg.event;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphElement;
+
+import org.apache.batik.dom.svg.SVGOMPoint;
+import org.w3c.dom.events.MouseEvent;
+
+/**
+ * SVG event listener for handling mouse button up events.
+ * 
+ * @author David Withers
+ */
+public class SVGMouseOutEventListener extends SVGEventListener {
+	public SVGMouseOutEventListener(GraphElement graphElement) {
+		super(graphElement);
+	}
+
+	@Override
+	protected void event(SVGOMPoint point, MouseEvent mouseEvent) {
+		graphElement.getEventManager().mouseOut(graphElement,
+				mouseEvent.getButton(), mouseEvent.getAltKey(),
+				mouseEvent.getCtrlKey(), mouseEvent.getMetaKey(),
+				(int) point.getX(), (int) point.getY(),
+				mouseEvent.getScreenX(), mouseEvent.getScreenY());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOverEventListener.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOverEventListener.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOverEventListener.java
new file mode 100644
index 0000000..1c5f9a4
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOverEventListener.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg.event;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphElement;
+
+import org.apache.batik.dom.svg.SVGOMPoint;
+import org.w3c.dom.events.MouseEvent;
+
+/**
+ * SVG event listener for handling mouse button up events.
+ * 
+ * @author David Withers
+ */
+public class SVGMouseOverEventListener extends SVGEventListener {
+	public SVGMouseOverEventListener(GraphElement graphElement) {
+		super(graphElement);
+	}
+
+	@Override
+	protected void event(SVGOMPoint point, MouseEvent mouseEvent) {
+		graphElement.getEventManager().mouseOver(graphElement,
+				mouseEvent.getButton(), mouseEvent.getAltKey(),
+				mouseEvent.getCtrlKey(), mouseEvent.getMetaKey(),
+				(int) point.getX(), (int) point.getY(),
+				mouseEvent.getScreenX(), mouseEvent.getScreenY());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseUpEventListener.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseUpEventListener.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseUpEventListener.java
new file mode 100644
index 0000000..492ecc2
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseUpEventListener.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph.svg.event;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphElement;
+
+import org.apache.batik.dom.svg.SVGOMPoint;
+import org.w3c.dom.events.MouseEvent;
+
+/**
+ * SVG event listener for handling mouse button up events.
+ * 
+ * @author David Withers
+ */
+public class SVGMouseUpEventListener extends SVGEventListener {
+	public SVGMouseUpEventListener(GraphElement graphElement) {
+		super(graphElement);
+	}
+
+	@Override
+	protected void event(SVGOMPoint point, MouseEvent mouseEvent) {
+		graphElement.getEventManager().mouseUp(graphElement,
+				mouseEvent.getButton(), mouseEvent.getAltKey(),
+				mouseEvent.getCtrlKey(), mouseEvent.getMetaKey(),
+				(int) point.getX(), (int) point.getY(),
+				mouseEvent.getScreenX(), mouseEvent.getScreenY());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/jjtree/NamedNode.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/jjtree/NamedNode.java b/taverna-graph-model/src/main/jjtree/NamedNode.java
new file mode 100644
index 0000000..da92a97
--- /dev/null
+++ b/taverna-graph-model/src/main/jjtree/NamedNode.java
@@ -0,0 +1,65 @@
+package net.sf.taverna.t2.workbench.models.graph.dot;
+
+public class NamedNode  {
+	
+	protected String name, value, port;
+
+	/**
+	 * Returns the name.
+	 * 
+	 * @return the name
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * Sets the value of name.
+	 * 
+	 * @param name
+	 *            the new value for name
+	 */
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	/**
+	 * Returns the value.
+	 * 
+	 * @return the value
+	 */
+	public String getValue() {
+		return value;
+	}
+
+	/**
+	 * Sets the value of value.
+	 * 
+	 * @param value
+	 *            the new value for value
+	 */
+	public void setValue(String value) {
+		this.value = value;
+	}
+
+	/**
+	 * Returns the port.
+	 * 
+	 * @return the port
+	 */
+	public String getPort() {
+		return port;
+	}
+
+	/**
+	 * Sets the value of port.
+	 * 
+	 * @param port
+	 *            the new value for port
+	 */
+	public void setPort(String port) {
+		this.port = port;
+	}
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/main/jjtree/dotparser.jjt
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/jjtree/dotparser.jjt b/taverna-graph-model/src/main/jjtree/dotparser.jjt
new file mode 100644
index 0000000..001450b
--- /dev/null
+++ b/taverna-graph-model/src/main/jjtree/dotparser.jjt
@@ -0,0 +1,289 @@
+/**
+ * JavaCC grammar for parsing Graphviz DOT files.
+ * Written by Lynn Monson at lm@lmonson.com
+ * Modified by David Withers
+ */
+options {
+  STATIC = false;
+  UNICODE_INPUT = true;
+    VISITOR = true;
+    MULTI=true;
+    NODE_EXTENDS="NamedNode";
+}
+
+/*===========================================================================================
+  Parser java class
+  ===========================================================================================*/
+PARSER_BEGIN(DOTParser)
+package net.sf.taverna.t2.workbench.models.graph.dot;
+import java.io.*;
+
+class DOTParser {
+
+  public static void parseDot(Reader r) throws IOException, ParseException {
+    new DOTParser(r).parse().dump(" ");
+  }
+
+  public static void main(String[] args) throws Exception
+  {
+    parseDot(new FileReader(args[0]));
+  }
+  
+}
+PARSER_END(DOTParser)
+
+
+// whitespace
+SKIP :  {  " " | "\t" | "\n" | "\r" | "\f" }
+
+// comments
+MORE :
+{
+  "//" : IN_SINGLE_LINE_COMMENT
+|
+  "/*" : IN_MULTI_LINE_COMMENT
+}
+
+<IN_SINGLE_LINE_COMMENT>
+SPECIAL_TOKEN :
+{
+  <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" > : DEFAULT
+}
+
+<IN_MULTI_LINE_COMMENT>
+SPECIAL_TOKEN :
+{
+  <MULTI_LINE_COMMENT: "*/" > : DEFAULT
+}
+
+<IN_SINGLE_LINE_COMMENT,IN_MULTI_LINE_COMMENT>
+MORE : {   < ~[] >  }
+
+// Case insensitive keywords
+TOKEN [IGNORE_CASE] : {
+    <DIGRAPH: ( "digraph" )>
+  | <EDGE: ( "edge" )>
+  | <GRAPH: ( "graph" )>
+  | <NODE: ( "node" )>
+  | <STRICT: ( "strict" )>
+  | <SUBGRAPH: ( "subgraph" )>
+}
+
+// All other tokens
+TOKEN:
+{
+    // -------------------------------------------
+    // Various pieces of syntax as lexical tokens
+    // -------------------------------------------
+      <EQ: "=">
+    | <LBRACE: "{">
+    | <RBRACE: "}">
+    | <EDGE_UNDIRECTED: "--">
+    | <EDGE_DIRECTED: "->">
+    | <LBRACKET: "[">
+    | <RBRACKET: "]">
+    | <COMMA: ",">
+    | <SEMICOLON: ";">
+    | <COLON: ":">
+
+
+    // -------------------------------------------
+    // Identifiers
+    // -------------------------------------------
+  | <ID: <$HtmlString> | <$Number> | <$UnquotedString> | <$QuotedString> >
+  | <HTML: <$HtmlString>>
+  | <#$HtmlString: ( "<" ( "<" (~[">"])* ">" (~["<", ">"])* )* ">" ) >
+
+      // -------------------------------------------
+      // Character definitions (taken from xpath)
+      // -------------------------------------------
+
+  |	<#$BaseChar:
+		  ["\u0041"-"\u005A"] | ["\u0061"-"\u007A"] | ["\u00C0"-"\u00D6"] | ["\u00D8"-"\u00F6"]
+		| ["\u00F8"-"\u00FF"] | ["\u0100"-"\u0131"] | ["\u0134"-"\u013E"] | ["\u0141"-"\u0148"]
+		| ["\u014A"-"\u017E"] | ["\u0180"-"\u01C3"] | ["\u01CD"-"\u01F0"] | ["\u01F4"-"\u01F5"]
+		| ["\u01FA"-"\u0217"] | ["\u0250"-"\u02A8"] | ["\u02BB"-"\u02C1"] | "\u0386" | ["\u0388"-"\u038A"]
+		| "\u038C" | ["\u038E"-"\u03A1"] | ["\u03A3"-"\u03CE"] | ["\u03D0"-"\u03D6"] | "\u03DA"
+		| "\u03DC" | "\u03DE" | "\u03E0" | ["\u03E2"-"\u03F3"] | ["\u0401"-"\u040C"] | ["\u040E"-"\u044F"]
+		| ["\u0451"-"\u045C"] | ["\u045E"-"\u0481"] | ["\u0490"-"\u04C4"] | ["\u04C7"-"\u04C8"]
+		| ["\u04CB"-"\u04CC"] | ["\u04D0"-"\u04EB"] | ["\u04EE"-"\u04F5"] | ["\u04F8"-"\u04F9"]
+		| ["\u0531"-"\u0556"] | "\u0559" | ["\u0561"-"\u0586"] | ["\u05D0"-"\u05EA"] | ["\u05F0"-"\u05F2"]
+		| ["\u0621"-"\u063A"] | ["\u0641"-"\u064A"] | ["\u0671"-"\u06B7"] | ["\u06BA"-"\u06BE"]
+		| ["\u06C0"-"\u06CE"] | ["\u06D0"-"\u06D3"] | "\u06D5" | ["\u06E5"-"\u06E6"] | ["\u0905"-"\u0939"]
+		| "\u093D" | ["\u0958"-"\u0961"] | ["\u0985"-"\u098C"] | ["\u098F"-"\u0990"] | ["\u0993"-"\u09A8"]
+		| ["\u09AA"-"\u09B0"] | "\u09B2" | ["\u09B6"-"\u09B9"] | ["\u09DC"-"\u09DD"] | ["\u09DF"-"\u09E1"]
+		| ["\u09F0"-"\u09F1"] | ["\u0A05"-"\u0A0A"] | ["\u0A0F"-"\u0A10"] | ["\u0A13"-"\u0A28"]
+		| ["\u0A2A"-"\u0A30"] | ["\u0A32"-"\u0A33"] | ["\u0A35"-"\u0A36"] | ["\u0A38"-"\u0A39"]
+		| ["\u0A59"-"\u0A5C"] | "\u0A5E" | ["\u0A72"-"\u0A74"] | ["\u0A85"-"\u0A8B"] | "\u0A8D"
+		| ["\u0A8F"-"\u0A91"] | ["\u0A93"-"\u0AA8"] | ["\u0AAA"-"\u0AB0"] | ["\u0AB2"-"\u0AB3"]
+		| ["\u0AB5"-"\u0AB9"] | "\u0ABD" | "\u0AE0" | ["\u0B05"-"\u0B0C"] | ["\u0B0F"-"\u0B10"]
+		| ["\u0B13"-"\u0B28"] | ["\u0B2A"-"\u0B30"] | ["\u0B32"-"\u0B33"] | ["\u0B36"-"\u0B39"]
+		| "\u0B3D" | ["\u0B5C"-"\u0B5D"] | ["\u0B5F"-"\u0B61"] | ["\u0B85"-"\u0B8A"]
+		| ["\u0B8E"-"\u0B90"] | ["\u0B92"-"\u0B95"] | ["\u0B99"-"\u0B9A"] | "\u0B9C" | ["\u0B9E"-"\u0B9F"]
+		| ["\u0BA3"-"\u0BA4"] | ["\u0BA8"-"\u0BAA"] | ["\u0BAE"-"\u0BB5"] | ["\u0BB7"-"\u0BB9"]
+		| ["\u0C05"-"\u0C0C"] | ["\u0C0E"-"\u0C10"] | ["\u0C12"-"\u0C28"] | ["\u0C2A"-"\u0C33"]
+		| ["\u0C35"-"\u0C39"] | ["\u0C60"-"\u0C61"] | ["\u0C85"-"\u0C8C"] | ["\u0C8E"-"\u0C90"]
+		| ["\u0C92"-"\u0CA8"] | ["\u0CAA"-"\u0CB3"] | ["\u0CB5"-"\u0CB9"] | "\u0CDE" | ["\u0CE0"-"\u0CE1"]
+		| ["\u0D05"-"\u0D0C"] | ["\u0D0E"-"\u0D10"] | ["\u0D12"-"\u0D28"] | ["\u0D2A"-"\u0D39"]
+		| ["\u0D60"-"\u0D61"] | ["\u0E01"-"\u0E2E"] | "\u0E30" | ["\u0E32"-"\u0E33"] | ["\u0E40"-"\u0E45"]
+		| ["\u0E81"-"\u0E82"] | "\u0E84" | ["\u0E87"-"\u0E88"] | "\u0E8A" | "\u0E8D" | ["\u0E94"-"\u0E97"]
+		| ["\u0E99"-"\u0E9F"] | ["\u0EA1"-"\u0EA3"] | "\u0EA5" | "\u0EA7" | ["\u0EAA"-"\u0EAB"]
+		| ["\u0EAD"-"\u0EAE"] | "\u0EB0" | ["\u0EB2"-"\u0EB3"] | "\u0EBD" | ["\u0EC0"-"\u0EC4"]
+		| ["\u0F40"-"\u0F47"] | ["\u0F49"-"\u0F69"] | ["\u10A0"-"\u10C5"] | ["\u10D0"-"\u10F6"] | "\u1100"
+		| ["\u1102"-"\u1103"] | ["\u1105"-"\u1107"] | "\u1109" | ["\u110B"-"\u110C"] | ["\u110E"-"\u1112"]
+		| "\u113C" | "\u113E" | "\u1140" | "\u114C" | "\u114E" | "\u1150" | ["\u1154"-"\u1155"] | "\u1159"
+		| ["\u115F"-"\u1161"] | "\u1163" | "\u1165" | "\u1167" | "\u1169" | ["\u116D"-"\u116E"]
+		| ["\u1172"-"\u1173"] | "\u1175" | "\u119E" | "\u11A8" | "\u11AB" | ["\u11AE"-"\u11AF"]
+		| ["\u11B7"-"\u11B8"] | "\u11BA" | ["\u11BC"-"\u11C2"] | "\u11EB" | "\u11F0" | "\u11F9"
+		| ["\u1E00"-"\u1E9B"] | ["\u1EA0"-"\u1EF9"] | ["\u1F00"-"\u1F15"] | ["\u1F18"-"\u1F1D"]
+		| ["\u1F20"-"\u1F45"] | ["\u1F48"-"\u1F4D"] | ["\u1F50"-"\u1F57"] | "\u1F59" | "\u1F5B" | "\u1F5D"
+		| ["\u1F5F"-"\u1F7D"] | ["\u1F80"-"\u1FB4"] | ["\u1FB6"-"\u1FBC"] | "\u1FBE" | ["\u1FC2"-"\u1FC4"]
+		| ["\u1FC6"-"\u1FCC"] | ["\u1FD0"-"\u1FD3"] | ["\u1FD6"-"\u1FDB"] | ["\u1FE0"-"\u1FEC"]
+		| ["\u1FF2"-"\u1FF4"] | ["\u1FF6"-"\u1FFC"] | "\u2126" | ["\u212A"-"\u212B"] | "\u212E"
+		| ["\u2180"-"\u2182"] | ["\u3041"-"\u3094"] | ["\u30A1"-"\u30FA"] | ["\u3105"-"\u312C"]
+		| ["\uAC00"-"\uD7A3"] >
+|	<#$Ideographic :    ["\u4E00"-"\u9FA5"] | "\u3007" | ["\u3021"-"\u3029"] >
+|	<#CombiningChar :
+		  ["\u0300"-"\u0345"] | ["\u0360"-"\u0361"] | ["\u0483"-"\u0486"] | ["\u0591"-"\u05A1"]
+		| ["\u05A3"-"\u05B9"] | ["\u05BB"-"\u05BD"] | "\u05BF" | ["\u05C1"-"\u05C2"] | "\u05C4"
+		| ["\u064B"-"\u0652"] | "\u0670" | ["\u06D6"-"\u06DC"] | ["\u06DD"-"\u06DF"]
+		| ["\u06E0"-"\u06E4"] | ["\u06E7"-"\u06E8"] | ["\u06EA"-"\u06ED"] | ["\u0901"-"\u0903"]
+		| "\u093C" | ["\u093E"-"\u094C"] | "\u094D" | ["\u0951"-"\u0954"] | ["\u0962"-"\u0963"]
+		| ["\u0981"-"\u0983"] | "\u09BC" | "\u09BE" | "\u09BF" | ["\u09C0"-"\u09C4"] | ["\u09C7"-"\u09C8"]
+		| ["\u09CB"-"\u09CD"] | "\u09D7" | ["\u09E2"-"\u09E3"] | "\u0A02" | "\u0A3C" | "\u0A3E"
+		| "\u0A3F" | ["\u0A40"-"\u0A42"] | ["\u0A47"-"\u0A48"] | ["\u0A4B"-"\u0A4D"] | ["\u0A70"-"\u0A71"]
+		| ["\u0A81"-"\u0A83"] | "\u0ABC" | ["\u0ABE"-"\u0AC5"] | ["\u0AC7"-"\u0AC9"] | ["\u0ACB"-"\u0ACD"]
+		| ["\u0B01"-"\u0B03"] | "\u0B3C" | ["\u0B3E"-"\u0B43"] | ["\u0B47"-"\u0B48"] | ["\u0B4B"-"\u0B4D"]
+		| ["\u0B56"-"\u0B57"] | ["\u0B82"-"\u0B83"] | ["\u0BBE"-"\u0BC2"] | ["\u0BC6"-"\u0BC8"]
+		| ["\u0BCA"-"\u0BCD"] | "\u0BD7" | ["\u0C01"-"\u0C03"] | ["\u0C3E"-"\u0C44"] | ["\u0C46"-"\u0C48"]
+		| ["\u0C4A"-"\u0C4D"] | ["\u0C55"-"\u0C56"] | ["\u0C82"-"\u0C83"] | ["\u0CBE"-"\u0CC4"]
+		| ["\u0CC6"-"\u0CC8"] | ["\u0CCA"-"\u0CCD"] | ["\u0CD5"-"\u0CD6"] | ["\u0D02"-"\u0D03"]
+		| ["\u0D3E"-"\u0D43"] | ["\u0D46"-"\u0D48"] | ["\u0D4A"-"\u0D4D"] | "\u0D57" | "\u0E31"
+		| ["\u0E34"-"\u0E3A"] | ["\u0E47"-"\u0E4E"] | "\u0EB1" | ["\u0EB4"-"\u0EB9"]
+		| ["\u0EBB"-"\u0EBC"] | ["\u0EC8"-"\u0ECD"] | ["\u0F18"-"\u0F19"] | "\u0F35" | "\u0F37" | "\u0F39"
+		| "\u0F3E" | "\u0F3F" | ["\u0F71"-"\u0F84"] | ["\u0F86"-"\u0F8B"] | ["\u0F90"-"\u0F95"] | "\u0F97"
+		| ["\u0F99"-"\u0FAD"] | ["\u0FB1"-"\u0FB7"] | "\u0FB9" | ["\u20D0"-"\u20DC"] | "\u20E1"
+		| ["\u302A"-"\u302F"] | "\u3099" | "\u309A" >
+|	<#$Digit:
+		  ["\u0030"-"\u0039"] | ["\u0660"-"\u0669"] | ["\u06F0"-"\u06F9"] | ["\u0966"-"\u096F"]
+		| ["\u09E6"-"\u09EF"] | ["\u0A66"-"\u0A6F"] | ["\u0AE6"-"\u0AEF"] | ["\u0B66"-"\u0B6F"]
+		| ["\u0BE7"-"\u0BEF"] | ["\u0C66"-"\u0C6F"] | ["\u0CE6"-"\u0CEF"] | ["\u0D66"-"\u0D6F"]
+		| ["\u0E50"-"\u0E59"] | ["\u0ED0"-"\u0ED9"] | ["\u0F20"-"\u0F29"] >
+|	<#Extender :
+		  "\u00B7" | "\u02D0" | "\u02D1" | "\u0387" | "\u0640" | "\u0E46" | "\u0EC6" | "\u3005"
+		| ["\u3031"-"\u3035"] | ["\u309D"-"\u309E"] | ["\u30FC"-"\u30FE"] >
+  | <#$EscapedCharacter: "\\" (["\u0020"-"\u007E", "\n", "\r" ] | "\r\n" ) >
+  | <#$NotWhitespaceNotQuoteNotEscape:~["\"","\\","\n","\r"]>
+  | <#$Letter: <$BaseChar> | <$Ideographic> >
+  | <#$Number: ("-")? ("." (<$Digit>)+) | ((<$Digit>)+ ("." (<$Digit>)*)?)>
+  | <#$UnquotedString:  (<$Letter>|"_"|<$Digit>)+       >
+  | <#$QuotedString: "\""  (<$NotWhitespaceNotQuoteNotEscape> | <$EscapedCharacter>)* "\""  >
+}
+
+/*===========================================================================================
+  DOT Grammar starts here
+  ===========================================================================================*/
+SimpleNode parse() #Parse :
+{}
+{
+    graph()
+    {
+        return jjtThis;
+        }
+}
+
+void graph() #Graph :
+{Token t;}
+{
+    [<STRICT>] (<GRAPH>|<DIGRAPH>) t=<ID>{jjtThis.setName(t.image);} <LBRACE> stmt_list() <RBRACE>  <EOF>
+}
+
+void stmt_list() #StatementList :
+{}
+{
+    stmt() [<SEMICOLON>] [stmt_list()]
+}
+
+
+void stmt() #Statement :
+{}
+{
+  ( LOOKAHEAD(edge_stmt()) edge_stmt() | LOOKAHEAD(2) subgraph() | LOOKAHEAD(2) node_stmt() | LOOKAHEAD(2) attr_stmt() | LOOKAHEAD(2) (<ID> <EQ> <ID>)  )
+
+}
+
+/*
+void ideq_stmt() :
+{}
+{
+    <ID> <EQ> <ID>
+}
+*/
+
+void attr_stmt() #AttributeStatement :
+{Token t;}
+{
+    (t=<GRAPH>{jjtThis.setName(t.image);} | t=<NODE>{jjtThis.setName(t.image);} | t=<EDGE>{jjtThis.setName(t.image);}) attr_list()
+}
+
+void node_stmt() #NodeStatement :
+{}
+{
+    node_id(jjtThis) [attr_list()]
+}
+
+void node_id(NamedNode namedNode) #NodeId :
+{Token t;}
+{
+    t=<ID>{namedNode.setName(t.image);} [port(namedNode)]
+}
+
+void port(NamedNode namedNode) #Port :
+{Token t;}
+{
+   <COLON> (LOOKAHEAD(2) t=<ID>{namedNode.setPort(t.image);} <COLON> <ID> | <ID>)
+}
+
+void edge_stmt() #EdgeStatement :
+{}
+{
+    (node_id(jjtThis)| subgraph()) edgeRHS() [attr_list()]
+}
+
+void subgraph() #Subgraph :
+{Token t;}
+{
+    (
+        LOOKAHEAD(2)
+        ([<SUBGRAPH>  [t=<ID>{jjtThis.setName(t.image);}]] <LBRACE> stmt_list() <RBRACE>)
+            |
+        (<SUBGRAPH> t=<ID>{jjtThis.setName(t.image);})
+    )
+
+}
+
+void edgeRHS() #EdgeRHS :
+{}
+{
+    edgeop() (node_id(jjtThis) | subgraph()) [ edgeRHS() ]
+}
+
+void edgeop() #void :
+{}
+{
+    <EDGE_UNDIRECTED> | <EDGE_DIRECTED>
+}
+
+
+void attr_list() #AttributeList :
+{}
+{
+    <LBRACKET> a_list() <RBRACKET>
+}
+
+void a_list() #AList :
+{Token t;}
+{
+    t=<ID>{jjtThis.setName(t.image);} [<EQ> t=<ID>{jjtThis.setValue(t.image);}] [<COMMA>] [a_list()]
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphControllerTest.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphControllerTest.java b/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphControllerTest.java
new file mode 100644
index 0000000..cb76b97
--- /dev/null
+++ b/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphControllerTest.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public class GraphControllerTest {
+
+	Workflow dataflow;
+
+	GraphController graphController;
+
+	@Before
+	public void setUp() throws Exception {
+//		System.setProperty("raven.eclipse", "true");
+//		setUpRavenRepository();
+//		dataflow = WorkflowModelTranslator.doTranslation(loadScufl("nested_iteration.xml"));
+		graphController = new GraphController(dataflow, null, false, null, null, null, null) {
+
+			@Override
+			public GraphEdge createGraphEdge() {
+				return new GraphEdge(this);
+			}
+
+			@Override
+			public Graph createGraph() {
+				return new Graph(this);
+			}
+
+			@Override
+			public GraphNode createGraphNode() {
+				return new GraphNode(this);
+			}
+
+			@Override
+			public void redraw() {
+
+			}
+
+		};
+		graphController.setPortStyle(PortStyle.NONE);
+	}
+
+	@Test
+	@Ignore
+	public void testGenerateGraph() throws IOException, InterruptedException {
+		Graph graph = graphController.generateGraph();
+		assertEquals(5, graph.getNodes().size());
+		assertEquals(9, graph.getEdges().size());
+		assertEquals(1, graph.getSubgraphs().size());
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphEdgeTest.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphEdgeTest.java b/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphEdgeTest.java
new file mode 100644
index 0000000..10a3c20
--- /dev/null
+++ b/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphEdgeTest.java
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.workbench.models.graph;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import net.sf.taverna.t2.workbench.models.graph.GraphEdge.ArrowStyle;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class GraphEdgeTest {
+
+	private GraphEdge edge;
+
+	private GraphNode source;
+
+	private GraphNode sink;
+
+	private ArrowStyle arrowHeadStyle;
+
+	private ArrowStyle arrowTailStyle;
+
+	private GraphController graphController;
+
+	@Before
+	public void setUp() throws Exception {
+		source = new GraphNode(graphController);
+		sink = new GraphNode(graphController);
+		arrowHeadStyle = ArrowStyle.DOT;
+		arrowTailStyle = ArrowStyle.NORMAL;
+		edge = new GraphEdge(graphController);
+		edge.setArrowHeadStyle(arrowHeadStyle);
+		edge.setArrowTailStyle(arrowTailStyle);
+		edge.setSink(sink);
+		edge.setSource(source);
+	}
+
+	@Test
+	public void testEdge() {
+		edge = new GraphEdge(graphController);
+		assertNull(edge.getSource());
+		assertNull(edge.getSink());
+		assertNull(edge.getLabel());
+	}
+
+	@Test
+	public void testEdgeNodeNode() {
+		edge = new GraphEdge(graphController);
+		edge.setSource(source);
+		edge.setSink(sink);
+		assertEquals(source, edge.getSource());
+		assertEquals(sink, edge.getSink());
+		assertNull(edge.getLabel());
+	}
+
+	@Test
+	public void testGetSource() {
+		assertEquals(source, edge.getSource());
+	}
+
+	@Test
+	public void testSetSource() {
+		GraphNode node = new GraphNode(graphController);
+		edge.setSource(node);
+		assertEquals(node, edge.getSource());
+		edge.setSource(null);
+		assertNull(edge.getSource());
+	}
+
+	@Test
+	public void testGetSink() {
+		assertEquals(sink, edge.getSink());
+	}
+
+	@Test
+	public void testSetSink() {
+		GraphNode node = new GraphNode(graphController);
+		edge.setSink(node);
+		assertEquals(node, edge.getSink());
+		edge.setSink(null);
+		assertNull(edge.getSink());
+	}
+
+	@Test
+	public void testGetArrowHeadStyle() {
+		assertEquals(arrowHeadStyle, edge.getArrowHeadStyle());
+	}
+
+	@Test
+	public void testSetArrowHeadStyle() {
+		edge.setArrowHeadStyle(ArrowStyle.DOT);
+		assertEquals(ArrowStyle.DOT, edge.getArrowHeadStyle());
+		edge.setArrowHeadStyle(null);
+		assertNull(edge.getArrowHeadStyle());
+	}
+
+	@Test
+	public void testGetArrowTailStyle() {
+		assertEquals(arrowTailStyle, edge.getArrowTailStyle());
+	}
+
+	@Test
+	public void testSetArrowTailStyle() {
+		edge.setArrowTailStyle(ArrowStyle.NORMAL);
+		assertEquals(ArrowStyle.NORMAL, edge.getArrowTailStyle());
+		edge.setArrowTailStyle(null);
+		assertNull(edge.getArrowTailStyle());
+	}
+
+}