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:39 UTC

[25/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-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context-osgi.xml b/taverna-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context-osgi.xml
new file mode 100644
index 0000000..e3db399
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context-osgi.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:beans="http://www.springframework.org/schema/beans"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd
+                      http://www.springframework.org/schema/osgi
+                      http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+
+	<service ref="IterationStrategyContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+
+	<reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" />
+	<reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context.xml
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context.xml b/taverna-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context.xml
new file mode 100644
index 0000000..f0bc464
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<bean id="IterationStrategyContextualViewFactory" class="net.sf.taverna.t2.workbench.iterationstrategy.contextview.IterationStrategyContextualViewFactory">
+			<property name="editManager" ref="editManager" />
+			<property name="fileManager" ref="fileManager" />
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/crossproducticon.png
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/crossproducticon.png b/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/crossproducticon.png
new file mode 100644
index 0000000..4627fc8
Binary files /dev/null and b/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/crossproducticon.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/dotproducticon.png
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/dotproducticon.png b/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/dotproducticon.png
new file mode 100644
index 0000000..c269883
Binary files /dev/null and b/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/dotproducticon.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/leafnodeicon.png
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/leafnodeicon.png b/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/leafnodeicon.png
new file mode 100644
index 0000000..36808c6
Binary files /dev/null and b/taverna-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/leafnodeicon.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/test/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/RunIterationStrategyEditor.java
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/test/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/RunIterationStrategyEditor.java b/taverna-iteration-strategy-ui/src/test/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/RunIterationStrategyEditor.java
new file mode 100644
index 0000000..1862233
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/test/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/RunIterationStrategyEditor.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.iterationstrategy.editor;
+
+import javax.swing.JFrame;
+
+import net.sf.taverna.t2.workflowmodel.processor.iteration.NamedInputPortNode;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.impl.IterationStrategyImpl;
+
+public class RunIterationStrategyEditor {
+
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		IterationStrategyImpl iterationStrategyImpl = new IterationStrategyImpl();
+		NamedInputPortNode fishPort = new NamedInputPortNode("fish", 2);
+		NamedInputPortNode otherPort = new NamedInputPortNode("other", 0);
+		NamedInputPortNode soupPort = new NamedInputPortNode("soup", 1);
+		iterationStrategyImpl.addInput(fishPort);
+		iterationStrategyImpl.addInput(soupPort);
+		iterationStrategyImpl.addInput(otherPort);
+
+		iterationStrategyImpl.connectDefault(otherPort);
+		iterationStrategyImpl.connectDefault(fishPort);
+		iterationStrategyImpl.connectDefault(soupPort);
+		
+		IterationStrategyEditorControl editorControl = new IterationStrategyEditorControl(iterationStrategyImpl);
+		
+		JFrame frame = new JFrame("List handling editor");
+		frame.add(editorControl);
+		frame.setSize(500,400);
+		frame.setVisible(true);
+		
+		
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/pom.xml b/taverna-loop-ui/pom.xml
new file mode 100644
index 0000000..36130b4
--- /dev/null
+++ b/taverna-loop-ui/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.apache.taverna.workbench</groupId>
+		<artifactId>taverna-workbench</artifactId>
+		<version>3.1.0-incubating-SNAPSHOT</version>
+	</parent>
+    <artifactId>loop-ui</artifactId>
+    <packaging>bundle</packaging>
+    <name>Loop layer contextual view</name>
+    <dependencies>
+        <dependency>
+            <groupId>${project.parent.groupId}</groupId>
+            <artifactId>contextual-views-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>${project.parent.groupId}</groupId>
+            <artifactId>file-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.parent.groupId}</groupId>
+            <artifactId>edits-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.parent.groupId}</groupId>
+            <artifactId>helper-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>net.sf.taverna.t2.ui-activities</groupId>
+            <artifactId>beanshell-activity-ui</artifactId>
+            <version>${t2.ui.activities.version}</version>
+        </dependency>
+
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>edits-impl</artifactId>
+			<version>${project.parent.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>file-impl</artifactId>
+			<version>${project.parent.version}</version>
+			<scope>test</scope>
+		</dependency>
+
+        <dependency>
+            <groupId>${project.parent.groupId}</groupId>
+            <artifactId>contextual-views-impl</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>test</scope>
+        </dependency>
+            <dependency>
+            <groupId>${project.parent.groupId}</groupId>
+            <artifactId>selection-impl</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+	</dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/ActivityGenerator.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/ActivityGenerator.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/ActivityGenerator.java
new file mode 100644
index 0000000..9bd68e8
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/ActivityGenerator.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (C) 2008 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.loop;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import net.sf.taverna.t2.workbench.loop.comparisons.Comparison;
+import net.sf.taverna.t2.workbench.loop.comparisons.EqualTo;
+import net.sf.taverna.t2.workbench.loop.comparisons.IsGreaterThan;
+import net.sf.taverna.t2.workbench.loop.comparisons.IsLessThan;
+import net.sf.taverna.t2.workbench.loop.comparisons.Matches;
+import net.sf.taverna.t2.workbench.loop.comparisons.NotEqualTo;
+import net.sf.taverna.t2.workbench.loop.comparisons.NotMatches;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.port.InputActivityPort;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.port.OutputActivityPort;
+import uk.org.taverna.scufl2.api.port.OutputPort;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class ActivityGenerator {
+
+    private static final String LOOP_PORT = "loop";
+
+    private static final String SCRIPT = "script";
+
+    public static URI BEANSHELL_ACTIVITY = URI
+            .create("http://ns.taverna.org.uk/2010/activity/beanshell");
+
+    public static URI BEANSHELL_CONFIG = BEANSHELL_ACTIVITY.resolve("#Config");
+
+    
+	public static final double DEFAULT_DELAY_S = 0.2;
+	public static final String COMPARE_PORT = "comparePort";
+	public static final String COMPARISON = "comparison";
+	public static final String CUSTOM_COMPARISON = "custom";
+	public static final String COMPARE_VALUE = "compareValue";
+	public static final String IS_FEED_BACK = "isFeedBack";
+	public static final String DELAY = "delay";
+
+	private static Logger logger = Logger.getLogger(ActivityGenerator.class);
+	private final ObjectNode loopProperties;
+	private final Processor processorToCompare;
+	private static Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	public ActivityGenerator(ObjectNode configuration,
+			Processor processorToCompare) {
+		this.loopProperties = configuration;
+		this.processorToCompare = processorToCompare;
+	}
+
+	protected Activity generateActivity() {
+		Activity beanshell = new Activity();
+		beanshell.setType(BEANSHELL_ACTIVITY);
+		Configuration config = generateBeanshellConfig(beanshell);
+		// TODO: Where to put the config?
+		return beanshell;
+	}
+
+	private Configuration generateBeanshellConfig(Activity beanshell) {
+	    Configuration config = scufl2Tools.createConfigurationFor(beanshell, BEANSHELL_CONFIG);
+	    generateInputPorts(beanshell);
+	    generateOutputPorts(beanshell);
+	    config.getJsonAsObjectNode().put(SCRIPT, generateScript());
+		return config;
+	}
+
+	protected static List<Comparison> comparisons = Arrays.asList(
+			new EqualTo(), new NotEqualTo(), new Matches(), new NotMatches(),
+			new IsGreaterThan(), new IsLessThan());
+
+	protected static Comparison getComparisonById(String id) {
+	    if (id == null || id.isEmpty()) {
+	        return comparisons.get(0);
+	    }
+		for (Comparison potentialComparison : comparisons) {
+			if (potentialComparison.getId().equals(id)) {
+				return potentialComparison;
+			}
+		}
+		return null;
+	}
+
+	@SuppressWarnings("boxing")
+	private String generateScript() {
+		Map<String, String> replacements = new HashMap<String, String>();
+		replacements.put("${loopPort}", LOOP_PORT);
+		replacements.put("${port}", loopProperties.findValue(COMPARE_PORT).asText());
+		replacements.put("${value}", beanshellString(loopProperties
+				.findValue(COMPARE_VALUE).asText()));
+
+
+		// as seconds
+		Double delay = loopProperties.findPath(DELAY).asDouble(DEFAULT_DELAY_S);
+		// as milliseconds
+		delay = Math.max(0.0, delay) * 1000;
+		// as integer (for Thread.sleep)
+		replacements.put("${delay}", Integer.toString(delay.intValue()));
+
+		String template = getComparisonById(
+				loopProperties.findValue(COMPARISON).asText()).getScriptTemplate();
+
+		if (delay > 0.0) {
+    		template += "\nif (\"true\".matches(${loopPort})) {\n";
+    		template += "   Thread.sleep(${delay});\n";
+    		template += "}";
+		}
+
+		String script = template;
+		for (Entry<String, String> mapping : replacements.entrySet()) {
+			script = script.replace(mapping.getKey(), mapping.getValue());
+		}
+		return script;
+	}
+
+	private String beanshellString(String value) {
+		value = value.replace("\\", "\\\\");
+		value = value.replace("\n", "\\n");
+		value = value.replace("\"", "\\\"");
+		return '"' + value + '"';
+	}
+
+	private void generateInputPorts(Activity beanshell) {
+		if (processorToCompare == null) {
+		    return;
+		}
+		for (OutputProcessorPort procOut : processorToCompare.getOutputPorts()) {
+		    // Any of the outputs are available to the script, giving
+		    // a custom script that compares multiple outputs a better
+		    // starting point.
+			String portName = procOut.getName();
+			if (portName.equals(loopProperties.findValue(COMPARE_PORT).asText()) ||
+			        (loopProperties.findValue(IS_FEED_BACK).asBoolean())) {
+				InputActivityPort input = new InputActivityPort(beanshell, portName);
+				input.setDepth(procOut.getDepth());
+				input.setParent(beanshell);
+			}
+		}
+	}
+
+	private void generateOutputPorts(Activity beanshell) {
+	       OutputActivityPort loopPort = new OutputActivityPort(beanshell, LOOP_PORT);
+	        loopPort.setDepth(0);
+	        loopPort.setGranularDepth(0);
+	    if (processorToCompare == null) {
+            return;
+	    }	    
+	    if (! loopProperties.findValue(IS_FEED_BACK).asBoolean()) {
+           return;
+	    }
+	    for (InputProcessorPort procIn : processorToCompare.getInputPorts()) {
+            String portName = procIn.getName();
+            if (processorToCompare.getOutputPorts().containsName(portName)) {
+                OutputActivityPort actOut = new OutputActivityPort(beanshell, portName);
+                actOut.setDepth(procIn.getDepth());
+                actOut.setGranularDepth(procIn.getDepth());
+            }
+	    }
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/AddLoopFactory.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/AddLoopFactory.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/AddLoopFactory.java
new file mode 100644
index 0000000..f23650c
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/AddLoopFactory.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (C) 2008 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.loop;
+
+import java.awt.event.ActionEvent;
+import java.net.URI;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.workbench.MainWindow;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.AddLayerFactorySPI;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.core.Processor;
+
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class AddLoopFactory implements AddLayerFactorySPI {
+
+    private static final URI LOOP_TYPE = URI.create("http://ns.taverna.org.uk/2010/scufl2/taverna/dispatchlayer/Loop");
+
+    
+    private static Logger logger = Logger.getLogger(AddLoopFactory.class);
+    private static final JsonNodeFactory JSON_NODE_FACTORY = JsonNodeFactory.instance;
+    private static Scufl2Tools scufl2Tools = new Scufl2Tools();
+    
+	private EditManager editManager;
+	private FileManager fileManager;
+	private SelectionManager selectionManager;
+	private ApplicationConfiguration applicationConfig;
+
+	public boolean canAddLayerFor(Processor processor) {
+	   return findLoopLayer(processor) == null;
+	}
+
+
+    public ObjectNode findLoopLayer(Processor processor) {
+        List<Configuration> configs = scufl2Tools.configurationsFor(processor, selectionManager.getSelectedProfile());
+        for (Configuration config : configs) {
+            if (config.getJson().has("loop")) {
+                return (ObjectNode) config.getJson().get("loop");
+            }
+        }
+        return null;
+    }
+	
+	@SuppressWarnings("serial")
+	public Action getAddLayerActionFor(final Processor processor) {
+		return new AbstractAction("Add looping") {
+
+            public void actionPerformed(ActionEvent e) {
+				    ObjectNode loopLayer = findLoopLayer(processor);
+				    if (loopLayer == null) {
+				        loopLayer = JSON_NODE_FACTORY.objectNode();
+				    }
+					// Pop up the configure loop dialog
+                LoopConfigureAction loopConfigureAction = new LoopConfigureAction(
+                        MainWindow.getMainWindow(), null, processor, loopLayer,
+                        selectionManager.getSelectedProfile(), editManager,
+                        fileManager, getApplicationConfig());
+					loopConfigureAction.actionPerformed(e);
+			}
+		};
+	}
+
+	@Override
+	public boolean canCreateLayerClass(URI dispatchLayerType) {
+	    return dispatchLayerType.equals(LOOP_TYPE);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+	}
+
+    public SelectionManager getSelectionManager() {
+        return selectionManager;
+    }
+
+    public void setSelectionManager(SelectionManager selectionManager) {
+        this.selectionManager = selectionManager;
+    }
+
+
+    public ApplicationConfiguration getApplicationConfig() {
+        return applicationConfig;
+    }
+
+
+    public void setApplicationConfig(ApplicationConfiguration applicationConfig) {
+        this.applicationConfig = applicationConfig;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopAddMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopAddMenuAction.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopAddMenuAction.java
new file mode 100644
index 0000000..e7b6e87
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopAddMenuAction.java
@@ -0,0 +1,73 @@
+/**********************************************************************
+ * Copyright (C) 2007-2009 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.loop;
+
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+
+public class LoopAddMenuAction extends AbstractContextualMenuAction {
+
+	public static final URI configureRunningSection = URI
+	.create("http://taverna.sf.net/2009/contextMenu/configureRunning");
+
+	private static final URI LOOP_ADD_URI = URI
+	.create("http://taverna.sf.net/2008/t2workbench/loopAdd");
+
+	private static final String LOOP_ADD = "Loop add";
+
+	public LoopAddMenuAction() {
+		super(configureRunningSection, 20, LOOP_ADD_URI);
+	}
+
+	private AddLoopFactory addLoopFactory;
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		return new AbstractAction("Looping...") {
+			public void actionPerformed(ActionEvent e) {
+				//Loop loopLayer = null;
+				Processor p = (Processor) getContextualSelection().getSelection();
+				addLoopFactory.getAddLayerActionFor(p).actionPerformed(e);
+				//LoopConfigureMenuAction.configureLoopLayer(p, e); // Configuration dialog pop up is now done from getAddLayerActionFor()
+			}
+		};
+	}
+
+	public boolean isEnabled() {
+		Object selection = getContextualSelection().getSelection();
+		return (super.isEnabled() && (selection instanceof Processor) && (LoopConfigureMenuAction.getLoopLayer((Processor)selection) == null));
+	}
+
+	public void setAddLoopFactory(AddLoopFactory addLoopFactory) {
+		this.addLoopFactory = addLoopFactory;
+	}
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigurationPanel.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigurationPanel.java
new file mode 100644
index 0000000..88efb1a
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigurationPanel.java
@@ -0,0 +1,588 @@
+/*******************************************************************************
+ * Copyright (C) 2008 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.loop;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+import javax.swing.AbstractAction;
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.activities.beanshell.views.BeanshellConfigurationPanel;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+import net.sf.taverna.t2.workbench.loop.comparisons.Comparison;
+import net.sf.taverna.t2.workbench.ui.Utils;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * UI for {@link LoopConfiguration}
+ *
+ * @author Stian Soiland-Reyes
+ *
+ */
+@SuppressWarnings("serial")
+public class LoopConfigurationPanel extends JPanel {
+
+	private static final String CONDITION_ACTIVITY = "conditionActivity";
+    private static final String DEFAULT_DELAY_S = "0.5";
+	protected ObjectNode configuration;
+
+	private static final Scufl2Tools scufl2tools = new Scufl2Tools();
+	private ApplicationConfiguration applicationConfig;
+
+	
+	protected final Processor processor;
+
+	protected JPanel headerPanel = new JPanel();
+	protected JPanel optionsPanel = new JPanel();
+	protected JPanel configPanel = new JPanel();
+	protected JPanel customPanel = new JPanel();
+
+	protected JLabel valueTypeLabel = new JLabel("the string");
+
+	protected JTextField valueField = new JTextField("", 15);
+
+	protected JLabel delayLabel = new JLabel("adding a delay of ");
+	protected JTextField delayField = new JTextField(
+			Double.toString(ActivityGenerator.DEFAULT_DELAY_S), 4);
+	protected JLabel secondsLabel = new JLabel(" seconds between the loops.");
+
+	private JComboBox<String> portCombo;
+	private JComboBox<Comparison> comparisonCombo;
+	private JButton customizeButton;
+
+	protected ObjectNode loopLayer;
+	private Object Comparison;
+	private Activity originalCondition = null;
+    private Profile profile;
+
+    public LoopConfigurationPanel(Processor processor, ObjectNode loopLayer,
+            Profile profile, ApplicationConfiguration applicationConfig) {
+		this.processor = processor;
+		this.loopLayer = loopLayer;
+        this.profile = profile;
+        this.applicationConfig = applicationConfig;
+		this.setBorder(new EmptyBorder(10,10,10,10));
+		initialise();
+		setConfiguration(loopLayer);
+	}
+
+	public ObjectNode getConfiguration() {
+		uiToConfig();
+		return loopLayer.deepCopy();
+	}
+
+	private static Logger logger = Logger
+			.getLogger(LoopConfigurationPanel.class);
+
+	protected void uiToConfig() {
+	    String comparisonStr = configuration.path(ActivityGenerator.COMPARISON).asText();
+	    if (comparisonStr.isEmpty()) {
+	        comparisonStr = ActivityGenerator.CUSTOM_COMPARISON;
+	    }
+		if (comparisonStr.equals(ActivityGenerator.CUSTOM_COMPARISON)
+				&& ! configuration.path(CONDITION_ACTIVITY).asText().isEmpty()) {
+			// Ignore values
+		} else {
+		    configuration.put("runFirst", true);
+			if (portCombo.getSelectedItem() == null) {
+			    // unconfigured port
+				configuration.remove(ActivityGenerator.COMPARE_PORT);
+				configuration.putNull(CONDITION_ACTIVITY);
+				return;
+			} else {
+				configuration.put(ActivityGenerator.COMPARE_PORT,
+						((String) portCombo.getSelectedItem()));
+			}
+
+			Comparison comparison = (Comparison) comparisonCombo
+					.getSelectedItem();
+			if (comparison == null) {
+				configuration.remove(ActivityGenerator.COMPARISON);
+				configuration.putNull(CONDITION_ACTIVITY);
+				return;
+			} else {
+				configuration
+						.put(ActivityGenerator.COMPARISON, comparison.getId());
+			}
+			configuration.put(ActivityGenerator.COMPARE_VALUE, valueField
+					.getText());
+			configuration.put(ActivityGenerator.DELAY, Double.parseDouble(delayField.getText()));
+			configuration.put(ActivityGenerator.IS_FEED_BACK, feedBackCheck.isSelected());
+
+			// Generate activity
+			ActivityGenerator activityGenerator = new ActivityGenerator(
+					configuration, processor);
+			configuration.put(CONDITION_ACTIVITY, activityGenerator.generateActivity().getName());
+		}
+	}
+
+	public class ResetAction extends AbstractAction {
+		public ResetAction() {
+			super("Clear");
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			configuration.putNull(CONDITION_ACTIVITY);
+			configToUi();
+		}
+	}
+
+	private final class CustomizeAction implements ActionListener {
+
+
+//		public CustomizeAction() {
+//			super();
+//			//putValue(NAME, "Customise loop condition");
+//		}
+
+		public void actionPerformed(ActionEvent e) {
+			uiToConfig();
+
+			String conditionName = configuration.path(CONDITION_ACTIVITY).asText();
+			
+			Activity condition = profile.getActivities().getByName(conditionName);
+			if (condition == null) {
+			    condition = new Activity();
+			    profile.getActivities().add(condition);
+			    configuration.put(CONDITION_ACTIVITY, condition.getName());
+			    condition.setType(ActivityGenerator.BEANSHELL_ACTIVITY);
+			    Configuration config = scufl2tools.createConfigurationFor(condition, ActivityGenerator.BEANSHELL_CONFIG);
+			} else if (!(condition.getType().equals(ActivityGenerator.BEANSHELL_ACTIVITY))) {
+				logger.warn("Can't configure unsupported loop condition of service type "
+						+ condition.getType());
+				return;
+			}
+
+			Frame owner = Utils.getParentFrame(LoopConfigurationPanel.this);
+			
+			
+            final BeanshellConfigurationPanel beanshellConfigView = new BeanshellConfigurationPanel(
+                    condition, applicationConfig);
+			
+			final JDialog dialog = new HelpEnabledDialog(owner, "Customize looping", true);
+			dialog.setLayout(new BorderLayout());
+			dialog.add(beanshellConfigView, BorderLayout.NORTH);
+			dialog.setSize(600, 600);
+			JPanel buttonPanel = new JPanel();
+
+			buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+
+			JButton applyButton = new JButton(new AbstractAction() {
+
+				public void actionPerformed(ActionEvent e) {
+					if (beanshellConfigView.isConfigurationChanged()) {
+						beanshellConfigView.noteConfiguration();
+//							beanshellActivity.configure(beanshellConfigView
+//									.getConfiguration());
+//							configuration.setCondition(beanshellActivity);
+						Configuration config = beanshellConfigView.getConfiguration();
+						// TODO: Do we need to store this somehow?
+						configuration.put(
+								ActivityGenerator.COMPARISON,
+								ActivityGenerator.CUSTOM_COMPARISON);
+					}
+					dialog.setVisible(false);
+					configToUi();
+				}
+
+			});
+			applyButton.setText("Apply");
+
+			buttonPanel.add(applyButton);
+			JButton closeButton = new JButton(new AbstractAction() {
+
+				public void actionPerformed(ActionEvent e) {
+					dialog.setVisible(false);
+				}
+			});
+			closeButton.setText("Cancel");
+			buttonPanel.add(closeButton);
+			dialog.add(buttonPanel, BorderLayout.SOUTH);
+			dialog.setLocationRelativeTo(customizeButton);
+			dialog.setVisible(true);
+
+		}
+	}
+
+	public void setConfiguration(ObjectNode configuration) {
+		this.configuration = configuration.deepCopy();
+		configToUi();
+	}
+
+	protected void configToUi() {
+		
+
+		String comparisonId;
+		
+		if (configuration.has(ActivityGenerator.COMPARISON)) {
+            comparisonId = configuration.get(ActivityGenerator.COMPARISON)
+                    .asText();
+		} else {
+            comparisonId = ActivityGenerator.CUSTOM_COMPARISON;
+		}
+
+		if (comparisonId.equals(ActivityGenerator.CUSTOM_COMPARISON)
+				&& configuration.has("conditionalActivity")) {
+			configPanel.setVisible(false);
+			customPanel.setVisible(true);
+		} else {
+			configPanel.setVisible(true);
+			customPanel.setVisible(false);
+		}
+
+		portCombo.setSelectedItem(configuration.get(ActivityGenerator.COMPARE_PORT).asText());
+		if (portCombo.getSelectedIndex() == -1
+				&& portCombo.getModel().getSize() > 0) {
+			portCombo.setSelectedIndex(0);
+		}
+
+		Comparison comparison = ActivityGenerator
+				.getComparisonById(comparisonId);
+		comparisonCombo.setSelectedItem(comparison);
+		if (comparisonCombo.getSelectedIndex() == -1
+				&& comparisonCombo.getModel().getSize() > 0) {
+			comparisonCombo.setSelectedIndex(0);
+		}
+
+		valueField.setText(configuration.get(ActivityGenerator.COMPARE_VALUE).asText());
+
+		if (configuration.has(ActivityGenerator.DELAY)) {
+		    delayField.setText(configuration.get(ActivityGenerator.DELAY).asText());
+		} else {
+		    delayField.setText(DEFAULT_DELAY_S);
+		}
+
+		feedBackCheck.setSelected(configuration.get(ActivityGenerator.IS_FEED_BACK).asBoolean());
+		updateFeedbackHelp();
+	}
+
+	private void initialise() {
+		removeAll();
+		setLayout(new GridBagLayout());
+		GridBagConstraints gbc = new GridBagConstraints();
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+		gbc.anchor = GridBagConstraints.FIRST_LINE_START;
+		gbc.gridx = 0;
+		gbc.weightx = 0.1;
+
+		makeHeader();
+		add(headerPanel, gbc);
+
+		makeConfigPanel();
+		gbc.weighty = 0.1;
+		gbc.anchor = GridBagConstraints.CENTER;
+		gbc.fill = GridBagConstraints.BOTH;
+		add(configPanel, gbc);
+
+		makeCustomPanel();
+		add(customPanel, gbc);
+
+		makeOptions();
+		add(optionsPanel, gbc);
+	}
+
+	protected void makeCustomPanel() {
+		customPanel.removeAll();
+		customPanel.setLayout(new GridBagLayout());
+
+		GridBagConstraints gbc = new GridBagConstraints();
+		gbc.anchor = GridBagConstraints.LINE_START;
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.gridwidth = 2;
+		gbc.weightx = 0.1;
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+
+		JLabel helpLabel = new JLabel(
+				"<html><body>"
+						+ "The service <strong>" + processor.getName() +  "</strong> will be "
+						+ "invoked repeatedly as "
+						+ "long as the <em>customized loop condition service</em> returns a string equal "
+						+ "to <strong>\"true\"</strong> on its output port <code>loop</code>."
+//						+ "<br><br>"
+//						+ "Input ports of the condition service will be populated with values from "
+//						+ "the <em>corresponding output ports</em> of the main service invocation "
+//						+ "(as long as they are also "
+//						+ "<strong>connected</strong> in the containing workflow)."
+//						+ "<br><br> "
+//
+//						+ "Any <em>matching "
+//						+ "output ports</em> from the condition service will provide the corresponding "
+//						+ "<em>inputs</em> to the main service while looping. You will need to connect "
+//						+ "the <em>initial inputs</em> in the containing workflow."
+						+ "</body></html>");
+		customPanel.add(helpLabel, gbc);
+
+		gbc.weightx = 0.1;
+		gbc.fill = GridBagConstraints.NONE;
+		gbc.gridx = 0;
+		gbc.gridy++;
+		gbc.gridwidth = 1;
+		gbc.anchor = GridBagConstraints.EAST;
+		JPanel customiseButtonPanel = new JPanel(new FlowLayout());
+		customiseButtonPanel.setBorder(new EmptyBorder(10,0,0,0));
+		customizeButton = new JButton("Customize loop condition");
+		customizeButton.addActionListener(new CustomizeAction());
+		customiseButtonPanel.add(customizeButton);
+		customiseButtonPanel.add(new JButton(new ResetAction()));
+		customPanel.add(customiseButtonPanel, gbc);
+
+	}
+
+	protected void makeConfigPanel() {
+		configPanel.removeAll();
+		configPanel.setLayout(new GridBagLayout());
+
+		GridBagConstraints gbc = new GridBagConstraints();
+		gbc.anchor = GridBagConstraints.LINE_START;
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.gridwidth = 4;
+		gbc.weightx = 0.1;
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+		JLabel invokedRepeatedlyLabel = new JLabel(
+
+				"<html><body>The service <strong>" + processor.getName() +  "</strong> " +
+						"will be invoked repeatedly <em>until</em> its output port</body></html>");
+		invokedRepeatedlyLabel.setBorder(new EmptyBorder(10,0,10,0)); // give some top and bottom border to the label
+		configPanel.add(invokedRepeatedlyLabel, gbc);
+		gbc.ipadx = 4;
+		gbc.ipady = 4;
+
+		gbc.weightx = 0.0;
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.gridwidth = 1;
+		List<String> activityOutputPorts = getActivityOutputPorts();
+		portCombo = new JComboBox(activityOutputPorts.toArray());
+		configPanel.add(portCombo, gbc);
+
+		comparisonCombo = new JComboBox(ActivityGenerator.comparisons.toArray());
+		comparisonCombo.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				Comparison selectedComparison = (Comparison) comparisonCombo
+						.getSelectedItem();
+				if (selectedComparison != null) {
+					valueTypeLabel.setText("the "
+							+ selectedComparison.getValueType());
+				}
+			}
+		});
+		if (comparisonCombo.getSelectedIndex() == -1) {
+			comparisonCombo.setSelectedIndex(0);
+		}
+		gbc.gridx = 1;
+		gbc.gridy = 1;
+		configPanel.add(comparisonCombo, gbc);
+
+		gbc.gridx = 2;
+		gbc.gridy = 1;
+		valueTypeLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+		configPanel.add(valueTypeLabel, gbc);
+
+		gbc.gridx = 3;
+		gbc.gridy = 1;
+		gbc.weightx = 0.5; // request all extra space
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+		configPanel.add(valueField, gbc);
+
+		gbc.gridx = 0;
+		gbc.gridy = 2;
+		gbc.weightx = 0.0;
+		configPanel.add(delayLabel, gbc);
+
+		gbc.gridx = 1;
+		gbc.gridx = 1;
+		gbc.gridy = 2;
+		gbc.weightx = 0.0;
+		delayField.setHorizontalAlignment(JTextField.RIGHT);
+		configPanel.add(delayField, gbc);
+
+		gbc.gridx = 2;
+		gbc.gridy = 2;
+		gbc.gridwidth = 2;
+		gbc.weightx = 0.5; // request all extra space
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+		configPanel.add(secondsLabel, gbc);
+
+		if (activityOutputPorts.isEmpty()) {
+			JLabel warningLabel = new JLabel(
+					"<html><body><strong>Warning:</strong><br>"
+							+ "<i>No single value output ports detected on the main service, "
+							+ "cannot use built-in comparisons. You may still add a customized " +
+									"looping script</i></body></html>");
+			gbc.gridx = 0;
+			gbc.gridy++;
+			gbc.gridwidth = 4;
+			gbc.weightx = 0.1;
+			gbc.fill = GridBagConstraints.BOTH;
+			gbc.gridy++;
+			configPanel.add(warningLabel, gbc);
+			invokedRepeatedlyLabel.setVisible(false);
+			portCombo.setVisible(false);
+			comparisonCombo.setVisible(false);
+			portWarning.setVisible(false);
+			valueTypeLabel.setVisible(false);
+			valueField.setVisible(false);
+			delayField.setVisible(false);
+			delayLabel.setVisible(false);
+			secondsLabel.setVisible(false);
+		}
+
+		gbc.gridy++;
+		gbc.gridx = 0;
+		gbc.weightx = 0.1;
+		gbc.gridwidth = 4;
+		gbc.weightx = 0.1;
+		gbc.fill = GridBagConstraints.BOTH;
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+		gbc.insets = new Insets(10, 0, 10, 0);
+		configPanel.add(portWarning, gbc);
+
+		gbc.insets = new Insets(0, 0, 0, 0);
+		gbc.weightx = 0.1;
+		gbc.fill = GridBagConstraints.NONE;
+		gbc.gridx = 0;
+		gbc.gridy++;
+		gbc.gridwidth = 4;
+		gbc.anchor = GridBagConstraints.LAST_LINE_END;
+		JPanel customiseButtonPanel = new JPanel(new FlowLayout());
+		customizeButton = new JButton("Customize loop condition");
+		customizeButton.addActionListener(new CustomizeAction());
+		customiseButtonPanel.add(customizeButton);
+		configPanel.add(customiseButtonPanel, gbc);
+
+		// filler
+		gbc.gridy++;
+		gbc.fill = GridBagConstraints.BOTH;
+		gbc.gridx = 4;
+		gbc.weightx = 0.1;
+		gbc.weighty = 0.1;
+		gbc.gridwidth = 4;
+		configPanel.add(Box.createGlue(), gbc);
+	}
+
+	private List<String> getActivityOutputPorts() {
+	    // Should already be sorted
+	    return new ArrayList<>(processor.getOutputPorts().getNames());
+    }
+
+    protected JCheckBox feedBackCheck = new JCheckBox(
+			"Enable output port to input port feedback");
+	private JLabel portWarning = new JLabel(
+			"<html><body><small>Note that for Taverna to be able to execute this loop, "
+					+ "the output port <strong>must</strong> be connected to an input of another service "
+					+ "or a workflow output port.</small></body></html>");
+
+	protected void makeOptions() {
+		optionsPanel.removeAll();
+		optionsPanel.setLayout(new GridBagLayout());
+		GridBagConstraints gbc = new GridBagConstraints();
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.weightx = 0.1;
+		gbc.anchor = GridBagConstraints.FIRST_LINE_START;
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+		feedBackCheck.setBorder(new EmptyBorder(0,0,10,0));
+		optionsPanel.add(feedBackCheck, gbc);
+		feedBackCheck.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				updateFeedbackHelp();
+			}
+		});
+		updateFeedbackHelp();
+
+		gbc.gridy = 1;
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+		optionsPanel.add(feedbackHelp, gbc);
+	}
+
+	protected void updateFeedbackHelp() {
+		feedbackHelp.setEnabled(feedBackCheck.isSelected());
+		Color color;
+		if (feedBackCheck.isSelected()) {
+			color = valueTypeLabel.getForeground();
+		} else {
+			// Work around
+			// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4303706
+			// and assume gray is the 'disabled' colour in our Look n Feel
+			color = Color.gray;
+		}
+		feedbackHelp.setForeground(color);
+
+	}
+
+	JLabel feedbackHelp = new JLabel(
+			"<html><small>"
+					+ "<p>When feedback is enabled, the value of the output port is used as input " +
+							"the next time the loop in invoked. The input and output ports used for feedback "
+					+ "<strong>must</strong> have the same <strong>name</strong> and <strong>depth</strong>."
+					+ "</p><br>"
+
+					+ "<p>Feedback can be useful for looping over a nested workflow, "
+					+ "where the nested workflow's output determines its next input value.</p><br>"
+
+					+ "<p>In order to use feedback looping, you must provide an initial value to the input port by "
+					+ "connecting it to the output of a previous service or workflow input port."
+					+ "The output port used as feedback also has to be connected to a downstream service " +
+							"or a workflow output port.</p>"
+
+					+ "</small></html>");
+
+	protected void makeHeader() {
+		headerPanel.removeAll();
+		headerPanel.setLayout(new BorderLayout());
+		//headerPanel.add(new ShadedLabel("Looping for service"
+		//		+ processor.getLocalName(), ShadedLabel.ORANGE));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureAction.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureAction.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureAction.java
new file mode 100644
index 0000000..0b7ad8f
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureAction.java
@@ -0,0 +1,262 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.loop;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import org.apache.log4j.Logger;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+
+/**
+ * @author Alan R Williams
+ * @author Stian Soiland-Reyes
+ *
+ */
+@SuppressWarnings("serial")
+public class LoopConfigureAction extends AbstractAction {
+
+	private static Logger logger = Logger.getLogger(LoopConfigureAction.class);
+
+	private final EditManager editManager;
+	private final FileManager fileManager;
+
+	private final Frame owner;
+	private final ObjectNode loopLayer;
+	private final LoopContextualView contextualView;
+	private final Processor processor;
+    private final Profile profile;
+
+    private ApplicationConfiguration applicationConfig;
+
+
+    protected LoopConfigureAction(Frame owner,
+            LoopContextualView contextualView, Processor processor,
+            ObjectNode loopLayer, Profile profile, EditManager editManager,
+            FileManager fileManager, ApplicationConfiguration applicationConfig) {
+		super("Configure");
+		this.owner = owner;
+		this.contextualView = contextualView;
+		this.loopLayer = loopLayer;
+        this.profile = profile;
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+		this.processor = processor;
+        this.applicationConfig = applicationConfig;
+	}
+
+	public void actionPerformed(ActionEvent e) {
+		String title = "Looping for service " + processor.getName();
+		final JDialog dialog = new HelpEnabledDialog(owner, title, true);
+        LoopConfigurationPanel loopConfigurationPanel = new LoopConfigurationPanel(
+                processor, loopLayer, profile, applicationConfig);
+		dialog.add(loopConfigurationPanel, BorderLayout.CENTER);
+
+		JPanel buttonPanel = new JPanel();
+		buttonPanel.setLayout(new FlowLayout());
+
+		JButton okButton = new JButton(new OKAction(dialog, loopConfigurationPanel));
+		buttonPanel.add(okButton);
+
+		JButton resetButton = new JButton(new ResetAction(loopConfigurationPanel));
+		buttonPanel.add(resetButton);
+
+		JButton cancelButton = new JButton(new CancelAction(dialog));
+		buttonPanel.add(cancelButton);
+
+		dialog.add(buttonPanel, BorderLayout.SOUTH);
+		dialog.pack();
+		dialog.setSize(650, 430);
+		dialog.setLocationRelativeTo(null);
+		dialog.setVisible(true);
+	}
+
+	protected class CancelAction extends AbstractAction {
+		private final JDialog dialog;
+
+		protected CancelAction(JDialog dialog) {
+			super("Cancel");
+			this.dialog = dialog;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			dialog.setVisible(false);
+			if (contextualView != null) {
+				contextualView.refreshView();
+			}
+		}
+
+	}
+
+	protected class OKAction extends AbstractAction {
+		private final JDialog dialog;
+		private final LoopConfigurationPanel loopConfigurationPanel;
+
+		protected OKAction(JDialog dialog, LoopConfigurationPanel loopConfigurationPanel) {
+			super("OK");
+			this.dialog = dialog;
+			this.loopConfigurationPanel = loopConfigurationPanel;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			try {
+
+				List<Edit<?>> compoundEdit = new ArrayList<Edit<?>>();
+				LoopConfiguration configuration = loopConfigurationPanel.getConfiguration();
+				compoundEdit.add(edits.getConfigureEdit(loopLayer, configuration));
+				compoundEdit.addAll(checkPortMappings(configuration.getCondition()));
+
+				editManager.doDataflowEdit(fileManager.getCurrentDataflow(), new CompoundEdit(
+						compoundEdit));
+				dialog.setVisible(false);
+				if (contextualView != null) {
+					contextualView.refreshView();
+				}
+			} catch (RuntimeException ex) {
+				logger.warn("Could not configure looping", ex);
+				JOptionPane.showMessageDialog(owner, "Could not configure looping",
+						"An error occured when configuring looping: " + ex.getMessage(),
+						JOptionPane.ERROR_MESSAGE);
+			} catch (EditException ex) {
+				logger.warn("Could not configure looping", ex);
+				JOptionPane.showMessageDialog(owner, "Could not configure looping",
+						"An error occured when configuring looping: " + ex.getMessage(),
+						JOptionPane.ERROR_MESSAGE);
+			}
+		}
+
+		protected List<Edit<?>> checkPortMappings(Activity<?> conditionActivity) {
+
+			List<Edit<?>> compoundEdit = new ArrayList<Edit<?>>();
+			if (processor.getActivityList().isEmpty()) {
+				return compoundEdit;
+			}
+			Set<String> newInputs = new HashSet<String>();
+			Set<String> newOutputs = new HashSet<String>();
+
+			Activity<?> firstProcessorActivity;
+			firstProcessorActivity = processor.getActivityList().get(0);
+			if (conditionActivity != null) {
+				for (OutputPort condOutPort : conditionActivity.getOutputPorts()) {
+					String portName = condOutPort.getName();
+					Map<String, String> mapping = firstProcessorActivity.getInputPortMapping();
+					if (!mapping.containsKey(portName)) {
+						if (mapping.containsKey(portName)) {
+							logger.warn("Can't re-map input for " + "conditional output "
+									+ portName);
+						}
+						for (InputPort inputPort : firstProcessorActivity.getInputPorts()) {
+							if (inputPort.equals(portName)) {
+								Edit<Activity<?>> edit = edits.getAddActivityInputPortMappingEdit(
+										firstProcessorActivity, portName, portName);
+								compoundEdit.add(edit);
+								newInputs.add(portName);
+							}
+						}
+					}
+				}
+				for (InputPort condInPort : conditionActivity.getInputPorts()) {
+					String portName = condInPort.getName();
+					Map<String, String> mapping = firstProcessorActivity.getOutputPortMapping();
+					if (!mapping.containsValue(portName)) {
+						for (OutputPort outputPort : firstProcessorActivity.getOutputPorts()) {
+							if (outputPort.equals(portName)) {
+								if (mapping.containsKey(portName)) {
+									logger.warn("Can't re-map output for " + "conditional input "
+											+ portName);
+								}
+								Edit<Activity<?>> edit = edits.getAddActivityOutputPortMappingEdit(
+										firstProcessorActivity, portName, portName);
+								logger.info("Mapping for conditional non-outgoing activity port binding "
+										+ portName);
+								compoundEdit.add(edit);
+								newOutputs.add(portName);
+							}
+						}
+					}
+				}
+			}
+			// Remove any stale bindings that no longer match neither
+			// conditional activity or the processor output ports
+			for (String processorIn : firstProcessorActivity.getInputPortMapping().keySet()) {
+				if (newInputs.contains(processorIn)) {
+					continue;
+				}
+				boolean foundMatch = false;
+				for (InputPort processorPort : processor.getInputPorts()) {
+					if (processorPort.getName().equals(processorIn)) {
+						foundMatch = true;
+						break;
+					}
+				}
+				if (!foundMatch) {
+					Edit<Activity<?>> edit = edits.getRemoveActivityInputPortMappingEdit(
+							firstProcessorActivity, processorIn);
+					logger.info("Removing stale input port binding " + processorIn);
+					compoundEdit.add(edit);
+				}
+			}
+			for (String processorOut : firstProcessorActivity.getOutputPortMapping().keySet()) {
+				if (newInputs.contains(processorOut)) {
+					continue;
+				}
+				boolean foundMatch = false;
+				for (OutputPort processorPort : processor.getOutputPorts()) {
+					if (processorPort.getName().equals(processorOut)) {
+						foundMatch = true;
+						break;
+					}
+				}
+				if (!foundMatch) {
+					Edit<Activity<?>> edit = edits.getRemoveActivityOutputPortMappingEdit(
+							firstProcessorActivity, processorOut);
+					logger.info("Removing stale output port binding " + processorOut);
+					compoundEdit.add(edit);
+				}
+			}
+
+			return compoundEdit;
+		}
+	}
+
+	protected class ResetAction extends AbstractAction {
+		private LoopConfigurationPanel loopConfigurationPanel;
+
+		protected ResetAction(LoopConfigurationPanel loopConfigurationPanel) {
+			super("Reset");
+			this.loopConfigurationPanel = loopConfigurationPanel;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			if (contextualView != null) {
+				contextualView.refreshView();
+			}
+			loopConfigurationPanel.setConfiguration(loopLayer.getConfiguration());
+		}
+
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureMenuAction.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureMenuAction.java
new file mode 100644
index 0000000..3d6700d
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureMenuAction.java
@@ -0,0 +1,97 @@
+/**********************************************************************
+ * Copyright (C) 2007-2009 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.loop;
+
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+
+public class LoopConfigureMenuAction extends AbstractContextualMenuAction {
+
+	public static final URI configureRunningSection = URI
+	.create("http://taverna.sf.net/2009/contextMenu/configureRunning");
+
+	private static final URI LOOP_CONFIGURE_URI = URI
+	.create("http://taverna.sf.net/2008/t2workbench/loopConfigure");
+
+	private static final String LOOP_CONFIGURE = "Loop configure";
+
+	private EditManager editManager;
+
+	private FileManager fileManager;
+
+	public LoopConfigureMenuAction() {
+		super(configureRunningSection, 20, LOOP_CONFIGURE_URI);
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		return new AbstractAction("Looping...") {
+			public void actionPerformed(ActionEvent e) {
+				Processor p = (Processor) getContextualSelection().getSelection();
+				configureLoopLayer(p, e);
+			}
+		};
+	}
+
+	public void configureLoopLayer(Processor p, ActionEvent e) {
+	    ObjectNode loopLayer = getLoopLayer(p);
+		if (loopLayer != null) {
+			LoopConfigureAction loopConfigureAction = new LoopConfigureAction(null, null, loopLayer, editManager, fileManager);
+			loopConfigureAction.actionPerformed(e);
+		}
+	}
+
+	public static ObjectNode getLoopLayer(Processor p) {
+		for (DispatchLayer dl : p.getDispatchStack().getLayers()) {
+			if (dl instanceof Loop) {
+				result = (Loop) dl;
+				break;
+			}
+		}
+		return result;
+	}
+
+	public boolean isEnabled() {
+		Object selection = getContextualSelection().getSelection();
+		return (super.isEnabled() && (selection instanceof Processor) && (getLoopLayer((Processor)selection) != null));
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualView.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualView.java
new file mode 100644
index 0000000..c22f376
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualView.java
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Copyright (C) 2008 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.loop;
+
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.util.Properties;
+
+import javax.swing.Action;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.loop.comparisons.Comparison;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+
+/**
+ * View of a processor, including it's iteration stack, activities, etc.
+ *
+ * @author Stian Soiland-Reyes
+ *
+ */
+public class LoopContextualView extends ContextualView {
+
+	private static final long serialVersionUID = 1L;
+
+	private static Logger logger = Logger.getLogger(LoopContextualView.class);
+
+	private EditManager editManager;
+	private FileManager fileManager;
+
+	private Loop loopLayer;
+
+	private JPanel panel;
+
+	private Processor processor;
+
+	public LoopContextualView(Processor processor, EditManager editManager, FileManager fileManager) {
+		super();
+		this.loopLayer = loopLayer;
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+		this.processor = processor;
+		initialise();
+		initView();
+	}
+
+	@Override
+	public Action getConfigureAction(Frame owner) {
+		return new LoopConfigureAction(owner, this, processor, editManager, fileManager);
+	}
+
+	@Override
+	public void refreshView() {
+		initialise();
+	}
+
+	private void initialise() {
+		if (panel == null) {
+			panel = new JPanel();
+		} else {
+			panel.removeAll();
+		}
+		panel.setLayout(new GridBagLayout());
+		updateUIByConfig();
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		return panel;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Loop of " + processor.getLocalName();
+	}
+
+	protected void updateUIByConfig() {
+		GridBagConstraints gbc = new GridBagConstraints();
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.weightx = 0.1;
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+
+		StringBuilder description = new StringBuilder("<html><body>");
+		Properties properties = loopLayer.getConfiguration().getProperties();
+		if (properties.getProperty(ActivityGenerator.COMPARISON,
+				ActivityGenerator.CUSTOM_COMPARISON).equals(
+				ActivityGenerator.CUSTOM_COMPARISON)) {
+			Activity<?> condition = loopLayer.getConfiguration().getCondition();
+			if (condition != null) {
+				description.append("Looping using custom conditional ");
+				if (condition instanceof BeanshellActivity) {
+					String script = ((BeanshellActivity)condition).getConfiguration().getScript();
+					if (script != null) {
+						if (script.length() <= 100) {
+							description.append("<pre>\n");
+							description.append(script);
+							description.append("</pre>\n");
+						}
+					}
+				}
+			} else {
+				description.append("<i>Unconfigured, will not loop</i>");
+			}
+		} else {
+			description.append("The service will be invoked repeatedly ");
+			description.append("until<br> its output <strong>");
+			description.append(properties
+					.getProperty(ActivityGenerator.COMPARE_PORT));
+			description.append("</strong> ");
+
+			Comparison comparison = ActivityGenerator
+					.getComparisonById(properties
+							.getProperty(ActivityGenerator.COMPARISON));
+			description.append(comparison.getName());
+
+			description.append(" the " + comparison.getValueType() + ": <pre>");
+			description.append(properties
+					.getProperty(ActivityGenerator.COMPARE_VALUE));
+			description.append("</pre>");
+
+			String delay = properties.getProperty(ActivityGenerator.DELAY, "");
+			try {
+				if (Double.parseDouble(delay) > 0) {
+					description.append("adding a delay of " + delay
+							+ " seconds between loops.");
+				}
+			} catch (NumberFormatException ex) {
+			}
+		}
+		description.append("</body></html>");
+
+		panel.add(new JLabel(description.toString()), gbc);
+		gbc.gridy++;
+
+		revalidate();
+	}
+
+
+
+	@Override
+	public int getPreferredPosition() {
+		return 400;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualViewFactory.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualViewFactory.java
new file mode 100644
index 0000000..d222849
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualViewFactory.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (C) 2008 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.loop;
+
+import java.util.Arrays;
+import java.util.List;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+
+public class LoopContextualViewFactory implements ContextualViewFactory<Processor> {
+
+	private EditManager editManager;
+	private FileManager fileManager;
+
+	public boolean canHandle(Object selection) {
+		return selection instanceof Processor;
+	}
+
+	public List<ContextualView> getViews(Processor selection) {
+		return Arrays.asList(new ContextualView[] {new LoopContextualView(selection, editManager, fileManager)});
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopRemoveMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopRemoveMenuAction.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopRemoveMenuAction.java
new file mode 100644
index 0000000..12a9592
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopRemoveMenuAction.java
@@ -0,0 +1,92 @@
+/**********************************************************************
+ * Copyright (C) 2007-2009 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.loop;
+
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+
+public class LoopRemoveMenuAction extends AbstractContextualMenuAction {
+
+	private static Logger logger = Logger
+	.getLogger(LoopRemoveMenuAction.class);
+
+	public static final URI configureRunningSection = URI
+	.create("http://taverna.sf.net/2009/contextMenu/configureRunning");
+
+	private static final URI LOOP_REMOVE_URI = URI
+	.create("http://taverna.sf.net/2008/t2workbench/loopRemove");
+
+	private static final String LOOP_REMOVE = "Loop remove";
+
+	public LoopRemoveMenuAction() {
+		super(configureRunningSection, 25, LOOP_REMOVE_URI);
+	}
+
+	private EditManager editManager;
+	private FileManager fileManager;
+
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		return new AbstractAction("Disable looping") {
+			public void actionPerformed(ActionEvent e) {
+				Processor p = (Processor) getContextualSelection().getSelection();
+					Loop loopLayer = LoopConfigureMenuAction.getLoopLayer(p);
+					Edit<DispatchStack> deleteEdit = editManager.getEdits().getDeleteDispatchLayerEdit(
+							p.getDispatchStack(), loopLayer);
+					// TODO: Should warn before removing "essential" layers
+					try {
+						editManager.doDataflowEdit(fileManager.getCurrentDataflow(),
+								deleteEdit);
+					} catch (EditException ex) {
+						logger.warn("Could not remove layer " + loopLayer, ex);
+					}
+
+			}
+		};
+	}
+
+	public boolean isEnabled() {
+		Object selection = getContextualSelection().getSelection();
+		return (super.isEnabled() && (selection instanceof Processor) && (LoopConfigureMenuAction.getLoopLayer((Processor)selection) != null));
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Comparison.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Comparison.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Comparison.java
new file mode 100644
index 0000000..608030c
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Comparison.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (C) 2008 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.loop.comparisons;
+
+import net.sf.taverna.t2.workbench.loop.LoopConfigurationPanel;
+
+/**
+ * A comparison beanshell template for {@link LoopConfigurationPanel}.
+ * <p>
+ * A comparison is a template for generating a beanshell that can be used for
+ * comparisons in say the {@link Loop} layer.
+ * 
+ * @author Stian Soiland-Reyes
+ * 
+ */
+public abstract class Comparison {
+
+	public String toString() {
+		return getName();
+	}
+
+	public abstract String getId();
+
+	public abstract String getName();
+
+	public abstract String getValueType();
+
+	public abstract String getScriptTemplate();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/EqualTo.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/EqualTo.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/EqualTo.java
new file mode 100644
index 0000000..dbfb8f5
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/EqualTo.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (C) 2008 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.loop.comparisons;
+
+public class EqualTo extends Comparison {
+
+	public String getId() {
+		return "EqualTo";
+	}
+
+	public String getName() {
+		return "is equal to";
+	}
+
+	public String getScriptTemplate() {
+		return "${loopPort} = \"\" + ! ${port}.equals(${value}); ";
+	}
+
+	public String getValueType() {
+		return "string";
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsGreaterThan.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsGreaterThan.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsGreaterThan.java
new file mode 100644
index 0000000..fc6f56b
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsGreaterThan.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (C) 2008 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.loop.comparisons;
+
+public class IsGreaterThan extends Comparison {
+
+	public String getId() {
+		return "IsGreaterThan";
+	}
+
+	public String getName() {
+		return "is greater than";
+	}
+
+	public String getScriptTemplate() {
+		return "${loopPort} = \"\" + (! (Double.parseDouble(${port}) > Double.parseDouble(${value})));";
+	}
+
+	public String getValueType() {
+		return "number";
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsLessThan.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsLessThan.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsLessThan.java
new file mode 100644
index 0000000..d5fe38c
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsLessThan.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (C) 2008 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.loop.comparisons;
+
+public class IsLessThan extends Comparison {
+
+	public String getId() {
+		return "IsLessThan";
+	}
+
+	public String getName() {
+		return "is less than";
+	}
+
+	public String getScriptTemplate() {
+		return "${loopPort} = \"\" + (! (Double.parseDouble(${port}) < Double.parseDouble(${value})));";
+	}
+
+	public String getValueType() {
+		return "number";
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Matches.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Matches.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Matches.java
new file mode 100644
index 0000000..fa84aeb
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Matches.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (C) 2008 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.loop.comparisons;
+
+public class Matches extends Comparison {
+
+	public String getId() {
+		return "Matches";
+	}
+
+	public String getName() {
+		return "matches";
+	}
+
+	public String getScriptTemplate() {
+		return "${loopPort} = \"\" + ! ${port}.matches(${value});";
+	}
+
+	public String getValueType() {
+		return "regular expression";
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotEqualTo.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotEqualTo.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotEqualTo.java
new file mode 100644
index 0000000..9c73835
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotEqualTo.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (C) 2008 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.loop.comparisons;
+
+public class NotEqualTo extends Comparison {
+
+	public String getId() {
+		return "NotEqualTo";
+	}
+
+	public String getName() {
+		return "is not equal to";
+	}
+
+	public String getScriptTemplate() {
+		return "${loopPort} = \"\" + ${port}.equals(${value});";
+	}
+
+	public String getValueType() {
+		return "string";
+	}
+}
\ No newline at end of file