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

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

Repository: incubator-taverna-workbench
Updated Branches:
  refs/heads/master 4f8453f09 -> 52fd79dd4


http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Group.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Group.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Group.java
new file mode 100644
index 0000000..fa4ac87
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Group.java
@@ -0,0 +1,222 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment.model;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.jdom.Document;
+import org.jdom.Element;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.User;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class Group extends Resource
+{
+  private User admin;
+  
+  private List<Tag> tags;
+  private List<Comment> comments;
+  private List<User> members;
+  private List<Resource> sharedItems;
+  
+  
+  public Group()
+  {
+    super();
+    this.setItemType(Resource.GROUP);
+  }
+  
+  public User getAdmin() {
+    return admin;
+  }
+
+  public void setAdmin(User admin) {
+    this.admin = admin;
+  }
+  
+  public List<Tag> getTags() {
+    return this.tags;
+  }
+  
+  public List<Comment> getComments()
+  {
+    return this.comments;
+  }
+  
+  public int getSharedItemCount()
+  {
+    return this.sharedItems.size();
+  }
+  
+  public int getMemberCount()
+  {
+    return this.members.size();
+  }
+  
+  public List<Resource> getSharedItems()
+  {
+    return this.sharedItems;
+  }
+  
+  public List<User> getMembers()
+  {
+    return this.members;
+  }
+  
+  
+  /**
+   * A helper method to return a set of API elements that are
+   * needed to satisfy request of a particular type - e.g. creating
+   * a listing of resources or populating full preview, etc.
+   * 
+   * @param iRequestType A constant value from Resource class.
+   * @return Comma-separated string containing values of required API elements.
+   */
+  public static String getRequiredAPIElements(int iRequestType)
+  {
+    String strElements = "";
+    
+    // cases higher up in the list are supersets of those that come below -
+    // hence no "break" statements are required, because 'falling through' the
+    // switch statement is the desired behaviour in this case
+    switch (iRequestType) {
+      case Resource.REQUEST_FULL_PREVIEW:
+        strElements += "created-at,updated-at,members,shared-items,tags,comments,";
+      case Resource.REQUEST_FULL_LISTING:
+        strElements += "owner,";
+      case Resource.REQUEST_SHORT_LISTING:
+        strElements += "id,title,description";
+    }
+    
+    return (strElements);
+  }
+  
+  
+  public static Group buildFromXML(Document doc, Logger logger)
+  {
+    // if no XML document was supplied, return NULL
+    if(doc == null) return(null);
+    
+    // call main method which parses XML document starting from root element
+    return (Group.buildFromXML(doc.getRootElement(), logger));
+  }
+  
+  
+  //class method to build a group instance from XML
+  @SuppressWarnings("unchecked")
+  public static Group buildFromXML(Element docRootElement, Logger logger)
+  {
+    // return null to indicate an error if XML document contains no root element
+    if(docRootElement == null) return(null);
+    
+    Group g = new Group();
+
+    try {
+      // URI
+      g.setURI(docRootElement.getAttributeValue("uri"));
+      
+      // Resource URI
+      g.setResource(docRootElement.getAttributeValue("resource"));
+      
+      // Id
+      String id = docRootElement.getChildText("id");
+      if (id == null || id.equals("")) {
+        id = "API Error - No group ID supplied";
+        logger.error("Error while parsing group XML data - no ID provided for group with title: \"" + docRootElement.getChildText("title") + "\"");
+      }
+      g.setID(Integer.parseInt(id));
+      
+      // Title
+      g.setTitle(docRootElement.getChildText("title"));
+      
+      // Description
+      g.setDescription(docRootElement.getChildText("description"));
+      
+      // Owner
+      Element ownerElement = docRootElement.getChild("owner");
+      g.setAdmin(Util.instantiatePrimitiveUserFromElement(ownerElement));
+      
+      // Created at
+      String createdAt = docRootElement.getChildText("created-at");
+      if (createdAt != null && !createdAt.equals("")) {
+        g.setCreatedAt(MyExperimentClient.parseDate(createdAt));
+      }
+      
+      // Updated at
+      String updatedAt = docRootElement.getChildText("updated-at");
+      if (updatedAt != null && !updatedAt.equals("")) {
+        g.setUpdatedAt(MyExperimentClient.parseDate(updatedAt));
+      }
+      
+      
+      // Tags
+      g.tags = new ArrayList<Tag>();
+      g.getTags().addAll(Util.retrieveTags(docRootElement));
+      
+      // Comments
+      g.comments = new ArrayList<Comment>();
+      g.getComments().addAll(Util.retrieveComments(docRootElement, g));
+      
+      // Members
+      g.members = new ArrayList<User>();
+      
+      Element membersElement = docRootElement.getChild("members");
+      if (membersElement != null) {
+        List<Element> memberNodes = membersElement.getChildren();
+        for (Element e : memberNodes) {
+          g.getMembers().add(Util.instantiatePrimitiveUserFromElement(e));
+        }
+      }
+      // sort the items after all items have been added
+      Collections.sort(g.getMembers());
+      
+      
+      // Shared Items
+      g.sharedItems = new ArrayList<Resource>();
+      
+      Element sharedItemsElement = docRootElement.getChild("shared-items");
+      if (sharedItemsElement != null) {
+        List<Element> itemsNodes = sharedItemsElement.getChildren();
+        for (Element e : itemsNodes) {
+          g.getSharedItems().add(Util.instantiatePrimitiveResourceFromElement(e));
+        }
+      }
+      // sort the items after all items have been added
+      Collections.sort(g.getSharedItems());
+      
+      
+      logger.debug("Found information for group with ID: " + g.getID() + ", Title: " + g.getTitle());
+    }
+    catch (Exception e) {
+      logger.error("Failed midway through creating group object from XML", e);
+    }
+    
+    // return created group instance
+    return(g);
+  }
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/ServiceFilteringSettings.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/ServiceFilteringSettings.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/ServiceFilteringSettings.java
new file mode 100644
index 0000000..76d4eca
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/ServiceFilteringSettings.java
@@ -0,0 +1,184 @@
+package net.sf.taverna.biocatalogue.model.search;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.tree.TreePath;
+
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.biocatalogue.ui.filtertree.FilterTreeNode;
+import net.sf.taverna.biocatalogue.ui.tristatetree.JTriStateTree;
+
+/**
+ * This class provides functionality to deal with service filtering settings.
+ * Particularly used to save the current state of the filtering tree as a
+ * favourite filter.
+ * 
+ * Instances of this class hold all necessary information to restore the
+ * filtering state at a later point.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class ServiceFilteringSettings implements Comparable<ServiceFilteringSettings>, Serializable
+{
+  private static final long serialVersionUID = -5706169924295062628L;
+  
+  private String filterName;
+  private int filteringCriteriaNumber;
+  private List<TreePath> filterTreeRootsOfCheckedPaths;
+  
+  
+  
+  /**
+   * Stores current filtering selection in the provided JTriStateTree
+   * instance into the instance of this class.
+   * 
+   * @param filterTree The JTriStateTree instance to get the current selection from.
+   */
+  public ServiceFilteringSettings(JTriStateTree filterTree)
+  {
+    this(null, filterTree);
+  }
+  
+  
+  /**
+   * Stores current filtering selection in the provided JTriStateTree
+   * instance into the instance of this class.
+   * 
+   * @param filterName The name to associate with this filter.
+   * @param filterTree The JTriStateTree instance to get the current selection from.
+   */
+  @SuppressWarnings("unchecked")
+  public ServiceFilteringSettings(String filterName, JTriStateTree filterTree)
+  {
+    this.filterName = filterName;
+    
+    this.filteringCriteriaNumber = filterTree.getLeavesOfCheckedPaths().size();
+    
+    // a deep copy of the data from the filter tree is created, so that the data stored in this instance
+    // is fully independent of the filter tree itself; therefore local copy of this data may be modified
+    // as needed and will not affect the main filter (and vice versa) 
+    this.filterTreeRootsOfCheckedPaths = (List<TreePath>)Util.deepCopy(filterTree.getRootsOfCheckedPaths());
+  }
+  
+  
+  /**
+   * Analyses the filter tree and produces part of the request URL containing settings regarding filters.
+   */
+  @SuppressWarnings("unchecked")
+  public Map<String,String[]> getFilteringURLParameters()
+  {
+    // analyse filter tree to get checked elements 
+    Map<String,HashSet<String>> selections = new HashMap<String,HashSet<String>>(); 
+    
+    // cycle through the deepest selected nodes;
+    // NB! the CheckboxTree acts in a way that if A contains B,C --
+    // 1) if only B is checked, tp.getLastPathComponent() will be B;
+    // 2) if both B,C are checked, tp.getLastPathComponent() will be A;
+    for (TreePath selectedRootNodePath : getFilterTreeRootsOfCheckedPaths()) {
+      FilterTreeNode selectedNode = (FilterTreeNode)selectedRootNodePath.getLastPathComponent();
+      
+      // identify affected nodes
+      HashSet<FilterTreeNode> affectedNodes = new HashSet<FilterTreeNode>();
+      if (selectedNode.isFilterCategory()) {
+        // case as in example 2) -- need to "extract" nodes that are one level deeper
+        for (Enumeration children = selectedNode.children(); children.hasMoreElements(); ) {
+          affectedNodes.add((FilterTreeNode)children.nextElement());
+        }
+      }
+      else {
+        // case as in example 1)
+        affectedNodes.add(selectedNode);
+      }
+      
+      // walk through the identified collection of nodes and build the data structure with URL values
+      for (FilterTreeNode node : affectedNodes) {
+        if (selections.containsKey(node.getType())) {
+          selections.get(node.getType()).add(node.getUrlValue());
+        }
+        else {
+          HashSet<String> newSet = new HashSet<String>();
+          newSet.add(node.getUrlValue());
+          
+          selections.put(node.getType(), newSet);
+        }
+      }
+    }
+    
+    
+    // now use the constructed set of data to build the map of filtering URL parameters
+    Map<String,String[]> filterUrlParameters = new HashMap<String,String[]>();
+    for(String key : selections.keySet())
+    {
+      List<String> categoryValues = new ArrayList<String>();
+      for (String value : selections.get(key)) {
+        categoryValues.add(value);
+      }
+      
+      filterUrlParameters.put(key, categoryValues.toArray(new String[0]));
+    }
+    
+    return (filterUrlParameters);
+  }
+  
+  
+  // *** Getters ***
+  
+  public String getFilterName() {
+    return (this.filterName == null || filterName.length() == 0 ? "untitled filter" : this.filterName);
+  }
+  
+  public List<TreePath> getFilterTreeRootsOfCheckedPaths() {
+    return filterTreeRootsOfCheckedPaths;
+  }
+  
+  /**
+   * @return Number of filtering criteria within the current filter.
+   */
+  public int getNumberOfFilteringCriteria() {
+    return filteringCriteriaNumber;
+  }
+  
+  // *** End of getters ***
+  
+  
+  public boolean equals(Object other)
+  {
+    if (other instanceof ServiceFilteringSettings)
+    {
+      ServiceFilteringSettings o = (ServiceFilteringSettings)other;
+      return (this.filterName.equals(o.filterName) &&
+              this.filterTreeRootsOfCheckedPaths.equals(o.filterTreeRootsOfCheckedPaths));
+    }
+    else {
+      return false;
+    }
+  }
+  
+  
+  public int compareTo(ServiceFilteringSettings other)
+  {
+    int iOrdering = this.filterName.compareTo(other.filterName);
+    if (iOrdering == 0) {
+      iOrdering = this.getNumberOfFilteringCriteria() - other.getNumberOfFilteringCriteria();
+    }
+    
+    // inverse order, as the traversal of lists in the favourite filters panel is
+    // done this way round
+    return (-1 * iOrdering);
+  }
+  
+  
+  public String toString() {
+    return ("Filter: '" + getFilterName() + "' [" + detailsAsString() + "]");
+  }
+  
+  public String detailsAsString() {
+    return (getNumberOfFilteringCriteria() + " filtering criteria");
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/AnnotationBean.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/AnnotationBean.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/AnnotationBean.java
new file mode 100644
index 0000000..81ce849
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/AnnotationBean.java
@@ -0,0 +1,52 @@
+package net.sf.taverna.biocatalogue.test;
+
+
+public class AnnotationBean
+{
+  public AnnotationBean() { }
+  
+  public String self;
+  private int version;
+  private String created;
+  public Annotatable annotatable;
+  private Source source;
+  private Attribute attribute;
+  private Value value;
+  
+  
+  public static class Annotatable
+  {
+    private Annotatable() { }
+    
+    private String name;
+    public String resource;
+    private String type;
+  }
+  
+  public static class Source
+  {
+    private Source() { }
+    
+    private String name;
+    private String resource;
+    private String type;
+  }
+  
+  public static class Attribute
+  {
+    private Attribute() { }
+    
+    private String name;
+    private String resource;
+    private String identifier;
+  }
+  
+  public static class Value
+  {
+    private Value() { }
+    
+    private String resource;
+    private String type;
+    private String content;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/DrawDefaultIconTest.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/DrawDefaultIconTest.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/DrawDefaultIconTest.java
new file mode 100644
index 0000000..1bec9e6
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/DrawDefaultIconTest.java
@@ -0,0 +1,38 @@
+package net.sf.taverna.biocatalogue.test;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.image.BufferedImage;
+
+import javax.swing.ImageIcon;
+import javax.swing.JOptionPane;
+
+public class DrawDefaultIconTest {
+
+  /**
+   * @param args
+   */
+  public static void main(String[] args)
+  {
+    int w = 16;
+    int h = 16;
+    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+    GraphicsDevice gd = ge.getDefaultScreenDevice();
+    GraphicsConfiguration gc = gd.getDefaultConfiguration();
+    
+    BufferedImage image = gc.createCompatibleImage(w, h, BufferedImage.TYPE_INT_ARGB);
+    Graphics2D g = image.createGraphics();
+    g.setColor(Color.RED);
+    g.setStroke(new BasicStroke(3, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER));
+    g.drawLine(4, 4, 12, 12);
+    g.drawLine(12, 4, 4, 12);
+    g.dispose();
+    
+    JOptionPane.showMessageDialog(null, new ImageIcon(image)); 
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest.java
new file mode 100644
index 0000000..eba01d6
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest.java
@@ -0,0 +1,19 @@
+package net.sf.taverna.biocatalogue.test;
+
+import com.google.gson.Gson;
+
+public class GSONTest
+{
+
+  public static void main(String[] args) throws Exception
+  {
+    String json = "[{\"annotatable\":{\"name\":\"IndexerService\",\"resource\":\"http://sandbox.biocatalogue.org/services/2158\",\"type\":\"Service\"},\"self\":\"http://sandbox.biocatalogue.org/annotations/47473\",\"value\":{\"resource\":\"http://sandbox.biocatalogue.org/tags/indexing\",\"type\":\"Tag\",\"content\":\"indexing\"},\"version\":1,\"created\":\"2010-01-13T09:24:04Z\",\"source\":{\"name\":\"Marco Roos\",\"resource\":\"http://sandbox.biocatalogue.org/users/48\",\"type\":\"User\"},\"attribute\":{\"name\":\"Tag\",\"resource\":\"http://sandbox.biocatalogue.org/annotation_attributes/2\",\"identifier\":\"http://www.biocatalogue.org/attribute#Category\"}}]";
+    
+    Gson gson = new Gson();
+    AnnotationBean[] a = gson.fromJson(json, AnnotationBean[].class);
+    
+    System.out.println("Self URL: " + a[0].self);
+    System.out.println("Annotatable resource: " + a[0].annotatable.resource);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest_exportingJSON.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest_exportingJSON.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest_exportingJSON.java
new file mode 100644
index 0000000..6e63ebb
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest_exportingJSON.java
@@ -0,0 +1,30 @@
+package net.sf.taverna.biocatalogue.test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sf.taverna.biocatalogue.model.connectivity.BeanForPOSTToFilteredIndex;
+
+import com.google.gson.Gson;
+
+public class GSONTest_exportingJSON
+{
+
+  /**
+   * @param args
+   */
+  public static void main(String[] args)
+  {
+    Map<String, String[]> m = new HashMap<String, String[]>();
+    m.put("a", new String[] {"b","c"});
+    m.put("d", new String[] {"e","f"});
+    
+    BeanForPOSTToFilteredIndex b = new BeanForPOSTToFilteredIndex();
+    b.filters = m;
+    
+    Gson gson = new Gson();
+    System.out.println(gson.toJson(b));
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest_forSoapOperationsIndex.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest_forSoapOperationsIndex.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest_forSoapOperationsIndex.java
new file mode 100644
index 0000000..30035f7
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/GSONTest_forSoapOperationsIndex.java
@@ -0,0 +1,27 @@
+package net.sf.taverna.biocatalogue.test;
+
+import net.sf.taverna.biocatalogue.model.Resource;
+import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI;
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+
+public class GSONTest_forSoapOperationsIndex
+{
+
+  /**
+   * @param args
+   * @throws Exception
+   */
+  public static void main(String[] args) throws Exception
+  {
+    BioCatalogueClient client = BioCatalogueClient.getInstance(); 
+    
+    String url = BioCatalogueClient.API_SOAP_OPERATIONS_URL;
+//    url = Util.appendURLParameter(url, "q", "blast");
+    BeansForJSONLiteAPI.ResourceIndex soapOpIndex = client.getBioCatalogueResourceLiteIndex(Resource.TYPE.SOAPOperation, url);
+    
+    System.out.println("result count: " + soapOpIndex.getResources().length + "\n\n");
+//    System.out.println(soapOpIndex.soap_operations[1].getName() + "\n" + soapOpIndex.soap_operations[1].getURL() + "\n\n");
+    
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/JWaitDialogTest.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/JWaitDialogTest.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/JWaitDialogTest.java
new file mode 100644
index 0000000..2aed586
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/JWaitDialogTest.java
@@ -0,0 +1,36 @@
+package net.sf.taverna.biocatalogue.test;
+
+import net.sf.taverna.biocatalogue.ui.JWaitDialog;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponent;
+
+public class JWaitDialogTest
+{
+
+  public static void main(String[] args)
+  {
+    System.out.println("start test");
+    
+    final JWaitDialog jwd = new JWaitDialog(MainComponent.dummyOwnerJFrame, "Old title", "Please wait... Please wait... Please wait... Please wait...");
+    
+    // NB! Background process must be started before the modal dialog box
+    //     is made visible - otherwise processing freezes.
+    new Thread("testing delayed update of JWaitDialog")
+    {
+      public void run()
+      {
+        // wait for some time
+        try { Thread.sleep(3000); }
+        catch (InterruptedException e) { /* do nothing */ }
+        
+        // update the dialog
+        jwd.setTitle("New title");
+        jwd.waitFinished("Great, all done!");
+        
+        System.out.println("end test");
+      }
+    }.start();
+    
+    jwd.setVisible(true);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/LinkedListEqualsTest.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/LinkedListEqualsTest.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/LinkedListEqualsTest.java
new file mode 100644
index 0000000..6f252bd
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/LinkedListEqualsTest.java
@@ -0,0 +1,25 @@
+package net.sf.taverna.biocatalogue.test;
+
+import java.util.LinkedList;
+
+public class LinkedListEqualsTest
+{
+  public static void main(String[] args)
+  {
+    LinkedList l = new LinkedList();
+    
+    String a = new String("test1");
+    String b = new String("test2");
+    
+    System.out.println(a == b);
+    System.out.println(a.equals(b));
+    
+    l.add(a);
+    l.add(b);
+    
+    System.out.println(l);
+    System.out.println(l.indexOf(a));
+    System.out.println(l.indexOf(b));
+    
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestAPICaller.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestAPICaller.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestAPICaller.java
new file mode 100644
index 0000000..a48a996
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestAPICaller.java
@@ -0,0 +1,241 @@
+package net.sf.taverna.biocatalogue.test;
+
+import javax.swing.JFrame;
+import java.awt.Dimension;
+import javax.swing.JPanel;
+import java.awt.BorderLayout;
+import javax.swing.JTextField;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JOptionPane;
+import javax.swing.JTextPane;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+
+import java.awt.GridBagLayout;
+import java.awt.GridBagConstraints;
+import java.awt.Insets;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class TestAPICaller extends JFrame implements ActionListener {
+
+	private JPanel jContentPane = null;
+	private JTextField tfURL = null;
+	private JButton bSubmitRequest = null;
+	private JButton bClear = null;
+	private JScrollPane spOutputPane = null;
+	private JTextPane tpOutputPane = null;
+
+	/**
+	 * This method initializes 
+	 * 
+	 */
+	public TestAPICaller() {
+		super();
+		initialize();
+	}
+
+	/**
+	 * This method initializes this
+	 * 
+	 */
+	private void initialize() {
+        this.setSize(new Dimension(515, 321));
+        this.setTitle("Test Service Catalogue API Caller");
+        this.setContentPane(getJContentPane());
+		
+        this.bSubmitRequest.setDefaultCapable(true);
+        this.getRootPane().setDefaultButton(bSubmitRequest);
+	}
+
+	/**
+	 * This method initializes jContentPane	
+	 * 	
+	 * @return javax.swing.JPanel	
+	 */
+	private JPanel getJContentPane() {
+		if (jContentPane == null) {
+			GridBagConstraints gridBagConstraints3 = new GridBagConstraints();
+			gridBagConstraints3.fill = GridBagConstraints.BOTH;
+			gridBagConstraints3.gridwidth = 2;
+			gridBagConstraints3.gridx = 0;
+			gridBagConstraints3.gridy = 2;
+			gridBagConstraints3.ipadx = 459;
+			gridBagConstraints3.ipady = 182;
+			gridBagConstraints3.weightx = 1.0;
+			gridBagConstraints3.weighty = 1.0;
+			gridBagConstraints3.insets = new Insets(4, 12, 9, 12);
+			GridBagConstraints gridBagConstraints2 = new GridBagConstraints();
+			gridBagConstraints2.fill = GridBagConstraints.HORIZONTAL;
+			gridBagConstraints2.gridy = 1;
+			gridBagConstraints2.ipadx = 0;
+			gridBagConstraints2.ipady = 0;
+			gridBagConstraints2.insets = new Insets(0, 5, 7, 12);
+			gridBagConstraints2.weightx = 0.5;
+			gridBagConstraints2.gridx = 1;
+			GridBagConstraints gridBagConstraints1 = new GridBagConstraints();
+			gridBagConstraints1.fill = GridBagConstraints.HORIZONTAL;
+			gridBagConstraints1.gridy = 1;
+			gridBagConstraints1.ipadx = 0;
+			gridBagConstraints1.ipady = 0;
+			gridBagConstraints1.insets = new Insets(0, 12, 7, 5);
+			gridBagConstraints1.weightx = 0.5;
+			gridBagConstraints1.gridx = 0;
+			GridBagConstraints gridBagConstraints = new GridBagConstraints();
+			gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+			gridBagConstraints.gridwidth = 2;
+			gridBagConstraints.gridx = 0;
+			gridBagConstraints.gridy = 0;
+			gridBagConstraints.ipadx = 466;
+			gridBagConstraints.weightx = 1.0;
+			gridBagConstraints.ipady = 3;
+			gridBagConstraints.insets = new Insets(13, 12, 8, 12);
+			jContentPane = new JPanel();
+			jContentPane.setLayout(new GridBagLayout());
+			jContentPane.add(getTfURL(), gridBagConstraints);
+			jContentPane.add(getBSubmitRequest(), gridBagConstraints1);
+			jContentPane.add(getBClear(), gridBagConstraints2);
+			jContentPane.add(getSpOutputPane(), gridBagConstraints3);
+		}
+		return jContentPane;
+	}
+
+	/**
+	 * This method initializes tfURL	
+	 * 	
+	 * @return javax.swing.JTextField	
+	 */
+	private JTextField getTfURL() {
+		if (tfURL == null) {
+			tfURL = new JTextField();
+			tfURL.setText(BioCatalogueClient.DEFAULT_API_SANDBOX_BASE_URL);
+		}
+		return tfURL;
+	}
+
+	/**
+	 * This method initializes tfSubmitRequest	
+	 * 	
+	 * @return javax.swing.JButton	
+	 */
+	private JButton getBSubmitRequest() {
+		if (bSubmitRequest == null) {
+			bSubmitRequest = new JButton();
+			bSubmitRequest.setText("Submit Request");
+			bSubmitRequest.addActionListener(this);
+		}
+		return bSubmitRequest;
+	}
+	
+	
+	/**
+	 * This method initializes bClear	
+	 * 	
+	 * @return javax.swing.JButton	
+	 */
+	private JButton getBClear() {
+		if (bClear == null) {
+			bClear = new JButton();
+			bClear.setText("Clear Output");
+			bClear.addActionListener(this);
+		}
+		return bClear;
+	}
+	
+	
+	/**
+	 * This method initializes tpOutputPane	
+	 * 	
+	 * @return javax.swing.JTextPane	
+	 */
+	private JTextPane getTpOutputPane() {
+		if (tpOutputPane == null) {
+			tpOutputPane = new JTextPane();
+			tpOutputPane.setContentType("text/plain");
+		}
+		return tpOutputPane;
+	}
+	
+
+	/**
+	 * This method initializes spOutputPane	
+	 * 	
+	 * @return javax.swing.JScrollPane	
+	 */
+	private JScrollPane getSpOutputPane() {
+		if (spOutputPane == null) {
+			spOutputPane = new JScrollPane();
+			spOutputPane.setViewportView(getTpOutputPane());
+		}
+		return spOutputPane;
+	}
+
+	
+	// ACTION LISTENER
+	
+	public void actionPerformed(ActionEvent e) {
+		if (e.getSource().equals(bSubmitRequest)) {
+			tfURL.selectAll();
+			
+			// call the actual test method
+			runBioCatalogueAPITest(tfURL.getText());
+		}
+		else if (e.getSource().equals(bClear)) {
+			this.tpOutputPane.setText("");
+		}
+		
+	}
+
+
+	// ACTUAL TEST CLASS
+	
+	private void runBioCatalogueAPITest(String url) {
+		final String urlFinal = url;
+		new Thread("making request") {
+  		public void run() {
+  		  tpOutputPane.setText("Initialising Service Catalogue client...");
+  		  BioCatalogueClient client = null;
+        try {
+          client = BioCatalogueClient.getInstance();
+        }
+        catch (Exception e) {
+          e.printStackTrace();
+        }
+        
+    		final StringBuilder text = new StringBuilder();
+    		try {
+    		  tpOutputPane.setText("Sending request...");
+    		  BufferedReader br = new BufferedReader(new InputStreamReader(client.doBioCatalogueGET(urlFinal).getResponseStream()));
+    		  String str = "";
+    		  
+    		  while ((str = br.readLine()) != null) {
+    		    text.append(str + "\n");
+    		  }
+    		  
+    		  br.close();
+    		}
+    		catch (Exception e) {
+    		  text.append(e);
+    		}
+    		
+    		SwingUtilities.invokeLater(new Runnable() {
+    		  public void run() {
+    		    tpOutputPane.setText(text.toString());
+    		  }
+    		});
+  		}
+		}.start();
+		
+	}
+
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestDoubleUsageOfSameSwingElement.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestDoubleUsageOfSameSwingElement.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestDoubleUsageOfSameSwingElement.java
new file mode 100644
index 0000000..e5ad39c
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestDoubleUsageOfSameSwingElement.java
@@ -0,0 +1,32 @@
+package net.sf.taverna.biocatalogue.test;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+
+public class TestDoubleUsageOfSameSwingElement extends JFrame
+{
+  private String text = "abc";
+  boolean bLowerCase = true;
+  
+  public TestDoubleUsageOfSameSwingElement()
+  {
+    final JButton bBtn = new JButton(text);
+    bBtn.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        bLowerCase = !bLowerCase;
+        bBtn.setText(bLowerCase ? text.toLowerCase() : text.toUpperCase());
+      }
+    });
+    
+    this.setLayout(new BorderLayout());
+    this.add(bBtn, BorderLayout.WEST);
+    this.add(bBtn, BorderLayout.EAST);
+    
+    this.pack();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestUtilURLHandling.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestUtilURLHandling.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestUtilURLHandling.java
new file mode 100644
index 0000000..71a6ca7
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestUtilURLHandling.java
@@ -0,0 +1,87 @@
+package net.sf.taverna.biocatalogue.test;
+
+import net.sf.taverna.biocatalogue.model.Util;
+
+public class TestUtilURLHandling
+{
+  public static void main(String[] args)
+  {
+    String url1 = "http://sandbox.biocatalogue.org/search";
+    String url2 = "http://sandbox.biocatalogue.org/search?";
+    String url3 = "http://sandbox.biocatalogue.org/search?q=";
+    String url4 = "http://sandbox.biocatalogue.org/search?q=franck";
+    String url5 = "http://sandbox.biocatalogue.org/services?tag=%5Bbiology%5D";
+    String url6 = "http://sandbox.biocatalogue.org/services?tag=%5B%3Chttp%3A%2F%2Fwww.mygrid.org.uk%2Fontology%23DDBJ%3E%5D";
+    
+    
+    System.out.println("----------------------------");
+    System.out.println("Extracting URL parameters:\n");
+    
+    
+    System.out.println(url1 + "\nParameter map:\n" + Util.extractURLParameters(url1));
+    System.out.println("Reconstructed query string from parameter map: " + Util.constructURLQueryString(Util.extractURLParameters(url1)) + "\n");
+    
+    
+    System.out.println(url2 + "\nParameter map:\n" + Util.extractURLParameters(url2));
+    System.out.println("Reconstructed query string from parameter map: " + Util.constructURLQueryString(Util.extractURLParameters(url2)) + "\n");
+    
+    
+    System.out.println(url3 + "\nParameter map:\n" + Util.extractURLParameters(url3));
+    System.out.println("Reconstructed query string from parameter map: " + Util.constructURLQueryString(Util.extractURLParameters(url3)) + "\n");
+    
+    
+    System.out.println(url4 + "\nParameter map:\n" + Util.extractURLParameters(url4));
+    System.out.println("Reconstructed query string from parameter map: " + Util.constructURLQueryString(Util.extractURLParameters(url4)) + "\n");
+    
+    
+    System.out.println(url5 + "\nParameter map:\n" + Util.extractURLParameters(url5));
+    System.out.println("Reconstructed query string from parameter map: " + Util.constructURLQueryString(Util.extractURLParameters(url5)) + "\n");
+    
+    
+    System.out.println("\n\n----------------------------");
+    System.out.println("Adding parameters:\n");
+    
+    String newUrl = Util.appendURLParameter(url1, "testParam", "testValue");
+    System.out.println(url1 + "\n" + newUrl + "\n");
+    
+    newUrl = Util.appendURLParameter(url2, "testParam", "testValue");
+    System.out.println(url2 + "\n" + newUrl + "\n");
+    
+    newUrl = Util.appendURLParameter(url3, "testParam", "testValue");
+    System.out.println(url3 + "\n" + newUrl + "\n");
+    
+    newUrl = Util.appendURLParameter(url4, "testParam", "testValue");
+    System.out.println(url4 + "\n" + newUrl + "\n");
+    
+    newUrl = Util.appendURLParameter(url5, "testParam", "testValue");
+    System.out.println(url5 + "\n" + newUrl + "\n");
+    
+    
+    System.out.println("\n\n----------------------------");
+    System.out.println("Getting parameter values:\n");
+    
+    System.out.println("Value of '" + "testParam" + "' in the URL: " + url1 + " -- " + Util.extractURLParameter(url1, "testParam"));
+    System.out.println("Value of '" + "testParam" + "' in the URL: " + url2 + " -- " + Util.extractURLParameter(url2, "testParam"));
+    System.out.println("Value of '" + "q" + "' in the URL: " + url3 + " -- " + Util.extractURLParameter(url3, "q"));
+    System.out.println("Value of '" + "q" + "' in the URL: " + url4 + " -- " + Util.extractURLParameter(url4, "q"));
+    System.out.println("Value of '" + "tag" + "' in the URL: " + url5 + " -- " + Util.extractURLParameter(url5, "tag"));
+    
+    
+    System.out.println("\n\n----------------------------");
+    System.out.println("URL decoding:\n");
+    
+    System.out.println("Original URL: " + url6 + "\nDecoded URL: " + Util.urlDecodeQuery(url6));
+    
+    
+    System.out.println("\n\n----------------------------");
+    System.out.println("Appending a string before URL parameters:\n");
+    
+    String strToAppend = ".xml";
+    System.out.println("Appending '" + strToAppend + "' in the URL: " + url1 + " -- " + Util.appendStringBeforeParametersOfURL(url1, strToAppend));
+    System.out.println("Appending '" + strToAppend + "' in the URL: " + url2 + " -- " + Util.appendStringBeforeParametersOfURL(url2, strToAppend));
+    System.out.println("Appending '" + strToAppend + "' in the URL: " + url3 + " -- " + Util.appendStringBeforeParametersOfURL(url3, strToAppend));
+    System.out.println("Appending '" + strToAppend + "' in the URL: " + url4 + " -- " + Util.appendStringBeforeParametersOfURL(url4, strToAppend));
+    System.out.println("Appending '" + strToAppend + "' in the URL: " + url5 + " -- " + Util.appendStringBeforeParametersOfURL(url5, strToAppend));
+    System.out.println("Appending '" + strToAppend + "' in the URL: " + url6 + " -- " + Util.appendStringBeforeParametersOfURL(url6, strToAppend));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestXHTMLRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestXHTMLRenderer.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestXHTMLRenderer.java
new file mode 100644
index 0000000..a50254e
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/TestXHTMLRenderer.java
@@ -0,0 +1,42 @@
+package net.sf.taverna.biocatalogue.test;
+
+import java.awt.Container;
+import java.io.File;
+
+import javax.swing.JFrame;
+
+import org.xhtmlrenderer.simple.FSScrollPane;
+import org.xhtmlrenderer.simple.XHTMLPanel;
+
+public class TestXHTMLRenderer extends JFrame {
+  public TestXHTMLRenderer() {
+    try {
+      init();
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+  
+  public void init() throws Exception {
+    Container contentPane = this.getContentPane();
+    
+    XHTMLPanel panel = new XHTMLPanel();
+    panel.getSharedContext().getTextRenderer().setSmoothingThreshold(0); // Anti-aliasing for all font sizes
+    panel.setDocument(new File("c:\\Temp\\MyExperiment\\T2 BioCatalogue Plugin\\BioCatalogue Plugin\\resources\\test.html"));
+    
+    FSScrollPane scroll = new FSScrollPane(panel);
+    contentPane.add(scroll);
+    
+    this.setTitle("XHTML rendered test");
+    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+    this.pack();
+    this.setSize(1024, 768);
+  }
+  
+  
+  public static void main(String[] args) {
+    JFrame f = new TestXHTMLRenderer();
+    f.setVisible(true);
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/WrappableJLabelTest.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/WrappableJLabelTest.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/WrappableJLabelTest.java
new file mode 100644
index 0000000..22afbd4
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/WrappableJLabelTest.java
@@ -0,0 +1,35 @@
+package net.sf.taverna.biocatalogue.test;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+public class WrappableJLabelTest extends JFrame
+{
+  public WrappableJLabelTest() {
+    
+    // depending on the LayoutManager of the container, JLabel may
+    // be resized or simply "cut off" on the edges - e.g. FlowLayout
+    // cuts it off, BorderLayout does the resizing
+    JPanel jpTestPanel = new JPanel(new BorderLayout());
+    jpTestPanel.add(new JLabel("<html><span color=\"red\">a very long</span> text that <b>is just</b> " +
+        "showing how the whole thing looks - will it wrap text or not; this " +
+    "is the question</html>"), BorderLayout.CENTER);
+    
+    this.getContentPane().add(jpTestPanel);
+    
+    this.pack();
+  }
+  
+  public static void main(String[] args)
+  {
+    WrappableJLabelTest f = new WrappableJLabelTest();
+    f.setLocationRelativeTo(null);
+    f.setPreferredSize(new Dimension(400, 300));
+    f.setVisible(true);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/XStreamTest.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/XStreamTest.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/XStreamTest.java
new file mode 100644
index 0000000..edc1d5a
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/test/XStreamTest.java
@@ -0,0 +1,32 @@
+package net.sf.taverna.biocatalogue.test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.io.xml.DomDriver;
+
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+
+
+public class XStreamTest
+{
+
+  public static void main(String[] args)
+  {
+    List<SoapOperationIdentity> processors = new ArrayList<SoapOperationIdentity>();
+    processors.add(new SoapOperationIdentity("http://www.test.com/test.wsdl", "aa", null));
+    processors.add(new SoapOperationIdentity("http://www.example.com/example.wsdl", "bb", null));
+    
+    XStream xstream = new XStream(new DomDriver());
+    String xml = xstream.toXML(processors);
+    
+    System.out.println(xml);
+    
+    List<SoapOperationIdentity> processorsFromXML = (List<SoapOperationIdentity>)xstream.fromXML(xml);
+    System.out.println("\n\n");
+    System.out.println(processorsFromXML.get(0).getWsdlLocation() + " - " + processorsFromXML.get(0).getOperationName());
+    System.out.println(processorsFromXML.get(1).getWsdlLocation() + " - " + processorsFromXML.get(1).getOperationName());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/BioCatalogueExplorationTab.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/BioCatalogueExplorationTab.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/BioCatalogueExplorationTab.java
new file mode 100644
index 0000000..d3f452d
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/BioCatalogueExplorationTab.java
@@ -0,0 +1,131 @@
+package net.sf.taverna.biocatalogue.ui;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.BorderFactory;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.LayoutFocusTraversalPolicy;
+
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+import net.sf.taverna.biocatalogue.ui.search_results.SearchResultsMainPanel;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponent;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class BioCatalogueExplorationTab extends JPanel implements HasDefaultFocusCapability
+{
+  private final MainComponent pluginPerspectiveMainComponent;
+  private final BioCatalogueClient client;
+  private final Logger logger;
+  
+  
+  // COMPONENTS
+  private BioCatalogueExplorationTab thisPanel;
+  
+  private SearchOptionsPanel searchOptionsPanel;
+  private SearchResultsMainPanel tabbedSearchResultsPanel;
+  
+  
+  public BioCatalogueExplorationTab()
+  {
+    this.thisPanel = this;
+    
+    this.pluginPerspectiveMainComponent = MainComponentFactory.getSharedInstance();
+    this.client = BioCatalogueClient.getInstance();
+    this.logger = Logger.getLogger(this.getClass());
+    
+    initialiseUI();
+    
+    // this is to make sure that search will get focused when this tab is opened
+    // -- is a workaround to a bug in JVM
+    setFocusCycleRoot(true);
+    setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
+      public Component getDefaultComponent(Container cont) {
+          return (thisPanel.getDefaultComponent());
+      }
+    });
+  }
+  
+  
+  private void initialiseUI()
+  {
+    this.tabbedSearchResultsPanel = new SearchResultsMainPanel();
+    this.searchOptionsPanel = new SearchOptionsPanel(tabbedSearchResultsPanel);
+    
+    
+    this.setLayout(new GridBagLayout());
+    GridBagConstraints c = new GridBagConstraints();
+    
+    c.gridx = 0;
+    c.gridy = 0;
+    c.weightx = 0.0;
+    c.anchor = GridBagConstraints.WEST;
+    c.insets = new Insets(3,10,3,10);
+    String baseString= "<html><b>Using service catalogue at </b>" + client.getBaseURL() + "</html>";
+    this.add(new JLabel(baseString), c);
+
+    
+    c.gridx = 1;
+    c.gridy = 0;
+    c.weightx = 0.1;
+    c.fill = GridBagConstraints.HORIZONTAL;
+    c.anchor = GridBagConstraints.EAST;
+    c.insets = new Insets(3,30,3,10);
+    
+    this.add(searchOptionsPanel, c);
+    
+    c.insets = new Insets(0,0,0,0);
+    c.gridy++;
+    c.gridx = 0;
+    c.gridwidth = 2;
+    c.weightx = c.weighty = 1.0;
+    c.fill = GridBagConstraints.BOTH;
+    c.anchor = GridBagConstraints.CENTER;
+    this.add(tabbedSearchResultsPanel, c);
+    
+    this.setBorder(BorderFactory.createEmptyBorder(20, 10, 10, 10));
+  }
+  
+  
+  public SearchResultsMainPanel getTabbedSearchResultsPanel() {
+    return tabbedSearchResultsPanel;
+  }
+  
+  
+  
+  // *** Callbacks for HasDefaultFocusCapability interface ***
+  
+  public void focusDefaultComponent() {
+    this.searchOptionsPanel.focusDefaultComponent();
+  }
+  
+  public Component getDefaultComponent() {
+    return (this.searchOptionsPanel.getDefaultComponent());
+  }
+  
+  // *********************************************************
+  
+  
+  public static void main(String[] args) {
+    JFrame f = new JFrame();
+    f.getContentPane().add(new BioCatalogueExplorationTab());
+    f.setSize(1000, 800);
+    f.setLocationRelativeTo(null);
+    
+    f.setVisible(true);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/HasDefaultFocusCapability.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/HasDefaultFocusCapability.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/HasDefaultFocusCapability.java
new file mode 100644
index 0000000..d8915d5
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/HasDefaultFocusCapability.java
@@ -0,0 +1,15 @@
+package net.sf.taverna.biocatalogue.ui;
+
+import java.awt.Component;
+
+/**
+ * Indicates that the class which implements this interface will focus default
+ * component (as if the component represented by that class was activated).
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public interface HasDefaultFocusCapability
+{
+  public void focusDefaultComponent();
+  public Component getDefaultComponent();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/JClickableLabel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/JClickableLabel.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/JClickableLabel.java
new file mode 100644
index 0000000..0cc9246
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/JClickableLabel.java
@@ -0,0 +1,172 @@
+package net.sf.taverna.biocatalogue.ui;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.EventListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.Icon;
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class JClickableLabel extends JLabel implements MouseListener
+{
+  /**
+   * Default height of the JClickableLabel - calculated based on 16-pixel
+   * standard square icon and 3-pixel thick padding on top / bottom of element.
+   */
+  public static final int DEFAULT_HEIGHT = 22;
+  
+  public static final Color DEFAULT_REGULAR_FOREGROUND_COLOR = Color.BLUE;
+  public static final Color DEFAULT_HOVER_FOREGROUND_COLOR = new Color(133, 53, 53);
+  
+  
+  // This will hold the data which is relevant to processing the 'click' event on this label
+  private final String strData;
+  
+  // This will hold a reference to ResourcePreviewBrowser instance that is supposed to process the clicks
+  // on JClickableLabels
+  private ActionListener clickHandler;
+  
+  
+  private Color REGULAR_FOREGROUND_COLOR = DEFAULT_REGULAR_FOREGROUND_COLOR;
+  private Color HOVER_FOREGROUND_COLOR = DEFAULT_HOVER_FOREGROUND_COLOR;
+  
+  
+  public JClickableLabel(String strLabel, String strDataForAction, EventListener eventHandler)
+  {
+    this(strLabel, strDataForAction, eventHandler, null);
+  }
+  
+  public JClickableLabel(String strLabel, String strDataForAction, EventListener eventHandler, Icon icon)
+  {
+    this(strLabel, strDataForAction, eventHandler, icon, SwingUtilities.LEFT);
+  }
+  
+  public JClickableLabel(String strLabel, String strDataForAction, EventListener eventHandler, Icon icon, int horizontalAlignment)
+  {
+    this(strLabel, strDataForAction, eventHandler, icon, horizontalAlignment, null);
+  }
+  
+  /**
+   * 
+   * @param strLabel Textual label that will be visible in the UI.
+   * @param strDataForAction Data that will be passed to eventHandler when click on the label is made.
+   * @param eventHandler ActionListener that will process clicks on this label.
+   * @param icon Icon to display in the label.
+   * @param horizontalAlignment This is one of SwingConstants: LEFT, CENTER, RIGHT, LEADING or TRAILING
+   * @param strTooltip Tooltip to show over the label - if none is provided (e.g. null value), the strLabel will be used as a tooltip.
+   */
+  public JClickableLabel(String strLabel, String strDataForAction, EventListener eventHandler, Icon icon, int horizontalAlignment, String strTooltip)
+  {
+    super(strLabel, icon, horizontalAlignment);
+    
+    this.strData = strDataForAction;
+    this.clickHandler = (ActionListener)eventHandler;
+    
+    // empty border at the top and bottom will simulate "line-spacing"
+    // (this is only needed when an icon is displayed)
+    if (icon != null) {
+      this.setBorder(BorderFactory.createEmptyBorder(3, 0, 3, 0));
+    }
+    
+    // the tooltip for now only shows the full label text
+    this.setToolTipText(strTooltip == null ? strLabel : strTooltip);
+    this.setForeground(REGULAR_FOREGROUND_COLOR);
+    this.addMouseListener(this);
+  }
+  
+  
+  public void setRegularForegroundColor(Color regularForegroundColor)
+  {
+    REGULAR_FOREGROUND_COLOR = regularForegroundColor;
+    
+    // apply the new foreground color immediately
+    this.setForeground(REGULAR_FOREGROUND_COLOR);
+  }
+
+  public Color getRegularForegroundColor() {
+    return REGULAR_FOREGROUND_COLOR;
+  }
+  
+  
+  public void setHoverForegroundColor(Color hoverForegroundColor)
+  {
+    // will be applied the next time mouse hovers over this label
+    HOVER_FOREGROUND_COLOR = hoverForegroundColor;
+  }
+
+  public Color getHoverForegroundColor() {
+    return HOVER_FOREGROUND_COLOR;
+  }
+  
+  
+  /**
+   * @return The "hidden" string value that is normally sent as an <code>ActionCommand</code>
+   *         within <code>ActionEvent</code> when JClickableLabel is clicked.
+   */
+  public String getData() {
+    return (this.strData);
+  }
+  
+  
+  /**
+   * @return String value of the label that this JClickableLabel would have in the UI.
+   */
+  public String toString() {
+    return (this.getText());
+  }
+  
+  
+  /* This class extends JLabel, so it can't extend MouseAdapter;
+   * therefore, empty methods will be added for not useful callbacks
+   * from the MouseListener interface.
+   */
+  public void mouseClicked(MouseEvent e) 
+  {
+    // call 'actionPerformed' method on the clickHandler instance that was supplied
+    // on creation of the JClickableLabel instance
+    this.clickHandler.actionPerformed(new ActionEvent(this, e.getID(), this.strData));
+  }
+  
+  public void mouseEntered(MouseEvent e) 
+  {
+    this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)) ;
+    this.setForeground(HOVER_FOREGROUND_COLOR);
+  }
+  
+  public void mouseExited(MouseEvent e) 
+  {
+    this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)) ;
+    this.setForeground(REGULAR_FOREGROUND_COLOR);
+  }
+  
+  public void mousePressed(MouseEvent e) 
+  {
+    // do nothing
+  }
+  
+  public void mouseReleased(MouseEvent e) 
+  {
+    // do nothing
+  }
+  
+  
+  /**
+   * @return A dummy instance of JClickable label - only intended to
+   *         represent an object of this class; doesn't have a click handler,
+   *         so a click on it will result in a <code>NullPointerException</code>.
+   */
+  public static JClickableLabel getDummyInstance() {
+    return (new JClickableLabel("dummy", "", null));
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/JWaitDialog.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/JWaitDialog.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/JWaitDialog.java
new file mode 100644
index 0000000..526066b
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/JWaitDialog.java
@@ -0,0 +1,234 @@
+package net.sf.taverna.biocatalogue.ui;
+
+import java.awt.BorderLayout;
+//import java.awt.Dimension;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.UIManager;
+
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.t2.workbench.MainWindow;
+
+/**
+ * Creates a modal non-resizable dialog window.
+ * 
+ * Intended to be used for operations that consume some
+ * time, but the user must wait for them to complete before
+ * proceeding.
+ * 
+ * Initially the dialog shows a specified string message or
+ * component and a "loader" bar - dynamic GIF image that
+ * displays "activity" going on. At this stage the window
+ * cannot be closed.
+ * 
+ * When the operation completes, the caller notifies the dialog
+ * that it has finished, provides a new message / component to
+ * display and allows the dialog to be closed.
+ * 
+ * If the operation doesn't complete within the specified time,
+ * a timeout occurs and the dialog windows lets to close itself.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class JWaitDialog extends JDialog
+{
+  private static final int DEFAULT_TIMEOUT = 10000;
+  private static final ImageIcon LOADER_ICON = ResourceManager.getImageIcon(ResourceManager.BAR_LOADER_ORANGE);
+  
+  private JPanel jpInformationPanel;
+  private JLabel jlLoaderIcon;
+  
+  private JButton bOK;
+  private JPanel jpOKButton;
+  
+  private Timer timeoutTimer;
+  private boolean hasTimedOut;
+  
+  
+  /**
+   * Creates a new Wait Dialog with no parent and default timeout on
+   * operation - <code>JWaitDialog.DEFAULT_TIMEOUT</code>.
+   * 
+   * @param dialogTitle Title to set for the dialog window.
+   * @param waitMessage Text to be displayed in the body of this dialog while
+   *                    the user waits.
+   */
+  public JWaitDialog(String dialogTitle, String waitMessage) {
+    this(null, dialogTitle, new JLabel(waitMessage, JLabel.CENTER), DEFAULT_TIMEOUT);
+  }
+  
+  
+  /**
+   * Creates a new Wait Dialog with specified parent and default timeout on
+   * operation - <code>JWaitDialog.DEFAULT_TIMEOUT</code>.
+   * 
+   * @param owner Specified JFrame is set as an owner for this Wait Dialog.
+   * @param dialogTitle Title to set for the dialog window.
+   * @param waitMessage Text to be displayed in the body of this dialog while
+   *                    the user waits.
+   */
+  public JWaitDialog(JFrame owner, String dialogTitle, String waitMessage) {
+    this(owner, dialogTitle, new JLabel(waitMessage, JLabel.CENTER), DEFAULT_TIMEOUT);
+  }
+  
+  
+  /**
+   * Creates a new Wait Dialog with specified parent and timeout on
+   * operation.
+   * 
+   * @param owner Specified JFrame is set as an owner for this Wait Dialog.
+   * @param dialogTitle Title to set for the dialog window.
+   * @param waitMessage Text to be displayed in the body of this dialog while
+   *                    the user waits.
+   * @param timeoutMillis Duration of the timeout on the operation - after this
+   *                      time has passed the window will notify of the timeout
+   *                      and allow to close itself. Value of 0 indicates that the timeout will never occur.
+   */
+  public JWaitDialog(JFrame owner, String dialogTitle, String waitMessage, int timeoutMillis) {
+    this(owner, dialogTitle, new JLabel(waitMessage, JLabel.CENTER), timeoutMillis);
+  }
+  
+  
+  /**
+   * Creates a new Wait Dialog with parent JFrame.
+   * 
+   * @param owner Specified JFrame is set as an owner for this Wait Dialog.
+   * @param dialogTitle Title to set for the dialog window.
+   * @param waitInformationComponent Component to be shown in the body of this
+   *                    dialog windows while the user waits for an operation to complete.
+   * @param timeoutMillis Duration of the timeout on the operation - after this
+   *                      time has passed the window will notify of the timeout
+   *                      and allow to close itself. Value of 0 indicates that the timeout will never occur.
+   */
+  public JWaitDialog(JFrame owner, String dialogTitle, JComponent waitInformationComponent, int timeoutMillis)
+  {
+    super(owner);
+    this.setModal(true);
+    this.setTitle(dialogTitle);
+    
+    // this will show the wait message to the user
+    jpInformationPanel = new JPanel(new GridLayout());
+    jpInformationPanel.add(waitInformationComponent);
+    jpInformationPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 10, 20));
+    
+    // some graphical indication that the loading activity is going on
+    jlLoaderIcon = new JLabel(LOADER_ICON);
+    jlLoaderIcon.setBorder(BorderFactory.createEmptyBorder(0, 20, 20, 20));
+    
+    // put components into the dialog box
+    this.getContentPane().setLayout(new BorderLayout());
+    this.getContentPane().add(jpInformationPanel, BorderLayout.CENTER);
+    this.getContentPane().add(jlLoaderIcon, BorderLayout.SOUTH);
+
+    this.pack();
+    // Set the height of the dialog not to be more than 500; the message is in the scroll pane so that should be OK
+    this.setSize(new Dimension(this.getPreferredSize().width, this.getPreferredSize().height > 500 ? 500 : this.getPreferredSize().height));
+    //    this.setResizable(false);
+    this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
+    
+    // center this window within the main Taverna Workbench window
+    this.setLocationRelativeTo(MainWindow.getMainWindow());
+    
+    
+    // start the timer - on timeout it will produce the
+    // timeout message and allow to close the window
+    hasTimedOut = false;
+    if (timeoutMillis > 0)
+    {
+      timeoutTimer = new Timer();
+      timeoutTimer.schedule(
+          new TimerTask() {
+            public void run() {
+              waitFinished(new JLabel("<html><center>The operation did not complete within the " +
+              		                    "allocated time.</center></html>",
+              		                    UIManager.getIcon("OptionPane.warningIcon"), JLabel.CENTER));
+              hasTimedOut = true;
+            }
+          },
+          timeoutMillis);
+    }
+  }
+  
+  
+  public void waitFinished(String resultMessage) {
+    waitFinished(new JLabel(resultMessage, JLabel.CENTER));
+  }
+  
+  public void waitFinished(JComponent resultInformationComponent)
+  {
+    // this prevents the real response to be set after the
+    // timeout message was already displayed
+    if (!hasTimedOut)
+    {
+      // first of all stop the timeout timer: if this
+      // method was called by the application explicitly, not on
+      // timeout, we don't want the timeout message to appear after that
+      if (timeoutTimer != null) { timeoutTimer.cancel(); }
+      
+      // change the information component
+      jpInformationPanel.removeAll();
+      jpInformationPanel.add(resultInformationComponent);
+      
+      // the OK button will allow closing the window
+      bOK = new JButton("OK");
+      //bOK.setPreferredSize(new Dimension(LOADER_ICON.getIconWidth(), (int) (1.5 * LOADER_ICON.getIconHeight())));
+      bOK.addActionListener(new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          // just remove the window
+          dispose();
+        }
+      });
+      bOK.addKeyListener(new KeyAdapter() {
+        public void keyPressed(KeyEvent e) {
+          if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+            // a fallback mechanism - default button doesn't work for some reason
+            // when the button is added into the dialog not in the constructor
+            bOK.doClick();
+          }
+        }
+      });
+      bOK.setDefaultCapable(true);
+      this.getRootPane().setDefaultButton(bOK);
+      
+      // wrap OK button into a panel to add empty borders
+      jpOKButton = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
+      jpOKButton.add(bOK);
+      jpOKButton.setBorder(BorderFactory.createEmptyBorder(0, 20, 20 - (bOK.getPreferredSize().height - LOADER_ICON.getIconHeight()), 20));
+      
+      
+      // add OK button instead of the loader icon 
+      this.getContentPane().remove(jlLoaderIcon);
+      this.getContentPane().add(jpOKButton, BorderLayout.SOUTH);
+      this.bOK.requestFocusInWindow();
+      
+      // re-enable (X) button in the title bar
+      this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+      
+      // update the size of this window - as the inner sizes of components have
+      // been likely to change; then center the dialog box within its parent
+      this.pack();
+      // Set the height of the dialog not to be more than 500; the message is in the scroll pane so that should be OK
+      this.setSize(new Dimension(this.getPreferredSize().width, this.getPreferredSize().height > 500 ? 500 : this.getPreferredSize().height));
+      this.setLocationRelativeTo(MainWindow.getMainWindow());
+    }
+  }
+  
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/SearchOptionsPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/SearchOptionsPanel.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/SearchOptionsPanel.java
new file mode 100644
index 0000000..b0d9d26
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/SearchOptionsPanel.java
@@ -0,0 +1,167 @@
+package net.sf.taverna.biocatalogue.ui;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.util.Arrays;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+
+import net.sf.taverna.biocatalogue.model.BioCataloguePluginConstants;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.model.search.SearchOptions;
+import net.sf.taverna.biocatalogue.ui.search_results.SearchResultsMainPanel;
+import net.sf.taverna.t2.lang.ui.DeselectingButton;
+
+
+/**
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class SearchOptionsPanel extends JPanel implements HasDefaultFocusCapability
+{
+  // COMPONENTS
+  private SearchOptionsPanel thisPanel;
+  
+private JTextField tfSearchQuery;
+  private JButton bSearch;
+  
+  private final SearchResultsMainPanel tabbedSearchResultsPanel;
+  
+  
+  public SearchOptionsPanel(SearchResultsMainPanel tabbedSearchResultsPanel)
+  {
+    super();
+    this.thisPanel = this;
+    this.tabbedSearchResultsPanel = tabbedSearchResultsPanel;
+    
+    this.initialiseUI();
+  }
+  
+  
+  private void initialiseUI()
+  {
+    this.setLayout(new GridBagLayout());
+    GridBagConstraints c = new GridBagConstraints();
+    
+    c.gridx = 0;
+    c.gridy = 0;
+    c.weightx = 0.0;
+    c.fill = GridBagConstraints.NONE;
+    
+    
+    this.tfSearchQuery = new JTextField(30);
+    this.tfSearchQuery.setToolTipText(
+        "<html>&nbsp;Tips for creating search queries:<br>" +
+        "&nbsp;1) Use wildcards to make more flexible queries. Asterisk (<b>*</b>) matches any zero or more<br>" +
+        "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;characters (e.g. <b><i>Seq*</i></b> would match <b><i>Sequence</i></b>), question mark (<b>?</b>) matches any single<br>" +
+        "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;character (e.g. <b><i>Bla?t</i></b> would match <b><i>Blast</i></b>).<br>" +
+        "&nbsp;2) Enclose the <b><i>\"search query\"</i></b> in double quotes to make exact phrase matching, otherwise<br>" +
+        "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;items that contain any (or all) words in the <b><i>search query</i></b> will be found.</html>");
+    
+    this.tfSearchQuery.addFocusListener(new FocusListener() {
+      public void focusGained(FocusEvent e) {
+        tfSearchQuery.selectAll();
+      }
+      public void focusLost(FocusEvent e) { /* do nothing */ }
+    });
+    this.tfSearchQuery.addKeyListener(new KeyAdapter() {
+      public void keyPressed(KeyEvent e) {
+        // ENTER pressed - start search by simulating "search" button click
+        // (only do this if the "search" button was active at that moment)
+        if (e.getKeyCode() == KeyEvent.VK_ENTER && bSearch.isEnabled()) {    
+          bSearch.doClick();
+        }
+      }
+    });
+    JButton jbClearSearch = new DeselectingButton(new AbstractAction("Clear") {
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			tfSearchQuery.setText("");
+			clearSearch();
+		}}, "");
+    jbClearSearch.setIcon(ResourceManager.getImageIcon(ResourceManager.CLEAR_ICON));
+    
+    this.add(jbClearSearch, c);
+    
+    c.gridx++;
+    c.fill = GridBagConstraints.HORIZONTAL;
+    c.weightx = 0.1;
+    this.add(tfSearchQuery, c);
+    
+    
+    // --- Search button ---
+    
+    c.gridx++;
+    c.weightx = 0;
+    c.fill = GridBagConstraints.NONE;
+    c.anchor = GridBagConstraints.EAST;
+    this.bSearch = new DeselectingButton("Search",
+    		new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          if (getSearchQuery().length() == 0) {
+            clearSearch();
+          }
+          else {
+            // search query available - collect data about the current search and execute it
+            tabbedSearchResultsPanel.startNewSearch(thisPanel.getState());
+          }
+        }
+      },
+      tfSearchQuery.getToolTipText());
+    this.bSearch.setIcon(ResourceManager.getImageIcon(ResourceManager.SEARCH_ICON));
+    this.add(bSearch, c);
+    
+}
+   
+  private void clearSearch() {
+	  tabbedSearchResultsPanel.clearSearch();
+      thisPanel.focusDefaultComponent();
+  }
+  
+  /**
+   * Saves the current state of the search options into a single {@link SearchOptions} object.
+   */
+  public SearchOptions getState() {
+    return (new SearchOptions(getSearchQuery(), Arrays.asList(TYPE.values())));
+  }
+  
+  
+  // *** GETTERS AND SETTERS ***
+  
+  public String getSearchQuery() {
+    return (this.tfSearchQuery.getText().trim());
+  }
+  public void setSearchQuery(String strSearchQuery) {
+    this.tfSearchQuery.setText(strSearchQuery);
+  }
+   
+  
+  // *** Callbacks for HasDefaultFocusCapability interface ***
+  
+  public void focusDefaultComponent() {
+    this.tfSearchQuery.selectAll();
+    this.tfSearchQuery.requestFocusInWindow();
+  }
+  
+  public Component getDefaultComponent() {
+    return(this.tfSearchQuery);
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/FilterTreeNode.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/FilterTreeNode.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/FilterTreeNode.java
new file mode 100644
index 0000000..fedd553
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/FilterTreeNode.java
@@ -0,0 +1,91 @@
+package net.sf.taverna.biocatalogue.ui.filtertree;
+
+import net.sf.taverna.biocatalogue.ui.tristatetree.TriStateTreeNode;
+
+/**
+ * This class allows storing two pieces of data relevant to content filtering
+ * within the node of a tree. These values are kept hidden from the user and
+ * are only used when the filtering is about to happen.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class FilterTreeNode extends TriStateTreeNode
+{
+  private String type; 
+  private String urlValue;
+  final private boolean isFilterCategory;
+  
+  
+  /**
+   * This constructor is useful for root nodes, which need not have filter type / value.
+   */
+  public FilterTreeNode(Object userObject) {
+    super(userObject);
+    
+    this.isFilterCategory = true;
+  }
+  
+  
+  /**
+   * @param userObject As in the superclass (DefaultMutableTreeNode) - the object which represents the node in the UI
+   * @param filterType Type of the filter - e.g. 'Service Categories' --> "cat"; 'Service Types' --> "t"
+   * @param filterUrlValue Value that should be added to the URL to perform the filtering operation
+   */
+  public FilterTreeNode(Object userObject, String filterType, String filterUrlValue) {
+    super(userObject);
+    
+    this.setType(filterType);
+    this.setUrlValue(filterUrlValue);
+    this.isFilterCategory = false;
+  }
+  
+  
+  public void setType(String type) {
+    this.type = type;
+  }
+  
+  public String getType() {
+    return type;
+  }
+  
+  public void setUrlValue(String urlValue) {
+    this.urlValue = urlValue;
+  }
+  
+  
+  public String getUrlValue() {
+    return urlValue;
+  }
+  
+  /**
+   * @return True if and only if this node is one of the "root" filter categories (not to be mixed with root of the filter tree).
+   */
+  public boolean isFilterCategory() {
+    return isFilterCategory;
+  }
+  
+  
+  /**
+   * @return <code>true</code> if the current {@link FilterTreeNode} represents a tag with a namespace
+   *         (i.e. an ontological term), whose full tag name looks like:
+   *         <code>< http://example.namespace.com#tag_display_name ></code>
+   */
+  public boolean isTagWithNamespaceNode() {
+    return (this.getType() != null && this.getType().contains("tag") && this.getUrlValue().contains("#") &&
+            this.getUrlValue().startsWith("<") && this.getUrlValue().endsWith(">"));
+  }
+  
+  
+  /**
+   * Static wrapper for {@link FilterTreeNode#isTagWithNamespaceNode()}
+   *  
+   * @param filterType
+   * @param filterUrlValue
+   * @return
+   */
+  public static boolean isTagWithNamespaceNode(String filterType, String filterUrlValue) {
+    return (new FilterTreeNode("test_user_object", filterType, filterUrlValue).isTagWithNamespaceNode());
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/FilterTreePane.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/FilterTreePane.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/FilterTreePane.java
new file mode 100644
index 0000000..75a80ea
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/FilterTreePane.java
@@ -0,0 +1,348 @@
+package net.sf.taverna.biocatalogue.ui.filtertree;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.BorderFactory;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JToolBar;
+import javax.swing.SwingUtilities;
+
+import net.sf.taverna.biocatalogue.model.BioCataloguePluginConstants;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+import net.sf.taverna.biocatalogue.model.search.SearchInstance;
+import net.sf.taverna.biocatalogue.model.search.ServiceFilteringSettings;
+import net.sf.taverna.biocatalogue.ui.tristatetree.JTriStateTree;
+import net.sf.taverna.biocatalogue.ui.tristatetree.TriStateTreeCheckingListener;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.log4j.Logger;
+
+import org.biocatalogue.x2009.xml.rest.Filter;
+import org.biocatalogue.x2009.xml.rest.FilterGroup;
+import org.biocatalogue.x2009.xml.rest.FilterType;
+import org.biocatalogue.x2009.xml.rest.Filters;
+
+/**
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class FilterTreePane extends JPanel implements TriStateTreeCheckingListener
+{
+  private TYPE resourceType;
+  private String filtersURL;
+  private BioCatalogueClient client;
+  private Logger logger;
+  
+  private FilterTreePane thisPanel;
+  
+  private JToolBar tbFilterTreeToolbar;
+  
+  private JPanel jpFilters = null;
+  private JFilterTree filterTree;  // tree component to display filter selections
+  private Filters filtersRoot;     // last filters element which was received from the API
+
+  
+  
+  public FilterTreePane(TYPE resourceType)
+  {
+    this.thisPanel = this;
+    
+    this.resourceType = resourceType;
+    this.filtersURL = resourceType.getAPIResourceCollectionFiltersURL();
+    this.client = BioCatalogueClient.getInstance();
+    this.logger = Logger.getLogger(this.getClass());
+    
+    initialiseUI();
+    loadFiltersAndBuildTheTree();
+  }
+  
+  
+  private void initialiseUI()
+  {
+    jpFilters = new JPanel();
+    jpFilters.setBackground(Color.WHITE);
+    
+    JScrollPane spFilters = new JScrollPane(jpFilters);
+    spFilters.setMinimumSize(new Dimension(235,0));
+    spFilters.setPreferredSize(new Dimension(300,0));
+    spFilters.getVerticalScrollBar().setUnitIncrement(BioCataloguePluginConstants.DEFAULT_SCROLL);
+    
+    
+    tbFilterTreeToolbar = createTreeActionToolbar();
+    resetTreeActionToolbar();
+    
+    this.setLayout(new BorderLayout());
+    this.add(tbFilterTreeToolbar, BorderLayout.NORTH);
+    this.add(spFilters, BorderLayout.CENTER);
+  }
+  
+  
+  /**
+   * @return A toolbar that replicates all actions available in the contextual menu of
+   *         the filtering tree - mainly: saving current filter, reloading filter tree,
+   *         expanding/collapsing and selecting/deselecting everything in the tree.
+   */
+private JToolBar createTreeActionToolbar()
+  {
+     
+    
+    // the actual toolbar - no actions are added to it yet: done in a separate method
+    JToolBar tbTreeActions = new JToolBar(JToolBar.HORIZONTAL);
+    tbTreeActions.setAlignmentX(RIGHT_ALIGNMENT);
+    tbTreeActions.setBorderPainted(true);
+    tbTreeActions.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+    tbTreeActions.setFloatable(false);
+    return (tbTreeActions);
+  }
+  
+  
+  /**
+   * Resets the action toolbar to the original state.
+   */
+  public void resetTreeActionToolbar()
+  {
+    
+    tbFilterTreeToolbar.removeAll();
+    tbFilterTreeToolbar.repaint();
+  }
+  
+  
+  /**
+   * This method loads filter data from API and populates the view.
+   */
+  private void loadFiltersAndBuildTheTree()
+  {
+    SwingUtilities.invokeLater(new Runnable() {
+      public void run()
+      {
+        resetTreeActionToolbar();
+        
+        jpFilters.removeAll();
+        jpFilters.setLayout(new BorderLayout());
+        jpFilters.add(new JLabel(" Loading filters..."), BorderLayout.NORTH);
+        jpFilters.add(new JLabel(ResourceManager.getImageIcon(ResourceManager.BAR_LOADER_ORANGE)), BorderLayout.CENTER);
+        thisPanel.validate();
+        thisPanel.repaint();      // validate and repaint this component to make sure that
+                                  // scroll bar around the filter tree placeholder panel disappears
+      }
+    });
+    
+    new Thread("Load filters") {
+      public void run() {
+        try {
+          // load filter data
+          filtersRoot = client.getBioCatalogueFilters(filtersURL);
+          
+          // Create root of the filter tree component
+          FilterTreeNode root = new FilterTreeNode("root");
+          
+          // populate the tree via its root element
+          for (FilterGroup fgroup : filtersRoot.getGroupList())
+          {
+            // attach filter group directly to the root node
+            FilterTreeNode fgroupNode = new FilterTreeNode("<html><span style=\"color: black; font-weight: bold;\">" + StringEscapeUtils.escapeHtml(fgroup.getName().toString()) + "</span></html>");
+            root.add(fgroupNode);
+            
+            
+            // go through all filter types in this group and add them to the tree
+            for (FilterType ftype : fgroup.getTypeList())
+            {
+              // if there's more than one filter type in the group, add the type node as another level of nesting
+              // (otherwise, attach filters inside the single type directly to the group node)
+              FilterTreeNode filterTypeNode = fgroupNode;
+              if (fgroup.getTypeList().size() > 1) {
+                filterTypeNode = new FilterTreeNode("<html><span style=\"color: black; font-weight: bold;\">" + StringEscapeUtils.escapeHtml(ftype.getName().toString()) + "</span></html>");
+                fgroupNode.add(filterTypeNode);
+              }
+              
+              // For some reason sorting the list of filters before inserting into tree
+              // messes up the tree nodes
+//              Collections.sort(ftype.getFilterList(), new Comparator<Filter>(){
+//				@Override
+//				public int compare(Filter f1, Filter f2) {
+//				    return (f1.getName().compareToIgnoreCase(f2.getName()));
+//				}           	  
+//              });
+              addFilterChildren(filterTypeNode, ftype.getUrlKey().toString(), ftype.getFilterList());
+            }
+          }
+          
+          // Create the tree view with the populated root
+          filterTree = new JFilterTree(root);
+          filterTree.setRootVisible(false);      // don't want the root to be visible; not a standard thing, so not implemented within JTriStateTree
+          filterTree.setLargeModel(true);        // potentially can have many filters!
+          filterTree.addCheckingListener(thisPanel);
+          
+                   
+          // insert the created tree view into the filters panel
+          jpFilters.removeAll();
+          jpFilters.setLayout(new GridLayout(0,1));
+          jpFilters.add(filterTree);
+          jpFilters.validate();
+          
+          
+          // add actions from the contextual menu of the filter tree into the toolbar
+          // that replicates those plus adds additional ones in this panel
+          tbFilterTreeToolbar.removeAll();
+          for (Action a : filterTree.getContextualMenuActions()) {
+            tbFilterTreeToolbar.add(a);
+          }
+          
+          
+          // enable all actions
+          filterTree.enableAllContextualMenuAction(true);
+        }
+        catch (Exception e) {
+          logger.error("Failed to load filter tree from the following URL: " + filtersURL, e);
+        }
+      }
+      
+      
+      /**
+       * Recursive method to populate a node of the filter tree with all
+       * sub-filters.
+       * 
+       * Ontological terms will be underlined.
+       * 
+       * @param root Tree node to add children to.
+       * @param filterList A list of Filters to add to "root" as children.
+       */
+      private void addFilterChildren(FilterTreeNode root, String filterCategory, List<Filter> filterList) {
+        for (Filter f : filterList) {
+        	
+					// Is this an ontological term?
+					String ontology = null;
+					if (FilterTreeNode.isTagWithNamespaceNode(filterCategory, f
+							.getUrlValue())) {
+						String nameAndNamespace = f.getUrlValue().substring(1,
+								f.getUrlValue().length() - 1);
+						String[] namePlusNamespace = nameAndNamespace
+								.split("#");
+						ontology = JFilterTree
+								.getOntologyFromNamespace(namePlusNamespace[0]);
+					}
+
+					FilterTreeNode fNode = new FilterTreeNode("<html><span color=\"black\"" /*(FilterTreeNode.isTagWithNamespaceNode(filterCategory, f.getUrlValue()) ? " style=\"text-decoration: underline;\"" : "") */ + ">" +
+                               StringEscapeUtils.escapeHtml(f.getName()) + " (" + f.getCount() + ")" + "</span>" +
+                               /*(FilterTreeNode.isTagWithNamespaceNode(filterCategory, f.getUrlValue()) ? "<span color=\"gray\">&nbsp;("+f.getCount().intValue()+")</span></html>" : "</html>"),*/
+                               (ontology != null ? "<span color=\"#3090C7\"> &lt;"+ ontology +"&gt;</span></html>" : "</html>"),
+                               filterCategory, f.getUrlValue());
+					addFilterChildren(fNode, filterCategory, f.getFilterList());
+         
+					// Insert the node into the (alphabetically) sorted children nodes
+					List<FilterTreeNode> children = Collections.list(root.children());
+					// Search for the index the new node should be inserted at
+					int index = Collections.binarySearch(children, fNode,
+							new Comparator<FilterTreeNode>() {
+								@Override
+								public int compare(FilterTreeNode o1,
+										FilterTreeNode o2) {
+									String str1 = ((String) o1.getUserObject())
+											.toString();
+									String str2 = ((String) o2.getUserObject())
+											.toString();
+									return (str1.compareToIgnoreCase(str2));
+								}
+							});
+
+					if (index < 0){ // not found - index will be equal to -insertion-point -1
+						index = -index - 1;
+					}// else node with the same name found in the array - insert it at that position
+			        root.insert(fNode, index);
+
+			        //root.add(fNode);
+        		}
+      		} 
+    	}.start();
+  	}
+  
+  
+  /**
+   * @param si Uses this SearchInstance to restore the checking
+   *           state of filtering criteria in the filter tree. 
+   */
+  public void restoreFilteringSettings(SearchInstance si) {
+    this.filterTree.restoreFilterCheckingSettings(si.getFilteringSettings().getFilterTreeRootsOfCheckedPaths());
+  }
+  
+  
+  /**
+   * Clears any selections made in the filter tree -
+   * i.e. both clears checked nodes and removes all tree path selections.
+   */
+  public void clearSelection() {
+    // filter tree may not have been initialised yet, so perform a check
+    if (this.filterTree != null)
+    {
+      // remove, then restore self as a listener - this is to avoid
+      // receiving checking state change event
+      this.filterTree.removeCheckingListener(thisPanel);
+      this.filterTree.selectAllNodes(false);
+      this.filterTree.clearSelection();
+      this.filterTree.addCheckingListener(thisPanel);
+    }
+  }
+  
+  
+  /**
+   * Collapses all expanded nodes in the filter tree.
+   */
+  public void collapseAll() {
+    // filter tree may not have been initialised yet, so perform a check
+    if (this.filterTree != null) {
+      this.filterTree.collapseAll();
+    }
+  }
+  
+  public void applyQueryString(final String queryString) {
+	    this.filtersURL = resourceType.getAPIResourceCollectionFiltersURL() + "?q=" + queryString;
+	    loadFiltersAndBuildTheTree();
+  }
+  
+  /**
+   * Used for making preferred height of the search status label
+   * the same as the height of this toolbar.
+   * 
+   * @return
+   */
+  public Dimension getTreeToolbarPreferredSize() {
+    return this.tbFilterTreeToolbar.getPreferredSize();
+  }
+  
+  
+  // *** Callback for TriStateTreeCheckingListener ***
+  
+  /**
+   * We start a new search as soon as checking state of the filter tree changes.
+   */
+  public void triStateTreeCheckingChanged(JTriStateTree source)
+  {
+    MainComponentFactory.getSharedInstance().getBioCatalogueExplorationTab().getTabbedSearchResultsPanel().
+        startNewFiltering(resourceType, new ServiceFilteringSettings(filterTree));
+  }
+
+
+public void reset() {
+    this.filtersURL = resourceType.getAPIResourceCollectionFiltersURL();
+	loadFiltersAndBuildTheTree();
+}
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/JFilterTree.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/JFilterTree.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/JFilterTree.java
new file mode 100644
index 0000000..a6cc111
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/filtertree/JFilterTree.java
@@ -0,0 +1,69 @@
+package net.sf.taverna.biocatalogue.ui.filtertree;
+
+import java.awt.event.MouseEvent;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sf.taverna.biocatalogue.ui.tristatetree.JTriStateTree;
+import net.sf.taverna.biocatalogue.ui.tristatetree.TriStateTreeNode;
+
+/**
+ * This subclass of {@link JTriStateTree} provides custom behaviour
+ * for tooltips: ontological terms will now always get a tooltip that
+ * displays the namespace for the tag, but plain text tags will still
+ * behave as before - the way it is defined in the superclass (so that
+ * the tooltip will only be shown if the tag does not fully fit into
+ * the visible part of the {@link FilterTreePane}.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class JFilterTree extends JTriStateTree
+{
+  
+  private static Map<String, String> nameSpaceToOntologyMap = new HashMap<String, String>(){
+      {
+          put("http://www.mygrid.org.uk/ontology", "mygrid-domain-ontology");
+          put("http://www.mygrid.org.uk/mygrid-moby-service", "mygrid-service-ontology");
+      }
+  };
+ 
+
+  public JFilterTree(TriStateTreeNode root) {
+    super(root);
+  }
+  
+  
+  public String getToolTipText(MouseEvent e)
+  {
+    Object correspondingObject = super.getTreeNodeObject(e);
+    if (correspondingObject != null && correspondingObject instanceof FilterTreeNode) {
+      FilterTreeNode filterNode = (FilterTreeNode) correspondingObject;
+      
+      if (filterNode.isTagWithNamespaceNode())
+      {
+        String nameAndNamespace = filterNode.getUrlValue().substring(1, filterNode.getUrlValue().length() - 1);
+        String[] namePlusNamespace = nameAndNamespace.split("#");
+        
+        return ("<html>" + namePlusNamespace[1] + " (<b>Namespace: </b>" + namePlusNamespace[0] + ")</html>");
+      }
+    }
+    
+    return super.getToolTipText(e);
+  }
+  
+  public static String getOntologyFromNamespace(String namespace){
+	  if (namespace == null){
+		  return null;
+	  }
+	  else{
+		  if (nameSpaceToOntologyMap.containsKey(namespace)){
+			  return nameSpaceToOntologyMap.get(namespace);
+		  }
+		  else{
+			  return null;
+		  }
+	  }
+  }
+  
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-still.gif
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-still.gif b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-still.gif
new file mode 100644
index 0000000..b100470
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-still.gif differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader.gif
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader.gif b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader.gif
new file mode 100644
index 0000000..078b55f
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader.gif differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/biocatalogue-perspective.xml
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/biocatalogue-perspective.xml b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/biocatalogue-perspective.xml
new file mode 100644
index 0000000..8145d87
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/biocatalogue-perspective.xml
@@ -0,0 +1,19 @@
+<basepane>
+	<child>
+		<znode classname="net.sf.taverna.zaria.ZRavenComponent">
+			<component scroll="false">
+				<raven>
+					<group>net.sf.taverna.t2.ui-exts</group>
+					<artifact>perspective-biocatalogue</artifact>
+				</raven>
+				<classname>
+					net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory
+				</classname>
+				<interface>
+					net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
+				</interface>
+			</component>
+		</znode>
+	</child>
+	<namedcomponents />
+</basepane>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/biocatalogue_styles.css
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/biocatalogue_styles.css b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/biocatalogue_styles.css
new file mode 100644
index 0000000..3f7246a
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/biocatalogue_styles.css
@@ -0,0 +1,2673 @@
+@charset "utf-8";
+/**
+ * BioCatalogue: app/public/stylesheets/styles.css
+ *
+ * Copyright (c) 2009, University of Manchester, The European Bioinformatics 
+ * Institute (EMBL-EBI) and the University of Southampton.
+ * See license.txt for details
+ */
+
+/* CSS Document */
+
+/** 
+ * NOTE: this relies on the Yahoo IU reset-fonts CSS to normalise styles for HTML elements and fonts 
+ * ALl font sizes MUST be specified in %ages as per the table here: http://developer.yahoo.com/yui/fonts/
+ */
+
+/*
+** Markup free clearing
+** Details: http://www.positioniseverything.net/easyclearing.html
+*/
+.clear-block:after {
+  content: ".";
+  display: block;
+  height: 0;
+  clear: both;
+  visibility: hidden;
+}
+
+.clear-block {
+  display: inline-block;
+}
+
+/* Hides from IE-mac \*/
+* html .clear-block {
+  height: 1%;
+}
+.clear-block {
+  display: block;
+}
+/* End hide from IE-mac */
+
+
+
+/**
+ * 
+ * This css is a variation of the Garland theme, for Drupal 5.0
+ * 
+ * Credits to:
+ * Stefan Nagtegaal, iStyledThis [dot] nl
+ * Steven Wittens, acko [dot] net`
+ * 
+ */
+
+/**
+ * Generic elements
+ */
+
+html {
+	background-color: #e9f7cc;
+}
+
+body {
+  margin: 0;
+  padding: 0;
+  background-color: #e9f7cc;
+  font-family: arial;
+  color: #333333;
+  min-width: 950px;
+}
+
+p {
+	font-family: arial;
+  margin: 1em 0;
+  padding: 0;
+	line-height: 1.5;
+}
+
+input {
+  color: #333333;
+  vertical-align: middle;
+}
+
+textarea, select {
+  color: #333333;
+}
+
+h1, h2, h3, h4, h5, h6 {
+  margin: 0;
+  padding: 0;
+  font-weight: bold;
+  font-family: arial;
+  line-height: 1.5;
+  color: #333333;
+}
+
+h1 {
+  font-size: 153.9%;
+  color: #000033;
+  margin: 0.5em 0;
+}
+
+h2 {
+  font-size: 138.5%;
+}
+
+h3 {
+  font-size: 123.1%;
+}
+
+h4 {
+	margin: 0.5em 0;
+}
+
+h5 {
+	margin: 0.8em 0 0.5em 0;
+	padding-bottom: 0.3em;
+	border-bottom: 1px solid #CCC;
+}
+
+h6 {
+}
+
+table {
+	margin: 0;
+	border-collapse: separate;
+}
+
+th,td {
+	text-align: left;
+	border: none;
+	vertical-align: top;
+}
+
+ul, quote, code, fieldset {
+  margin: 0.5em 0;
+}
+
+pre {
+	background-color: #EEEEEE;
+	padding: 1em;
+	font-size: 85%;
+}
+
+small {
+	line-height: 1.3;
+	color: #555555;
+}
+
+fieldset {
+	border: 1px solid #cdeb8b;
+	padding: 0.3em 1.3em 0.8em 1.3em;
+}
+
+legend {
+	padding: 0.3em 0.6em;
+  text-align: left;
+	font-weight: bold;
+	margin: 0;
+	margin-bottom: 0.2em;
+}
+
+strong {
+	font-weight: bold;
+}
+
+em {
+	font-style: italic;
+}
+
+a:link, a:visited {
+  color: #333333;
+  text-decoration: underline;
+}
+
+a:hover {
+  color: #ff7400;
+  text-decoration: underline;
+}
+
+a:active, a.active {
+  color: #ff7400;
+  text-decoration: underline;
+}
+
+a img {
+	text-decoration: none;
+}
+
+hr {
+  margin: 0.5em 0;
+	*margin: 0;
+  padding: 0;
+  border: none;
+  height: 1px;
+  background: #5294c1;
+}
+
+ul {
+  margin: 0;
+  padding: 0;
+}
+
+ul li {
+  margin: 0;
+  padding: 0;
+	line-height: 1.4;
+}
+
+img, a img {
+  border: none;
+}
+
+.center {
+	text-align: center;
+}
+
+.nomargin {
+	margin: 0;
+}
+
+.medium_linespaced {
+	line-height: 1.5;
+}
+
+.high_linespaced {
+	line-height: 2.0;
+}
+
+.inline-block {
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display: inline-block;
+}
+
+.faded {
+	color: #999;
+}
+
+.faded_plus {
+	color: #AAA;
+}
+
+.faded_plus_plus {
+	color: #BBB;
+}
+
+.green_box {
+	border: 1px dotted #999999;
+	background-color: #e9f7cc;
+	padding: 2px;
+}
+
+.green_highlight {
+	background-color: #e9f7cc;
+	padding: 3px 5px 3px 5px;
+}
+
+.orange_highlight {
+ 	background-color: #ffbb7f;
+	padding: 4px 6px 4px 6px;
+}
+
+.yellow_highlight {
+    background-color: #FFC;
+    padding: 3px 5px 3px 5px;
+}
+
+.standout_link {
+	color: #333333;
+	border: 1px solid #EED799; 
+	background: #FFC; 
+	padding: 0.3em 0.5em; 
+	line-height: 1.3;
+}
+
+.ago {
+	margin-left: 0.2em; 
+	color: #666;
+}
+
+.container_middled * {
+	vertical-align: middle;
+}
+
+a:link.login, a:visited.login {
+	padding-left: 2px;
+	padding-right: 20px;
+	background: url(../images/login.gif) right no-repeat;
+	white-space: nowrap;
+}
+
+a:link.signup, a:visited.signup {
+	padding-left: 2px;
+	padding-right: 20px;
+	background: url(../images/pencil.gif) right no-repeat;
+	white-space: nowrap;
+}
+
+a:link.green_arrow, a:visited.green_arrow {
+	padding-left: 2px;
+	padding-right: 18px;
+	background: url(../images/green_arrow.gif) right no-repeat;
+	white-space: nowrap;
+}
+
+a:link.red_arrow, a:visited.red_arrow {
+	padding-left: 2px;
+	padding-right: 18px;
+	background: url(../images/red_arrow.gif) right no-repeat;
+	white-space: nowrap;
+}
+
+a:link.red_arrow_left, a:visited.red_arrow_left {
+	padding-right: 2px;
+	padding-left: 18px;
+	background: url(../images/red_arrow_left.gif) left no-repeat;
+	white-space: nowrap;
+}
+
+a:link.write_email, a:visited.write_email {
+	padding-left: 2px;
+	padding-right: 22px;
+	background: url(../images/write_email.gif) right no-repeat;
+	white-space: nowrap;
+}
+
+a:link.edit_profile, a:visited.edit_profile {
+	padding-right: 2px;
+	padding-left: 22px;
+	background: url(../images/user_edit.gif) left no-repeat;
+	white-space: nowrap;
+}
+
+a:link.change_pwd, a:visited.change_pwd {
+	padding-right: 2px;
+	padding-left: 22px;
+	background: url(../images/key.gif) left no-repeat;
+	white-space: nowrap;
+}
+
+a:hover.login {
+	background: url(../images/login_hover.gif) right no-repeat;
+	text-decoration: underline;
+}
+
+a:hover.signup {
+	background: url(../images/pencil_hover.gif) right no-repeat;
+	text-decoration: underline;
+}
+
+a:hover.green_arrow {
+	background: url(../images/green_arrow_hover.gif) right no-repeat;
+	text-decoration: underline;
+}
+
+a:hover.red_arrow {
+	background: url(../images/red_arrow_hover.gif) right no-repeat;
+	text-decoration: underline;
+}
+
+a:hover.red_arrow_left {
+	background: url(../images/red_arrow_left_hover.gif) left no-repeat;
+	text-decoration: underline;
+}
+
+a:hover.write_email {
+	background: url(../images/write_email_hover.gif) right no-repeat;
+	text-decoration: underline;
+}
+
+a:hover.edit_profile {
+	background: url(../images/user_edit_hover.gif) left no-repeat;
+	text-decoration: underline;
+}
+
+a:hover.change_pwd {
+	background: url(../images/key_hover.gif) left no-repeat;
+	text-decoration: underline;
+}
+
+#error_flash .rcontainer,
+#notice_flash .rcontainer {	
+	padding: 0;
+	margin: auto;
+	margin-top: 0;
+	margin-bottom: 1em;
+	text-align: center;
+	max-width: 800px;
+	line-height: 1.3;
+}
+
+#error_flash .rcontain,
+#notice_flash .rcontain {
+	padding: 0.1em 0.5em;
+}
+
+#last_search_notice {
+	text-align: right;
+	line-height: 1.0;
+	padding: 0;
+	margin: 0 1em;
+}
+
+#last_search_notice * {
+	vertical-align: middle;
+}
+
+#last_search_notice_text {
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display: inline-block;
+	color: #333333;
+	border: 1px solid #99CCFF;
+	background-color: #EEF6FF;
+	padding: 0.2em 0.4em;
+}
+
+.flash_header {
+  font-weight: bold;
+	font-size: 108%;
+	margin-top: 5px;
+	margin-bottom: 10px;
+}
+
+.flash_body {
+	margin-top: 5px;
+	margin-bottom: 10px;
+}
+
+/**
+ * Layout
+ */
+#header-region {
+  height: 0.75em;
+}
+
+#wrapper {
+  background: #e9f7cc url(../images/central_part_small.gif) repeat-x top center;
+}
+
+#wrapper #container {
+  margin: 0 auto;
+  padding: 0 20px;
+  max-width: 1270px;
+}
+
+#wrapper #container #header {
+    padding-top: 1.1em;
+    height: 5em;
+}
+
+#wrapper #container #header #logo-floater {
+  position: absolute;
+  /*padding-top: 8px;*/
+}
+
+/* With 2 columns, require a minimum width of 800px. */
+body.sidebar-right {
+  min-width: 850px;
+}
+
+/* We must define 100% width to avoid the body being too narrow for near-empty pages */
+#wrapper #container #center {
+  float: left;
+  width: 100%;
+}
+
+/* So we move the #center container over the sidebars to compensate */
+body.sidebar-right #center {
+  margin-right: -210px;
+}
+
+/* And add blanks right for the sidebars to fill */
+body.sidebar-right #squeeze {
+  margin-right: 210px;
+}
+
+/* We ensure the sidebars are still clickable using z-index */
+#wrapper #container .sidebar {
+  margin: 60px 0 5em;
+  width: 210px;
+  float: left;
+  z-index: 2;
+  position: relative;
+}
+
+/* Now we add the backgrounds for the main content shading */
+#wrapper #container #center #squeeze {
+  background: #fff url(../images/central_bar_big.png) repeat-x 50% 0;
+  position: relative;
+  /*min-width: 600px;*/
+  min-width: 900px;
+}
+
+#wrapper #container #center .right-corner {
+  background: transparent url(../images/right_corner.gif) no-repeat 100% 0;
+  position: relative;
+  /*left: 10px;*/
+  left: 5px;
+  /*border: 1px #ff0000 solid;*/
+  /*min-width: 600px;*/
+  min-width: 900px;
+}
+
+#wrapper #container #center .right-corner .left-corner {
+  padding: 50px 25px 3em 25px;
+  background: transparent url(../images/left_corner.gif) no-repeat 0 0;
+  margin-left: -10px;
+  position: relative;
+  /*left: 1px;*/
+  min-height: 400px;
+}
+
+#footer {
+  float: none;
+  clear: both;
+  text-align: center;
+  padding-top: 1em;
+  padding-bottom: 1em;
+  /*margin: 4em 0 -3em;*/
+  /*color: #898989;*/
+}
+
+body.sidebar-right #footer {
+  width: 75%;
+}
+
+#footer_links {
+  float:left;
+  position:relative;
+  left:50%;
+  text-align:left;
+}
+
+#footer_links ul {
+  list-style:none;
+  position:relative;
+  left:-50%;
+}
+
+#footer_links ul li {
+  list-style-type: none;
+  list-style-image: none;
+  margin: 0;
+  padding: 0;
+  float: left;
+  position:relative;
+}
+
+#footer_links ul li.separator {
+	border-left: 1px solid #333333;
+}
+
+#footer_links ul li a, #footer_links ul li a:link, #footer_links ul li a:visited {
+  display: block;
+  margin: 0 1em;
+  color: #333333;
+}
+
+#footer_links ul li a:hover, #footer_links ul li a.active {
+  color: #ff7400;
+}
+
+#footer .logos {
+	margin: 1.5em 0;
+	-moz-border-radius: 15px;
+	-webkit-border-radius: 15px;
+	border: 1px solid #DDD;
+	width: 950px;
+	color: #333;
+	background-color: #FFF;
+}
+
+#footer .logos * {
+	vertical-align: middle;
+}
+
+#footer .logos p {
+	text-align: center;
+}
+
+#footer .logos a {
+	margin: 0 0.8em;
+}
+
+#footer .copyright {
+	text-align: center;
+	font-weight: bold;
+	font-size: 93%;
+	margin-top: 1.5em;
+}
+
+#wrapper #container #content {
+	margin-top: 2em;
+}
+
+#wrapper #container #action_bar {
+  position: absolute;
+  top: 10px;
+  left: 30px;
+  z-index: 3;
+  font-size: 123.1%;
+	font-weight: bolder;
+	vertical-align: middle;
+}
+
+#wrapper #container #action_bar #action_links {
+	padding-left: 15px;
+}
+
+#wrapper #container #action_bar #action_links  * {
+	vertical-align: middle;
+}
+
+#wrapper #container #action_bar #action_links a {
+	padding-left: 15px;
+	padding-right: 14px;
+	background: transparent url(../images/green_separator.gif) no-repeat left;
+	vertical-align: middle;
+	text-align: center;
+}
+
+#wrapper #container #action_icons {
+	position: absolute;
+	top: 14px;
+	right: 10px;
+	z-index: 3;
+	vertical-align: middle;
+}
+
+#wrapper #container #action_icons * {
+	vertical-align: middle;
+}
+
+#wrapper #container #action_icons a {
+	padding: 0 5px 0 10px;
+	background: transparent url(../images/green_separator.gif) no-repeat left;
+	vertical-align: middle;
+}
+
+#wrapper #container #action_bar img {
+	vertical-align: middle;
+}
+
+body.sidebar-right #footer {
+  margin-right: -210px;
+}
+
+/* BEGIN breadcrumbs */
+
+#breadcrumbs_bar {
+  z-index: 3;
+	font-size: 85%;
+	color: #666666;
+	margin: 3px -10px 0 -5px;
+}
+
+#breadcrumbs_bar table {
+	width: 100%;
+	padding: 0;
+	margin: 0;
+}
+
+#breadcrumbs_bar td {
+	text-align: left;
+	margin: 0;
+	padding: 0;
+	vertical-align: top;
+}
+
+#breadcrumbs_bar td * {
+	vertical-align: middle;
+}
+
+ul.breadcrumb_list {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+	border: none;
+	vertical-align: middle;
+}
+
+ul.breadcrumb_list li {
+	display: inline;
+}
+
+/* END breadcrumbs */
+
+/**
+ * Primary navigation
+ */
+ul.primary-links {
+  margin: 0;
+  padding: 0;
+  float: right;
+  position: relative;
+  z-index: 4;
+  font-size: 108%;
+  font-weight: bold;
+}
+
+ul.primary-links li {
+  margin: 0;
+  padding: 0;
+  float: left;  
+  list-style-type: none;
+  list-style-image: none;
+}
+
+ul.primary-links li.separator {
+	/*background: transparent url(../images/green_separator.gif) no-repeat bottom left;*/
+	border-left: 1px solid #999;
+}
+
+ul.primary-links li a, ul.primary-links li a:link, ul.primary-links li a:visited {
+  display: block;
+  margin: 0 1em;
+  /*padding: .75em 0 0;*/
+  color: #333333;
+  /*background: transparent url(images/bg-navigation-item.png) no-repeat 50% 0;*/
+}
+
+ul.primary-links li a:hover, ul.primary-links li a.active {
+  color: #ff7400;
+  /*background: transparent url(images/bg-navigation-item-hover.png) no-repeat 50% 0;*/
+}
+
+/**
+ * Secondary navigation
+ */
+ul.secondary-links {
+  margin: 0;
+  padding: 18px 0 0;
+  float: right;
+  clear: right;
+  position: relative;
+  z-index: 4;
+}
+
+ul.secondary-links li {
+  margin: 0;
+  padding: 0;
+  float: left;
+  background-image: none;
+}
+
+ul.secondary-links li a, ul.secondary-links li a:link, ul.secondary-links li a:visited {
+  display: block;
+  margin: 0 1em;
+  padding: .75em 0 0;
+  color: #cde3f1;
+  background: transparent;
+}
+
+ul.secondary-links li a:hover, ul.secondary-links li a.active {
+  color: #cde3f1;
+  background: transparent;
+}
+
+/**
+ * Local tasks
+ */
+ul.primary, ul.primary li, ul.secondary, ul.secondary li {
+  border: 0;
+  background: none;
+  margin: 0;
+  padding: 0;
+}
+
+#tabs-wrapper {
+  margin: 0 -26px 1em;
+  padding: 0 26px;
+  border-bottom: 1px solid #e9eff3;
+  position: relative;
+}
+ul.primary {
+  padding: 0.5em 0 10px;
+  float: left;
+}
+ul.secondary {
+  clear: both;
+  text-align: left;
+  border-bottom: 1px solid #e9eff3;
+  margin: -0.2em -26px 1em;
+  padding: 0 26px 0.6em;
+}
+h2.with-tabs {
+  float: left;
+  margin: 0 2em 0 0;
+  padding: 0;
+}
+
+ul.primary li a, ul.primary li.active a, ul.primary li a:hover, ul.primary li a:visited,
+ul.secondary li a, ul.secondary li.active a, ul.secondary li a:hover, ul.secondary li a:visited {
+  border: 0;
+  background: transparent;
+  padding: 4px 1em;
+  margin: 0 0 0 1px;
+  height: auto;
+  text-decoration: none;
+  position: relative;
+  top: -1px;
+}
+ul.primary li.active a, ul.primary li.active a:link, ul.primary li.active a:visited, ul.primary li a:hover,
+ul.secondary li.active a, ul.secondary li.active a:link, ul.secondary li.active a:visited, ul.secondary li a:hover {
+  background: url(images/bg-tab.png) repeat-x 0 50%;
+  color: #fff;
+}
+ul.primary li.active a,
+ul.secondary li.active a {
+  font-weight: bold;
+}
+
+/**
+ * CSS support
+ */
+span.clear {
+  display: block;
+  clear: both;
+  height: 1px;
+  line-height: 0px;
+  font-size: 0px;
+  margin-bottom: -1px;
+}
+
+/* BEGIN Login box styles */
+#wrapper #container #header #login_links {
+	clear: both;
+	float: right;
+	padding: 2.1em 0 0 0;
+	margin-bottom: 0;
+	position: relative;
+	z-index: 4;
+	color: #333;
+	font-size: 93%;
+}
+
+#wrapper #container #header #login_links .sep {
+	color: #999;
+	margin: 0 0.4em;
+}
+
+body.sidebar-right #wrapper #container #header #login_links {
+	margin-right: 210px;
+}
+
+#login_box {
+	position: absolute;
+	top: 3px;
+	right: 3px;
+	background-color: #e9f7cc;
+	border: 2px solid #CCC;
+	width: 320px;
+	height: auto;
+	margin: 0 0 auto auto;
+	z-index: 4;
+	padding: 7px;
+}
+
+body.sidebar-right #login_box {
+	margin-right: 210px;
+	top: 3px;
+	right: -15.6em;
+}
+
+#login_box form {
+	margin: 10px 0 1.5em 0;
+}
+
+#login_box span label {
+	float: left;
+	font-weight: bold;
+	padding-top: 0.2em;
+}
+
+#login_box span input.small_login {
+	clear: both;
+	float: right;
+	height: 1.4em;
+	line-height: 1.4;
+	width: 200px;
+	/*background-color: #feffcf;*/
+	margin-bottom: 5px;
+}
+
+#login_box input.small_submit_button {
+	clear: both;
+	float: right;
+	position: relative;
+	top: 5px;
+	width: 80px;
+}
+
+#login_box .rpx {
+	margin: 0;
+	padding: 0.8em 0;
+	border-top: 1px dotted #CCC;
+	text-align: center;
+	line-height: 1.8;
+}
+
+#login_box_other_links {
+	clear: both;
+	font-size: 93%;
+	width: 100%;
+	padding-top: 1.2em;
+	border-top: 1px dotted #CCC;
+	text-align: right;
+}
+
+#login_box_other_links li {
+	margin-bottom: 0.7em;
+}
+
+/* END Login box styles */
+
+/* BEGIN sidebar box styles */
+.sidebar_box {
+	border: solid 1px #cccccc;
+	width: 200px;
+	height: 270px;
+	margin-left: 1em;
+}
+
+.sidebar_box .box_title {
+	text-align: center;	
+	font-weight: bold;
+	font-size: 123.1%;
+	height: 30px;
+	line-height: 28px;
+	background: url(../images/box_title_bkgd_green_14.gif) repeat-x;
+	/*color: #ff7400;*/
+	/*color: #ffffff;*/
+}
+
+.sidebar_box .box_body {
+	background-color: #fdfdfd;
+	height: 240px;
+	/*background: url(../images/green_body_bkgd2.gif) repeat-x;*/
+}
+
+.sidebar_box .box_body .box_text {
+	padding: 1em 1em 1em 1em;	
+}
+
+.sidebar_box .box_body .separator {
+	border-bottom: 1px solid #cccccc;
+	height: 1px;
+	margin: 0 1em 0 1em;
+}
+
+.sidebar_box .box_body .box_subtitle {
+	text-align: center;	
+	font-weight: bold;
+	font-size: 108%;
+	position: relative;
+}
+
+/* END sidebar box styles */
+
+/* BEGIN css tooltips/boxovers */
+
+.boxoverTooltipHeader {
+	z-index: 100000;
+	display: none;
+	border: 0;
+	height: 0;
+}
+
+.boxoverTooltipBody {
+	z-index: 100000;
+	border: solid 1px Gray;
+	color: #000000;
+	background-color: #FFFF66;
+	font-size: 93%;
+	font-weight: normal;
+	padding: 0.3em 0.6em;
+	max-width: 500px;
+	text-align: left;
+	line-height: 1.4;
+}
+
+.boxoverInfoHeader {
+	z-index: 100000;
+	display: none;
+	border: 0;
+	height: 0;
+}
+
+.boxoverInfoBody {
+	z-index: 100000;
+	border: solid 1px Gray;
+	color: #333333;
+	padding: 0.3em 0.6em;
+	background-color: #ebf3ff;
+	font-size: 93%;
+	text-align: left;
+	max-width: 500px;
+	line-height: 1.4;
+}
+
+/* END css tooltips/boxovers */
+
+/* BEGIN liquid round corners box styles */
+.liquid-round {
+	/*width: 70%;*/
+	margin: 25px auto;
+	background: url(../images/round_box_2_left.gif) repeat-y left top;
+}
+
+.liquid-round .top {
+	width: 100%;
+	height: 12px;
+	background: url(../images/round_box_2_top.gif) no-repeat left top;
+}
+
+.liquid-round .top span {
+	display: block;
+	position: relative;
+	height: 12px;
+	background: url(../images/round_box_2_top_right.gif) no-repeat right top;
+}
+
+.liquid-round .center-content {
+	position: relative;
+	background: #f8f8f8 url(../images/round_box_2_right.gif) repeat-y right top;
+	padding: 1px 15px 1px 15px;
+	margin-left: 3px;
+}
+
+.liquid-round .center-content img.round_box_title {
+	margin-top: -38px;
+}
+
+.liquid-round .center-content p {
+	margin-top: 0px;
+	text-align: justify;
+	*line-height: 1.5;	
+}
+
+.liquid-round .bottom {
+	width: 100%;
+	height: 13px;
+	background: url(../images/round_box_2_bottom.gif) no-repeat left bottom;
+}
+
+.liquid-round .bottom span {
+	display: block;
+	position: relative;
+	height: 13px;
+	background: url(../images/round_box_2_bottom_right.gif) no-repeat right bottom;
+}
+/* END liquid round corners box styles */
+
+.step_text {
+	font-weight: bold;
+	margin-top: 2em;
+	color: #660000;
+	/* border-top: 1px dotted #CCC;
+	padding-top: 0.5em; */
+}
+
+.none_text {
+	font-style: italic;
+	color: #666;
+}
+
+.error_text {
+ 	color: red;
+	font-weight: bold;
+}
+
+.additional_info_text {
+	font-size: 93%;
+	color: #666666;
+	font-weight: normal;
+}
+
+.required {
+	color: red;
+	font-weight: bold;
+}
+
+.framed {
+	vertical-align: middle;
+	padding: 3px;
+	background-color: #FFF;
+	border: 1px solid #DDD;
+}
+
+.field {
+	border: 1px solid #EEE;
+	background-color: #FFF;
+	padding: 0.2em 0.5em;
+}
+
+.flag {
+	vertical-align: middle;
+}
+
+/* BEGIN pagination styles */
+
+.pagination {
+	background: white;
+	/* self-clearing method: */ 
+ }
+ 
+.pagination a, 
+.pagination span {
+	padding: .2em .5em;
+	display: block;
+	float: left;
+	margin-right: 1px; 
+}
+
+.pagination span.disabled {
+	color: #999;
+	border: 1px solid #DDD; 
+}
+
+.pagination span.current {
+	font-weight: bold;
+	background: #E9F7CC;
+	color: #333333;
+	border: 1px solid #CDEB8B; 
+}
+
+.pagination a {
+	text-decoration: none;
+	color: #333333;
+	border: 1px solid #CDEB8B; 
+}
+
+.pagination a:hover, 
+.pagination a:focus {
+	background: #E9F7CC;
+}
+
+.pagination .page_info {
+	background: #E9F7CC;
+	color: #333333;
+	padding: .4em .6em;
+	width: 25em;
+	margin-bottom: 0.5em;
+	text-align: center; 
+}
+
+.pagination .page_info b {
+	color: #003;
+	padding: .1em .25em; 
+}
+
+.pagination:after {
+	content: ".";
+	display: block;
+	height: 0;
+	clear: both;
+	visibility: hidden; 
+}
+
+* html .pagination {
+	height: 1%; 
+}
+
+*:first-child+html .pagination {
+	overflow: hidden; 
+}
+
+/* END pagination styles
+
+/* BEGIN listing styles */
+
+.listings {
+	padding: 0;
+	margin: 0;
+}
+
+.listing_item { 
+	margin-bottom: 1.5em;
+	padding: 0.6em 1em;
+	border-left: 1px solid #CCC;
+	border-top: 1px solid #CCC;
+	border-right: 2px solid #BBB;
+	border-bottom: 2px solid #BBB;
+	background-color: #F5F5F5;
+}
+
+.listing_item p {
+	margin: 0;
+}
+
+.listing_item p * {
+	vertical-align: middle;
+}
+
+.listing_item .name_section td {
+	vertical-align: middle;
+}
+
+.listing_item .name {
+	font-size: 138.5%;
+	font-weight: bold;
+	line-height: 1.8;
+}
+
+.listing_item .simple_listing .name {
+	font-size: 123.1%;
+	font-weight: bold;
+	line-height: 1.6;
+}
+
+.listing_item .desc {
+	color: #333333;
+	padding: 0.6em 1em; 
+	margin: 1em 0; 
+	background-color: #F3F3F3; 
+	border: 1px dotted #DDD;
+}
+
+.listing_item .detail {
+	color: #333333;
+	font-size: 85%;
+	margin-top: 0.6em;
+}
+
+.listing_item .detail_simple {
+	color: #333333;
+	font-size: 93%;
+	margin-top: 0.6em;
+}
+
+.listing_item .detail_simple * {
+	vertical-align: baseline;
+}
+
+.listing_item .extra_detail_box {
+	color: #333333;
+	font-size: 85%;
+	margin-top: 1.2em; 
+	padding-top: 0.8em; 
+	border-top: 1px solid #DDD;
+}
+
+/* END listing styles */
+
+.submitter_info {
+	color: #666;
+	vertical-align: baseline;
+}
+
+.submitter_info * {
+	vertical-align: baseline;
+}
+
+a.service_type_badge {
+	font-size: 85%;
+	font-weight: bold;
+	background-color: #FFF;
+	border: 1px solid #DDD;
+	padding: 2px 3px;
+	text-decoration: none;
+	line-height: 1.0;
+}
+
+a.service_type_badge:hover {
+	color: #FFF;
+	background-color: #999;
+}
+
+.service_type_badge_special {
+	font-size: 85%;
+	font-weight: bold;
+	background-color: #FFEFEF;
+	border: 1px solid #DDD;
+	padding: 2px 3px;
+	text-decoration: none;
+	line-height: 1.0;
+}
+
+a.service_location_flag {
+	margin-left: 1em;
+	line-height: 1.0;
+	vertical-align: middle;
+	padding: 0px;
+	line-height: 1.0;
+}
+
+.operation_box {
+	margin: 2em 0;
+	line-height: 1.5;
+	padding-bottom: 1em;
+	border: 0px solid #DDD;
+	border-width: 2px 0 2px 0;
+}
+
+.operation_heading {
+	margin: 0;
+	padding: 1em 0;
+	font-size: 116%;
+}
+
+.operation_name {
+	background-color: #FFBB7F;
+	font-weight: bold;
+	padding: 0.3em 0.5em;
+}
+
+.port {
+	margin-bottom: 1.5em; 
+	padding: 0.7em 0.7em 0.3em 0.7em; 
+	border: 1px solid #DDE;
+	background-color: #F7F7F7;
+}
+
+.monitoring_section {
+	margin-bottom: 1em; 
+	padding: 0.7em 1em;
+	border: 1px solid #DDE;
+	background-color: #F7F7F7;
+}
+
+ul.simple_list {
+	padding: 0;
+	margin: 0;
+}
+
+ul.simple_list li {
+	padding: 0;
+	margin: 0 0 0.5em 2em;
+	list-style: disc; 
+}
+
+.home_heading {
+	font-weight: bold;
+	font-size: 153.9%;
+	line-height: 1.5;
+	text-align: center;
+	padding: 0;
+	margin: 0;
+	margin-top: 1em;
+}
+
+#home_content {
+	width: 950px;
+	/*padding-top: 1em;*/
+	margin: 1em auto auto auto;
+}
+
+#home_content em {
+	color: #ff7400;
+	font-weight: bold;
+}
+
+#key_points {
+	clear:both;
+	margin: 0 0 1em 0;
+}
+
+#key_points li {
+	margin-top: 5px;
+	padding-left: 20px;
+	background: url(../images/bullet_green2.gif) left top no-repeat;
+	/*list-style-type: circle;
+	list-style-image: url("../images/bullet_green2.gif");*/
+	font-size: 108%;
+	line-height: 1.5;
+}
+
+#key_points em {
+	color: #ff7400;
+	font-weight: bold;
+}
+
+div.search_widget {
+	text-align: center;
+}
+
+div.search_widget * {
+	vertical-align: middle;
+}
+
+/* BEGIN Tabber tab styles */
+
+/*--------------------------------------------------
+  REQUIRED to hide the non-active tab content.
+  But do not hide them in the print stylesheet!
+  --------------------------------------------------*/
+.tabberlive .tabbertabhide {
+	display: none;
+}
+
+/*--------------------------------------------------
+  .tabber = before the tabber interface is set up
+  .tabberlive = after the tabber interface is set up
+  --------------------------------------------------*/
+.tabber {
+}
+
+.tabberlive {
+	margin-top: 1em;
+}
+
+/*--------------------------------------------------
+  ul.tabbernav = the tab navigation list
+  li.tabberactive = the active tab
+  --------------------------------------------------*/
+ul.tabbernav
+{
+	margin: 0;
+	border-bottom: 1px solid #CCC;
+	font-weight: bold;
+	font-size: 123.1%;
+	line-height: 24px;
+	padding-bottom: 1px;
+}
+
+ul.tabbernav li
+{
+	list-style: none;
+	margin: 0;
+	display: inline;
+}
+
+ul.tabbernav li a
+{
+	padding: 3px 0.5em;
+	margin-right: 4px;
+	border: 1px solid #CCC;
+	border-bottom: none;
+	background: #E9F7CC;
+	text-decoration: none;
+}
+
+ul.tabbernav li a:link { color: #333; }
+
+ul.tabbernav li a:hover
+{
+	color: #000;
+	background: #BFE76F;
+	border-color: #CCC;
+}
+
+ul.tabbernav li.tabberactive a
+{
+	background-color: #fff;
+	border-bottom: 1px solid #fff;
+}
+
+ul.tabbernav li.tabberactive a:hover
+{
+	color: #000;
+	background: white;
+	border-bottom: 1px solid white;
+}
+
+/*--------------------------------------------------
+  .tabbertab = the tab content
+  Add style only after the tabber interface is set up (.tabberlive)
+  --------------------------------------------------*/
+.tabberlive .tabbertab {
+	padding: 1em;
+	border: 1px solid #CCC;
+	border-top: 0;
+
+ /* If you don't want the tab size changing whenever a tab is changed
+    you can set a fixed height */
+
+ /* height:200px; */
+
+ /* If you set a fix height set overflow to auto and you will get a
+    scrollbar when necessary */
+
+ /* overflow:auto; */
+}
+
+/* If desired, hide the heading since a heading is provided by the tab */
+.tabberlive .tabbertab h3 {
+	display: none;
+}
+.tabberlive .tabbertab h3 {
+ display:none;
+}
+
+/* Example of using an ID to set different styles for the tabs on the page */
+.tabberlive#tab1 {
+}
+.tabberlive#tab2 {
+}
+.tabberlive#tab2 .tabbertab {
+ height:200px;
+ overflow:auto;
+}
+
+/* END Tabber tab styles */
+
+.search_button {
+	border: none;
+	height: 31px;
+	width: 38px;
+	background: url('/images/go_button.png') 0 0 no-repeat;
+	cursor: pointer;
+}
+
+.search_button:hover { }
+
+/* BEGIN tables styles */
+.biocat_table {
+	border: 1px solid #cccccc;
+}
+
+.biocat_table th {
+	font-weight: bold;
+	white-space: nowrap;
+	vertical-align: middle;
+	color: #333333;
+	height: 1.8em;
+	background: url(../images/box_title_bkgd_green_14.gif) repeat-x;
+	background-position: bottom;
+	padding-left: 6px;
+	padding-right: 6px;
+	border-right: 1px solid #cccccc;
+}
+
+.biocat_table th.center {
+	text-align: center;
+}
+
+.biocat_table .small_center {
+	text-align: center;
+	width: 40px;
+}
+
+.biocat_table tr.odd {
+	background-color: #f2fbe1;
+}
+
+.biocat_table td {
+	white-space: nowrap;
+	padding: 3px 6px 3px 6px;
+	border-left: 1px solid #cccccc;
+	border-right: 1px solid #cccccc;
+}
+
+.biocat_table td.center {
+	text-align: center;
+}
+
+.biocat_table td.action_links a:link, .biocat_table td.action_links a:visited {
+	text-align: center;
+	text-decoration: underline;
+	font-size: 93%;
+}
+
+.biocat_table td.action_links a:hover {
+	background-color: #feffcf;
+}
+/* END tables styles */
+
+/* BEGIN ActiveRecord errors box */
+
+/*.fieldWithErrors {
+	padding: 2px;
+	background-color: #ffaaaa;
+	display: table;
+}*/
+
+.fieldWithErrors label {
+	padding: 2px;
+	background-color: #ffaaaa;
+}
+
+.fieldWithErrors input, .fieldWithErrors select {
+	padding: 2px;
+	background-color: #ffaaaa;
+}
+
+#errorExplanation {
+	width: 400px;
+	border: 2px solid #ffaaaa;
+	margin: 1.5em 0;
+	background-color: #f5f5f5;
+	padding-bottom: 1em;
+}
+
+#errorExplanation h2 {
+  text-align: center;
+	font-weight: bold;
+	padding: 0.5em;
+	font-size: 93%;
+	margin: 0;
+	background-image: none;
+	background-color: #ffaaaa;
+	border: none;
+}
+
+#errorExplanation p {
+	font-size: 93%;
+	margin-bottom: 0;
+	margin-top: 0.5em;
+	padding: 0.5em;
+}
+
+#errorExplanation ul {
+	padding-left: 2em;
+}
+
+#errorExplanation ul li {
+	font-size: 93%;
+	list-style: square;
+}
+
+/* END ActiveRecord errors box */
+
+.box_description {
+	padding: 0.9em 1.3em; 
+	background-color: #F7F7F7; 
+	border: 1px solid #DDD;
+	line-height: 1.4;
+}
+
+.box_info {
+	background: #E9F7CC;
+	color: #333333;
+	padding: 0.6em 1em;
+	margin: 0;
+	margin-bottom: 1em;
+	text-align: center; 
+}
+
+.box_info_standout {
+	color: #333333;
+	border: 1px solid #DDD; 
+	background: #FFFFCC; 
+	padding: 0.3em 0.6em; 
+	margin-bottom: 1em;
+	line-height: 1.5;
+}
+
+.box_indented_with_bar {
+	border: 0;
+	border-left: 4px solid #DDD;
+	padding: 1em 0 1em 1em;
+	margin-left: 1em;
+}
+
+.box_currentuser_specific {
+	padding: 1em 1.5em;
+	border: 1px solid #99CCFF;
+	background-color: #EEF6FF;
+}
+
+.box_currentuser_specific * {
+	vertical-align: middle;
+}
+
+.box_form {
+	margin: 1em 0;
+	border: 1px solid #CCC;
+	padding: 0.7em 1.2em;
+	background-color: #EEE;
+}
+
+.box_form fieldset {
+	border: 1px solid #cccccc;
+}
+
+.box_form fieldset legend {
+	font-size: 108%;
+	color: #660000;
+}
+
+.box_form hr {
+	background: #CCC;
+}
+
+/* BEGIN annotations styles */
+
+.annotations_container { }
+
+.annotations_container .inactive {
+	color: #BBB;
+	line-height: 1.0;
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display: inline-block;
+}
+
+.annotations_container .active { 
+	line-height: 1.0;
+	text-decoration: none;
+	color: #333;
+	display: none;
+}
+
+.annotations_container:hover .inactive {
+	display: none;
+}
+
+.annotations_container:hover .active {
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display: inline-block;
+}
+
+.box_annotations {
+	padding: 0;
+	line-height: 1.4;
+}
+
+.box_annotations .rcontain {
+	padding: 2px;
+}
+
+.box_annotations div.text {
+	padding: 0.1em 0.5em 0.2em 0.5em;
+  overflow-x: auto;
+}
+
+.box_annotations div.text p {
+	margin: 0.5em 0;
+	line-height: 1.4;
+}
+
+.box_annotations div.other {
+	padding: 0.1em 0.5em 0.2em 0.5em;
+}
+
+.box_annotations .actions {
+	margin: 0.5em 0 0 0; 
+	font-size: 85%;
+	color: #666;
+	text-align: right;
+}
+
+.annotations_container .add_box {
+	margin-top: 0.5em;
+	text-align: center;
+}
+
+.annotations_container .add_box form {
+	text-align: center;
+}
+
+.annotations_container .add_box form div {
+	width: 100%;
+	text-align: center;
+}
+.annotations_container .add_box form textarea {
+	width: 99%;
+}
+
+.annotations_container .add_box .submit_button_div {
+	margin-top: 0.4em;
+	text-align: right; 
+}
+
+.annotations_container .add_box .submit_button_div input {
+	width: 80px;
+}
+
+.annotations_container .login_link_box {
+	text-align: right;
+	margin-top: 0.7em;
+}
+
+.annotation_source_text {
+	float: right;
+	color: #666;
+	background-color: #FFC;
+	font-size: 85%;
+	padding: 0.1em 0.5em;
+	line-height: 1.3;
+}
+
+.annotation_source_text * {
+	vertical-align: middle;
+}
+
+.annotation_source_text a {
+	font-weight: bold;
+}
+
+.annotation_source_provider {
+	background-color: #FFDFDF;
+}
+
+.annotation_source_member {
+	background-color: #FFEFBF;
+}
+
+.annotation_source_registry {
+	background-color: #DDFFDD;
+}
+
+.aliases {
+	font-size: 100%;
+	margin: 0.3em 0 0.2em 0;
+	margin-left: 1em;
+	line-height: 1.6;
+}
+
+.annotations_counts_outer_box {
+	display: -moz-inline-block;
+	display: -moz-inline-box;
+	display: -moz-inline-stack;
+  display: inline-block;
+	*display: inline;
+	vertical-align: top;
+	white-space: nowrap;
+	line-height: 1.0;
+	zoom: 1;
+}
+
+.annotations_counts_outer_box .rtop,
+.annotations_counts_outer_box .rbottom {
+	*display: none;
+}
+
+.annotations_counts_outer_box .rcontain {
+	padding: 0px 3px;
+	*padding: 6px 3px 4px 3px;
+}
+
+.annotations_counts_box {
+	font-weight: bold;
+	font-size: 93%;
+	color: #333333;
+	vertical-align: middle;
+	line-height: 22px;
+}
+
+.annotations_counts_box span {
+	padding: 4px 3px 4px 5px;
+	vertical-align: middle;
+}
+
+.annotations_counts_box img {
+	vertical-align: middle;
+	margin-left: 2px;
+	*margin-left: 0;
+}
+
+.annotations_counts_box span.total {
+	background-color: #DDD;
+}
+
+.annotations_counts_box img {
+	vertical-align: middle;
+}
+
+.annotations_counts_box span.end {
+}
+
+/* END annotations styles */
+
+/* BEGIN terms of use page styles */
+#terms_of_use ul, #terms_of_use ol {
+	padding-left: 3em;
+}
+
+#terms_of_use ul li  {
+	padding-top: 1em;
+	list-style: square;
+}
+
+#terms_of_use ol li {
+	padding-top: 1em;
+	list-style: decimal;
+}
+/* END terms of use page styles */
+
+/* BEGIN generic horizontal buttons classes */
+/* Should be used with <ul> elements */
+
+.buttons {
+	margin: 2em 0;
+	font-weight: bold;
+	text-align: center;
+}
+
+.buttons li {
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display: inline-block;	
+	margin: 0;
+	margin-left: 8px;
+	line-height: 2.0;
+}
+
+.buttons li.first {
+	margin-left: 0;
+}
+
+.buttons li:hover {
+}
+
+.buttons li * {
+	vertical-align: middle;
+}
+
+.buttons li a {
+	width: 100%;
+	padding: 6px 0.5em;
+	line-height: 2.0;
+	font-weight: bold;
+	text-decoration: none;
+	border: 1px solid #BBBBBB;
+	background-image: url('/images/button_slim_bg.png');
+	background-position: top;
+	background-repeat: repeat-x;
+	background-color: #EEF6FF;
+}
+
+.buttons li a:hover {
+	background-image: none;
+	background-color: #0099FF;
+	color: #F5F5F5;
+}
+
+.buttons li a * {
+	vertical-align: middle;
+}
+
+a.button_slim {
+	font-weight: bold;
+	line-height: 1.6;
+	text-decoration: none;
+	border: 1px solid #BBBBBB;
+	background-image: url('/images/button_slim_bg.png');
+	background-position: top;
+	background-repeat: repeat-x;
+	background-color: #EEF6FF;
+	vertical-align: middle;
+	padding: 0.1em 0.5em;
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display: inline-block;	
+}
+
+a.button_slim:hover {
+	background-image: none;
+	background-color: #0099FF;
+	color: #F5F5F5;
+}
+
+a.button_slim * {
+	vertical-align: middle;
+}
+
+/* END generic horizontal buttons classes */
+
+/* BEGIN tag cloud styles */
+	
+.tag_cloud {
+	text-align: center;
+	line-height: 1.7;
+}
+
+.tag_cloud ul {
+	margin: 0;
+	padding: 0;
+}
+
+.tag_cloud ul li {
+	display: inline; 
+	text-decoration: none;
+	margin: 0 0.25em;
+}
+
+.tag_cloud li a {
+	color: #000099;
+	padding: 0.1em 0;
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display: inline-block;
+}
+
+span.namespace,
+.tag_cloud li a.namespace {
+	color: #999;
+	font-size: 10px;
+}
+
+span.ontology_term,
+.tag_cloud li a.ontology_term {
+	color: #6600CC;
+}
+
+/* END tag cloud styles */
+
+/* BEGIN Section Boxes (usually for inner sidebars) */
+
+.box_section {
+	margin: 0;
+	text-align: center;
+	margin-bottom: 1em;
+}
+
+.box_section .content {
+	padding: 0.5em 0.8em;
+	border-top: none;
+	border-right: 1px solid #DDD;
+	border-bottom: none;
+	border-left: 1px solid #DDD;
+	background-image: url('/images/box-bg1.png');
+	background-position: top;
+	background-repeat: repeat-x;
+	background-color: #FFC;
+	overflow: hidden;
+	line-height: 1.5;
+}
+
+.box_section .xtop, 
+.box_section .xbottom {
+	display: block;
+	background: transparent;
+	font-size: 1px;
+}
+
+.box_section .xb1, 
+.box_section .xb2, 
+.box_section .xb3, 
+.box_section .xb4,
+.box_section .xb5, 
+.box_section .xb6, 
+.box_section .xb7 {
+	display: block;
+	overflow: hidden;
+}
+
+.box_section .xb1, 
+.box_section .xb2, 
+.box_section .xb3, 
+.box_section .xb6, 
+.box_section .xb7 {
+	height: 1px;
+}
+
+.box_section .xb2, 
+.box_section .xb3, 
+.box_section .xb4 {
+	background: #f4f0b0;
+	border-left: 1px solid #DDD;
+	border-right: 1px solid #DDD;
+}
+
+.box_section .xb5, 
+.box_section .xb6, 
+.box_section .xb7 {
+	background: #ffffcb;
+	border-left: 1px solid #DDD;
+	border-right: 1px solid #DDD;
+}
+
+.box_section .xb1 {
+	margin: 0 5px;
+	background: #DDD;
+}
+
+.box_section .xb2, 
+.box_section .xb7 {
+	margin: 0 3px;
+	border-width: 0 2px;
+}
+
+.box_section .xb3, 
+.box_section .xb6 {
+	margin: 0 2px;
+}
+
+.box_section .xb4, 
+.box_section .xb5 {
+	height: 2px;
+	margin: 0 1px;
+}
+
+.box_section .heading {
+	font-size: 108%;
+	font-weight: bold;
+	line-height: 1.0;
+	margin-top: 0.2em;
+}
+
+.box_section .footer {
+	text-align: center;
+	margin-top: 1em; 
+	border-top: 1px solid #DDD; 
+	padding-top: 0.7em;
+}
+
+.box_section ul.items {
+	margin: 0 0 0.8em 1em;
+	text-align: left;
+}
+
+.box_section ul.items li {
+	list-style-type: none;
+	margin: 0.6em 0;
+	padding-left: 0.8em;
+	line-height: 1.1;
+	/*border-left: 3px solid #DDDDBB;*/
+	text-align: center;
+}
+
+.box_section ul.items li * {
+	vertical-align: middle;
+}
+
+/* END Section Boxes (usually for inner sidebars) */
+
+ 
+/* BEGIN ratings table styles */
+
+.ratings_table {
+	border: 1px solid #cccccc;
+	margin-bottom: 0.5em;
+	background-color: #FFC;
+	border-collapse: collapse;
+	font-size: 93%;
+}
+
+.ratings_table th {
+	font-weight: bold;
+	white-space: nowrap;
+	vertical-align: middle;
+	color: #333333;
+	height: 2em;
+	background: url(../images/box_title_bkgd_green_14.gif) repeat-x;
+	background-position: bottom;
+	padding-left: 6px;
+	padding-right: 6px;
+	border-right: 1px solid #cccccc;
+	text-align: center;
+}
+
+.ratings_table td {
+	white-space: nowrap;
+	padding: 4px 6px 4px 6px;
+	border-left: 1px solid #cccccc;
+	border-right: 1px solid #cccccc;
+	vertical-align: middle;
+	text-align: center;
+}
+
+.ratings_table tr:hover {
+	background-color: #F7F7F7;
+}
+
+.ratings_table .your_rating .delete_rating_faded {
+	width: 10px;
+	height: 10px;
+	margin-left: 0.5em;
+	vertical-align: middle;
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display: inline-block;
+	background: url('/images/delete_faded.png') no-repeat center;
+}
+
+.ratings_table .your_rating .delete_rating a {
+	text-decoration: none;	
+}
+
+.ratings_table .your_rating:hover .delete_rating {
+	background: url('/images/delete.png') no-repeat center;
+}
+
+/* END ratings table styles */
+
+/* BEGIN filtering styles */
+
+#filters_header {
+	width: auto;
+	color: #000;
+	line-height: 1.2; 
+	padding: 0.5em 0;
+	text-align: center;
+	margin-bottom: 0.5em;
+}
+
+#filters_header .text {
+	font-size: 108%;
+	font-weight: bold;
+	vertical-align: middle;
+}
+
+div.filters {
+	padding: 0 0.3em;
+	line-height: 1.5; 
+}
+
+div.filters p.type {
+	font-weight: bold;
+	text-align: center;
+	line-height: 1.2; 
+	padding: 0;
+	margin: 0;
+	margin-bottom: 0.8em;
+	color: #555;
+}
+
+div.filters p.subtype {
+	font-size: 93%;
+	font-weight: bold;
+	text-align: center;
+	line-height: 1.2; 
+	padding: 0;
+	margin: 0;
+	margin-top: 0.7em;
+	margin-bottom: 0.6em;
+	color: #666;
+	border-bottom: 1px dotted #DDD;
+	padding-bottom: 0.3em;
+}
+
+div.filters p.type_small {
+	font-size: 85%;
+	line-height: 1.2;
+	font-weight: bold;
+	margin: 0;
+	margin-bottom: 0.7em;
+	text-align: center;
+	color: #555;
+}
+
+div.filters div.filter_type_box {
+	margin-bottom: 1em;
+	padding: 0;
+}
+
+div.filters div.current_filters_box {
+	margin-bottom: 1em;
+	padding: 0;
+}
+
+div.filters div.current_filters_box .rcontain {
+	padding: 2px 3px;
+}
+
+div.filters div.current_filters_box .and {
+	font-weight: bold; 
+	font-size: 100%; 
+	text-align: center; 
+	margin: 0;
+	padding-right: 12px;
+}
+
+div.filters div.selected_filters_type_box {
+	margin: 0 0.1em;
+	padding: 0;
+}
+
+div.filters div.selected_filters_type_box .or {
+	font-weight: bold; 
+	font-size: 77%; 
+	text-align: center; 
+	margin: 2px 0;
+	padding-right: 12px;
+}
+
+div.filters ul.top_level {
+	font-size: 85%;
+}
+
+div.filters li {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+}
+
+div.filters li .container {
+	
+}
+
+div.filters li .container a {
+	display: block;
+	vertical-align: top;
+	margin-bottom: 0.3em;
+	padding: 1px 3px;
+	text-decoration: none;
+	border: 1px dotted transparent;
+	vertical-align: baseline;
+}
+
+div.filters li .container a * {
+	vertical-align: baseline;
+}
+
+div.filters li .container a:hover {
+	border: 1px dotted #CCC;
+	background-color: #DDE;
+}
+
+div.filters li .container a.selected {
+	border: 1px dotted #CCC;
+	background-color: #DDE;
+}
+
+div.filters li .container a:hover.selected { }
+
+div.filters li .container a span.text {
+	text-decoration: underline;
+}
+
+div.filters li .container a span.count { 
+	margin-left: 0.2em;
+}
+
+div.filters li .container a span.deselect {
+	float: right;
+	width: 12px;
+	background: url('/images/delete_faded_darker.png') no-repeat left center;
+}
+
+div.filters li .container a:hover span.deselect {
+	background: url('/images/delete.png') no-repeat left center;
+}
+
+div.filters li.category_root .container a {
+	margin-bottom: 0.5em;	
+}
+
+div.filters li.sub_category .container a {
+	/*border-left: 1px dotted #CCC;*/
+	margin-left: 0.2em;
+}
+
+/* END filtering styles */
+
+/* BEGIN autocomplete styles */
+
+
+div.auto_complete {
+  position: absolute;
+  background-color: white;
+  border: 1px solid #999;
+  margin-top: -2px;
+  padding: 0px;
+	z-index: 10000;
+	font-size: 85%;
+	font-weight: normal;
+}
+
+div.auto_complete * {
+	vertical-align: baseline;
+}
+
+div.auto_complete ul {
+  list-style-type: none;
+  margin: 0px;
+  padding: 0px;
+}
+
+div.auto_complete ul li.selected {
+  background-color: #ffb;
+}
+
+div.auto_complete ul li {
+  text-align: left;
+  list-style-type: none;
+  display: block;
+  margin: 0;
+  padding: 3px;
+  border-bottom: 1px solid #DDD;
+	cursor: pointer;
+	overflow: hidden;
+}
+
+div.auto_complete ul li.selected { 
+  background-color: #ffb; 
+} 
+
+div.auto_complete ul strong.highlight { 
+  color: #800; 
+  margin: 0px; 
+  padding: 0px;
+	font-weight: normal;
+}
+
+/* END autocomplete styles */
+
+/* BEGIN stats page styles */
+
+#stats_page {
+	margin: 0 2em;	
+}
+
+#stats_page p {
+	margin: 0.6em 0;
+}
+
+#stats_page h4 {
+	padding: 0.2em 0.6em;
+	background-color: #EEE;
+	margin-top: 1.5em;
+}
+
+#stats_page .left_column {
+	float: left; 
+	width: 80%;
+}
+
+#stats_page .right_column {
+	float: right; 
+	width: 18%; 
+}
+
+#stats_page .box_indented_with_bar {
+	padding: 0.5em 1.5em;
+}
+
+#stats_page .box_info_standout {
+	width: 550px;
+	font-size: 85%;
+}
+
+#stats_page .services {
+	margin: 0.5em 0 0.5em 1.5em;
+	line-height: 1.5;
+	font-size: 85%;
+}
+
+#stats_page ul.contents {
+	text-align: left;
+}
+
+#stats_page ul.contents li {
+	padding: 0.2em 0;
+}
+
+#stats_page ul.contents li ul {
+	margin-left: 1em;
+}
+
+/* END stats page styles */
+
+/* BEGIN styles for rounded corner boxes */
+
+.rcontain {
+	padding: 0 0.3em;
+}
+
+/* END styles for rounded corner boxes */
+
+p.more_less_links {
+	font-weight: bold;
+	text-align: right;
+	font-size: 85%;
+	margin: 0;
+}
+
+p.more_less_links a {
+	text-decoration: none;
+}
+
+.search_box .rcontain {
+	padding: 0.4em 1em;
+}
+
+.form_field_section {
+	margin-top: 1.5em;
+}
+
+.form_selected_values {
+	padding: 0.3em 0.5em;
+	border: 1px solid #DDD;
+	background-color: #FFF;
+	color: #333;
+}
+
+/* BEGIN categories styles */
+
+.categories_box {
+	font-size: 85%;
+	line-height: 16px;
+}
+
+ul.categories {
+	float: left;
+	overflow: hidden;
+	margin: 0;
+	padding: 0;
+	list-style-type: none;
+}
+
+ul.categories li {
+	display: inline;
+}
+
+ul.categories li span.main {
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display: inline-block;
+	background-color: #FFC;
+	margin-bottom: 0.4em;
+	margin-right: 0.5em;
+	padding: 0 3px;
+	border-top: 1px solid #DEDEDE;
+	border-left: 1px solid #DEDEDE;
+	border-right: 1px solid #999;
+	border-bottom: 1px solid #999;
+}
+
+ul.categories li span.main a.category {
+	line-height: 1.6;
+	text-decoration: none;
+	vertical-align: middle;
+}
+
+ul.categories li span.main a.category:hover {
+	color: #000;
+}
+
+ul.categories li span.main:hover {
+	background-color: #F0E68C;
+}
+
+/* END categories styles */
+
+/* BEGIN stats bar styles */
+
+.stats_bar {
+	color: #555;
+	font-weight: bold;
+}
+
+.stats_bar * {
+	vertical-align: middle;
+}
+
+.stats_bar img {
+	margin-right: 0.3em;
+}
+
+.stats_bar .each {
+	margin-left: 1em;
+}
+
+/* END stats bar styles */
+
+#feedback_help {
+	margin: 1.5em;
+	font-size: 93%; 
+	text-align: left;
+}
+
+#feedback_help ul {
+	margin-left: 1.5em;
+	margin-bottom: 1em;
+}
+
+#search_within {
+	margin-bottom: 1em;
+}
+
+#search_within .rcontain {
+	padding: 0 1.5em;
+	text-align: right;
+}
+
+.service_properties p,
+.service_properties .property {
+	margin: 0;
+	margin-top: 1.5em;
+}
+
+#filter_message {
+	text-align: left; 
+	font-size: 93%;
+	margin-bottom: 0.8em;
+	color: #333333;
+	/**border: 1px solid #DDD; 
+	background: #FFFFCC; 
+	padding: 0.4em 0.6em; */
+	line-height: 1.5;
+}
+
+#filter_message .rcontain {
+	padding: 1px 0.8em;
+}
+
+#filter_message .rtop,
+#filter_message .contain,
+#filter_message .rbottom {
+	margin: 0;
+}
+
+#filter_message .rtop,
+#filter_message .rbottom {
+	height: 4px;
+	line-height: 1.0;
+}
+
+#filter_message * {
+	vertical-align: middle;
+}
+
+#subscribe_results_link {
+	float: right;
+	text-decoration: none; 
+	vertical-align: middle;
+	font-weight: bold;
+	margin-left: 2em;
+}
+
+#subscribe_results_link img {
+	margin-right: 0.4em;
+}
+
+/* Style for default texts in inputs */ 
+.ex {
+	color: #999;	
+}
+
+/* BEGIN login page styles */
+
+.login_form {
+	padding: 1.2em 0.8em;	
+}
+
+.login_form label {
+	float: left;
+	font-weight: bold;
+	margin-bottom: 1em;
+}
+
+.login_form input.login_field {
+	clear: both;
+	float: right;
+	height: 1.4em;
+	line-height: 1.4;
+	width: 220px;
+	margin-bottom: 1em;
+}
+
+.login_form input.sign_in_button {
+	clear: both;
+	float: right;
+	width: 80px;
+}
+
+.login_form .other_links {
+	clear: both;
+	width: 100%;
+	padding-top: 1.2em;
+	border-top: 1px dotted #CCC;
+	text-align: right;
+}
+
+.login_form .other_links li {
+	margin-bottom: 0.7em;
+}
+
+/* END login page styles */
+
+img.account_provider_logo {
+	margin: 0 2px;
+}
+
+.side_link {
+	color: #999; 
+	margin-left: 3em; 
+	font-size: 85%;
+}
+
+.side_link a {
+	color: #999;
+}
+
+.toolbar {
+	float: right;
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/blue-sphere-50.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/blue-sphere-50.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/blue-sphere-50.png
new file mode 100644
index 0000000..0b3626d
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/blue-sphere-50.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/cross-sphere-35.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/cross-sphere-35.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/cross-sphere-35.png
new file mode 100644
index 0000000..2553bd9
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/cross-sphere-35.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/cross-sphere-50.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/cross-sphere-50.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/cross-sphere-50.png
new file mode 100644
index 0000000..2bcba73
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/cross-sphere-50.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/accept.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/accept.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/accept.png
new file mode 100644
index 0000000..89c8129
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/accept.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add - tick.pdn
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add - tick.pdn b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add - tick.pdn
new file mode 100644
index 0000000..04ee9be
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add - tick.pdn differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add - tick.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add - tick.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add - tick.png
new file mode 100644
index 0000000..5d2d4af
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add - tick.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add.png
new file mode 100644
index 0000000..6332fef
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/add.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/application_form_add.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/application_form_add.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/application_form_add.png
new file mode 100644
index 0000000..28c2175
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/application_form_add.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_join (flipped vertically).png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_join (flipped vertically).png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_join (flipped vertically).png
new file mode 100644
index 0000000..7858d14
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_join (flipped vertically).png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_left.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_left.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_left.png
new file mode 100644
index 0000000..2bed329
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_left.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_refresh.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_refresh.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_refresh.png
new file mode 100644
index 0000000..0de2656
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_refresh.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_right.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_right.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_right.png
new file mode 100644
index 0000000..2cf15f1
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/arrow_right.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/chart_organisation.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/chart_organisation.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/chart_organisation.png
new file mode 100644
index 0000000..c32d25c
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/chart_organisation.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/cross.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/cross.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/cross.png
new file mode 100644
index 0000000..1514d51
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/cross.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/disk.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/disk.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/disk.png
new file mode 100644
index 0000000..99d532e
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/disk.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/error.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/error.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/error.png
new file mode 100644
index 0000000..628cf2d
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/error.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/exclamation.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/exclamation.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/exclamation.png
new file mode 100644
index 0000000..c37bd06
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/exclamation.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/external_link_listing_small.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/external_link_listing_small.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/external_link_listing_small.png
new file mode 100644
index 0000000..a22b1f7
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/external_link_listing_small.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/folder_explore.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/folder_explore.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/folder_explore.png
new file mode 100644
index 0000000..0ba9391
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/folder_explore.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/grey_circle.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/grey_circle.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/grey_circle.png
new file mode 100644
index 0000000..69ede6c
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/grey_circle.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/help.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/help.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/help.png
new file mode 100644
index 0000000..5c87017
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/help.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/information.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/information.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/information.png
new file mode 100644
index 0000000..12cd1ae
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/information.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lightbulb.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lightbulb.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lightbulb.png
new file mode 100644
index 0000000..d22fde8
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lightbulb.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lock.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lock.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lock.png
new file mode 100644
index 0000000..2ebc4f6
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lock.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lock_open.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lock_open.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lock_open.png
new file mode 100644
index 0000000..a471765
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/lock_open.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/magnifier.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/magnifier.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/magnifier.png
new file mode 100644
index 0000000..cf3d97f
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/magnifier.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/multiple_star.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/multiple_star.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/multiple_star.png
new file mode 100644
index 0000000..c663d73
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/multiple_star.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/page_white_code.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/page_white_code.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/page_white_code.png
new file mode 100644
index 0000000..0c76bd1
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/page_white_code.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/plugin.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/plugin.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/plugin.png
new file mode 100644
index 0000000..6187b15
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/plugin.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/remote_resource.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/remote_resource.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/remote_resource.png
new file mode 100644
index 0000000..b8edc12
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/remote_resource.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/server.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/server.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/server.png
new file mode 100644
index 0000000..720a237
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/server.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/star.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/star.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/star.png
new file mode 100644
index 0000000..b88c857
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/star.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/style.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/style.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/style.png
new file mode 100644
index 0000000..81e41de
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/style.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/sum.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/sum.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/sum.png
new file mode 100644
index 0000000..fd7b32e
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/sum.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/tag_blue.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/tag_blue.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/tag_blue.png
new file mode 100644
index 0000000..9757fc6
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/tag_blue.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_linespacing (collapse).png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_linespacing (collapse).png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_linespacing (collapse).png
new file mode 100644
index 0000000..ff09e31
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_linespacing (collapse).png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_linespacing.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_linespacing.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_linespacing.png
new file mode 100644
index 0000000..1a91cbd
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_linespacing.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_list_numbers.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_list_numbers.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_list_numbers.png
new file mode 100644
index 0000000..33b0b8d
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/text_list_numbers.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/tick.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/tick.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/tick.png
new file mode 100644
index 0000000..a9925a0
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/tick.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/user.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/user.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/user.png
new file mode 100644
index 0000000..79f35cc
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/famfamfam_silk/user.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/favicon.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/favicon.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/favicon.png
new file mode 100644
index 0000000..1ac1bde
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/favicon.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/fold.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/fold.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/fold.png
new file mode 100644
index 0000000..a13d280
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/fold.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/fold_16x16.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/fold_16x16.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/fold_16x16.png
new file mode 100644
index 0000000..5a37b67
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/fold_16x16.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/unfold.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/unfold.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/unfold.png
new file mode 100644
index 0000000..589e2c9
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/unfold.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/unfold_16x16.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/unfold_16x16.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/unfold_16x16.png
new file mode 100644
index 0000000..dd2396c
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/folds/unfold_16x16.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/info-sphere-35.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/info-sphere-35.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/info-sphere-35.png
new file mode 100644
index 0000000..cdbacd3
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/info-sphere-35.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/info-sphere-50.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/info-sphere-50.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/info-sphere-50.png
new file mode 100644
index 0000000..8e8ac21
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/info-sphere-50.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/open_in_BioCatalogue.pdn
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/open_in_BioCatalogue.pdn b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/open_in_BioCatalogue.pdn
new file mode 100644
index 0000000..3357714
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/open_in_BioCatalogue.pdn differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/open_in_BioCatalogue.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/open_in_BioCatalogue.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/open_in_BioCatalogue.png
new file mode 100644
index 0000000..897b2c1
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/open_in_BioCatalogue.png differ


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context-osgi.xml b/taverna-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context-osgi.xml
new file mode 100644
index 0000000..f595eda
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context-osgi.xml
@@ -0,0 +1,37 @@
+<?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="askUserMasterPasswordProvider" interface="net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider" />
+
+	<service ref="simpleMasterPasswordProvider" interface="net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider" />
+
+	<service ref="askUserJavaTruststorePasswordProvider" interface="net.sf.taverna.t2.security.credentialmanager.JavaTruststorePasswordProvider" />
+
+	<service ref="askUserServiceUsernameAndPasswordProvider" interface="net.sf.taverna.t2.security.credentialmanager.ServiceUsernameAndPasswordProvider" />
+
+	<service ref="askUserTrustConfirmationProvider" interface="net.sf.taverna.t2.security.credentialmanager.TrustConfirmationProvider" />
+
+	<service ref="UIUsernamePasswordProvider" auto-export="interfaces" />
+        
+	<service ref="UIMasterPasswordProvider" auto-export="interfaces" />
+	
+        <service ref="ConfirmTrustedCertificateUI" auto-export="interfaces" />
+
+	<service ref="InitialiseSSLStartupHook" interface="net.sf.taverna.t2.workbench.StartupSPI" />
+	
+        <service ref="SetCredManAuthenticatorStartupHook" interface="net.sf.taverna.t2.workbench.StartupSPI" />
+
+	<service ref="CredentialManagerMenu" auto-export="interfaces" />
+
+	<reference id="CredentialManager" interface="net.sf.taverna.t2.security.credentialmanager.CredentialManager" />
+	
+        <reference id="distinguishedNameParser" interface="net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser" />
+	
+        <reference id="ApplicationConfiguration" interface="uk.org.taverna.configuration.app.ApplicationConfiguration" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context.xml
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context.xml b/taverna-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context.xml
new file mode 100644
index 0000000..0e54c93
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context.xml
@@ -0,0 +1,44 @@
+<?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="askUserMasterPasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.AskUserMasterPasswordProvider" />
+
+	<bean id="simpleMasterPasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.SimpleMasterPasswordProvider" />
+
+	<bean id="askUserJavaTruststorePasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.AskUserJavaTruststorePasswordProvider" />
+
+	<bean id="askUserServiceUsernameAndPasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.AskUserServiceUsernameAndPasswordProvider" />
+
+	<bean id="askUserTrustConfirmationProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.AskUserTrustConfirmationProvider" />
+
+	<bean id="MasterPasswordProviderComparator" class="net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider$ProviderComparator" />
+
+	<bean id="UIUsernamePasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.UIUsernamePasswordProvider" >
+                <property name="distinguishedNameParser" ref="distinguishedNameParser" />
+	</bean>            
+	
+        <bean id="UIMasterPasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.UIMasterPasswordProvider">
+		<property name="applicationConfiguration" ref="ApplicationConfiguration" />
+                <property name="distinguishedNameParser" ref="distinguishedNameParser" />
+	</bean>
+	
+        <bean id="ConfirmTrustedCertificateUI" class="net.sf.taverna.t2.workbench.ui.credentialmanager.ConfirmTrustedCertificateUI">
+                <property name="distinguishedNameParser" ref="distinguishedNameParser" />
+	</bean>            
+	
+        <bean id="InitialiseSSLStartupHook" class="net.sf.taverna.t2.workbench.ui.credentialmanager.startup.InitialiseSSLStartupHook">
+		<property name="credentialManager" ref="CredentialManager" />
+	</bean>
+	
+        <bean id="SetCredManAuthenticatorStartupHook" class="net.sf.taverna.t2.workbench.ui.credentialmanager.startup.SetCredManAuthenticatorStartupHook" >
+		<property name="credentialManager" ref="CredentialManager" />
+	</bean>
+
+	<bean id="CredentialManagerMenu" class="net.sf.taverna.t2.workbench.ui.credentialmanager.menu.CredentialManagerMenu" >
+		<property name="credentialManager" ref="CredentialManager" />
+                <property name="distinguishedNameParser" ref="distinguishedNameParser" />
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/images/cred_manager.png
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/images/cred_manager.png b/taverna-credential-manager-ui/src/main/resources/images/cred_manager.png
new file mode 100644
index 0000000..48cd63c
Binary files /dev/null and b/taverna-credential-manager-ui/src/main/resources/images/cred_manager.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/images/cred_manager16x16.png
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/images/cred_manager16x16.png b/taverna-credential-manager-ui/src/main/resources/images/cred_manager16x16.png
new file mode 100644
index 0000000..c6e73b5
Binary files /dev/null and b/taverna-credential-manager-ui/src/main/resources/images/cred_manager16x16.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/images/cred_manager_transparent.png
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/images/cred_manager_transparent.png b/taverna-credential-manager-ui/src/main/resources/images/cred_manager_transparent.png
new file mode 100644
index 0000000..1e89bde
Binary files /dev/null and b/taverna-credential-manager-ui/src/main/resources/images/cred_manager_transparent.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/images/table/entry_heading.png
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/images/table/entry_heading.png b/taverna-credential-manager-ui/src/main/resources/images/table/entry_heading.png
new file mode 100644
index 0000000..8b59845
Binary files /dev/null and b/taverna-credential-manager-ui/src/main/resources/images/table/entry_heading.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/images/table/key_entry.png
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/images/table/key_entry.png b/taverna-credential-manager-ui/src/main/resources/images/table/key_entry.png
new file mode 100644
index 0000000..1fd18c6
Binary files /dev/null and b/taverna-credential-manager-ui/src/main/resources/images/table/key_entry.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/images/table/keypair_entry.png
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/images/table/keypair_entry.png b/taverna-credential-manager-ui/src/main/resources/images/table/keypair_entry.png
new file mode 100644
index 0000000..8fd3e8b
Binary files /dev/null and b/taverna-credential-manager-ui/src/main/resources/images/table/keypair_entry.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/images/table/trustcert_entry.png
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/images/table/trustcert_entry.png b/taverna-credential-manager-ui/src/main/resources/images/table/trustcert_entry.png
new file mode 100644
index 0000000..0f110e1
Binary files /dev/null and b/taverna-credential-manager-ui/src/main/resources/images/table/trustcert_entry.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-data-management-config-ui/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-data-management-config-ui/pom.xml b/taverna-data-management-config-ui/pom.xml
new file mode 100644
index 0000000..f044b8a
--- /dev/null
+++ b/taverna-data-management-config-ui/pom.xml
@@ -0,0 +1,84 @@
+<?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>taverna-data-management-config-ui</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Data management configuration UI</name>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-ui</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-helper-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<!--<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-run-ui</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		-->
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${taverna.osgi.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-database-configuration-api</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+
+		<!-- <dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-aop</artifactId>
+			<version>${spring.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.aspectj</groupId>
+			<artifactId>aspectjrt</artifactId>
+			<version>${aspectj.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.aspectj</groupId>
+			<artifactId>aspectjweaver</artifactId>
+			<version>${aspectj.version}</version>
+		</dependency> -->
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>${log4j.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationPanel.java b/taverna-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationPanel.java
new file mode 100644
index 0000000..b705362
--- /dev/null
+++ b/taverna-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationPanel.java
@@ -0,0 +1,304 @@
+/*******************************************************************************
+ * 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.reference.config;
+
+import static java.awt.Color.RED;
+import static java.awt.Font.PLAIN;
+import static java.awt.GridBagConstraints.BOTH;
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.RELATIVE;
+import static java.awt.GridBagConstraints.WEST;
+import static net.sf.taverna.t2.workbench.helper.Helper.showHelp;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.sql.Connection;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.lang.ui.DialogTextArea;
+import uk.org.taverna.configuration.database.DatabaseConfiguration;
+import uk.org.taverna.configuration.database.DatabaseManager;
+
+@SuppressWarnings("serial")
+public class DataManagementConfigurationPanel extends JPanel {
+	private DatabaseConfiguration configuration;
+	private DatabaseManager databaseManager;
+
+	private JCheckBox enableProvenance;
+	private JCheckBox enableInMemory;
+	private JButton helpButton;
+	private JButton resetButton;
+	private JButton applyButton;
+	private JTextArea storageText;
+	private JTextArea exposeDatanatureText;
+	private JCheckBox exposeDatanatureBox;
+	private DialogTextArea enableInMemoryTextDisabled;
+
+	public DataManagementConfigurationPanel(DatabaseConfiguration configuration, DatabaseManager databaseManager) {
+		this.configuration = configuration;
+		this.databaseManager = databaseManager;
+
+		setLayout(generateLayout());
+		resetFields();
+	}
+
+	private static final boolean ADD_WARNING_LISTENERS = false;
+
+	private GridBagLayout generateLayout() {
+		GridBagLayout gridbag = new GridBagLayout();
+		GridBagConstraints c = new GridBagConstraints();
+
+		enableProvenance = new JCheckBox("Enable provenance capture");
+		DialogTextArea enableProvenanceText = new DialogTextArea(
+				"Disabling provenance will prevent you from being able to view intermediate results, but does give a performance benefit.");
+		Font plain = enableProvenanceText.getFont().deriveFont(PLAIN, 11);
+		enableProvenanceText.setLineWrap(true);
+		enableProvenanceText.setWrapStyleWord(true);
+		enableProvenanceText.setEditable(false);
+		enableProvenanceText.setFocusable(false);
+		enableProvenanceText.setOpaque(false);
+		enableProvenanceText.setFont(plain);
+
+		enableInMemory = new JCheckBox("In-memory storage");
+		DialogTextArea enableInMemoryText = new DialogTextArea(
+				"Data will not be stored between workbench sessions. If you run workflows passing larger amounts of data, try disabling in-memory storage, which can reduce execution performance, but also Taverna's memory consumption. ");
+		enableInMemoryText.setLineWrap(true);
+		enableInMemoryText.setWrapStyleWord(true);
+		enableInMemoryText.setEditable(false);
+		enableInMemoryText.setFocusable(false);
+		enableInMemoryText.setOpaque(false);
+		enableInMemoryText.setFont(plain);
+
+		enableInMemoryTextDisabled = new DialogTextArea(
+				"If you enable in-memory storage of data when provenance collection is turned on then provenance will not be available after you shutdown Taverna as the in-memory data will be lost.");
+		enableInMemoryTextDisabled.setLineWrap(true);
+		enableInMemoryTextDisabled.setWrapStyleWord(true);
+		enableInMemoryTextDisabled.setEditable(false);
+		enableInMemoryTextDisabled.setFocusable(false);
+		enableInMemoryTextDisabled.setOpaque(false);
+		enableInMemoryTextDisabled.setFont(plain);
+		enableInMemoryTextDisabled.setForeground(RED);
+		enableInMemoryTextDisabled.setVisible(false);
+
+		// Disable warning as inMemory is default
+		// To re-enable - also see resetFields()
+
+		if (ADD_WARNING_LISTENERS) {
+			enableInMemory.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					enableInMemoryTextDisabled.setVisible(enableProvenance
+							.isSelected() && enableInMemory.isSelected());
+				}
+			});
+			enableProvenance.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					enableInMemoryTextDisabled.setVisible(enableProvenance
+							.isSelected() && enableInMemory.isSelected());
+				}
+			});
+		}
+
+		storageText = new JTextArea(
+				"Select how Taverna stores the data and provenance produced when a workflow is run. This includes workflow results and intermediate results.");
+		storageText.setLineWrap(true);
+		storageText.setWrapStyleWord(true);
+		storageText.setEditable(false);
+		storageText.setFocusable(false);
+		storageText.setBorder(new EmptyBorder(10, 10, 10, 10));
+		storageText.setFont(plain);
+
+		JComponent portPanel = createDerbyServerStatusComponent();
+
+		c.anchor = WEST;
+		c.insets = new Insets(0, 0, 10, 0);
+		c.gridx = 0;
+		c.gridy = RELATIVE;
+		c.weightx = 0.0;
+		c.weighty = 0.0;
+		c.fill = HORIZONTAL;
+		gridbag.setConstraints(storageText, c);
+		add(storageText);
+
+		c.ipady = 0;
+		c.insets = new Insets(0, 0, 5, 0);
+		c.fill = NONE;
+		gridbag.setConstraints(enableProvenance, c);
+		add(enableProvenance);
+
+		c.insets = new Insets(0, 20, 15, 20);
+		c.fill = HORIZONTAL;
+		gridbag.setConstraints(enableProvenanceText, c);
+		add(enableProvenanceText);
+
+		c.insets = new Insets(0, 0, 5, 0);
+		c.fill = GridBagConstraints.NONE;
+		gridbag.setConstraints(enableInMemory, c);
+		add(enableInMemory);
+
+		c.insets = new Insets(0, 20, 15, 20);
+		c.fill = HORIZONTAL;
+		gridbag.setConstraints(enableInMemoryText, c);
+		add(enableInMemoryText);
+
+		c.insets = new Insets(0, 20, 15, 20);
+		c.fill = HORIZONTAL;
+		gridbag.setConstraints(enableInMemoryTextDisabled, c);
+		add(enableInMemoryTextDisabled);
+
+		c.insets = new Insets(0, 20, 15, 20);
+		gridbag.setConstraints(portPanel, c);
+		add(portPanel);
+
+		c.insets = new Insets(0, 0, 5, 0);
+		c.fill = NONE;
+		exposeDatanatureBox = new JCheckBox(
+				"Allow setting of input data encoding");
+		gridbag.setConstraints(exposeDatanatureBox, c);
+		add(exposeDatanatureBox);
+
+		exposeDatanatureText = new JTextArea(
+				"Select if you want to control how Taverna handles files read as input data");
+		exposeDatanatureText.setLineWrap(true);
+		exposeDatanatureText.setWrapStyleWord(true);
+		exposeDatanatureText.setEditable(false);
+		exposeDatanatureText.setFocusable(false);
+		exposeDatanatureText.setOpaque(false);
+		exposeDatanatureText.setFont(plain);
+
+		c.insets = new Insets(0, 20, 15, 20);
+		c.fill = HORIZONTAL;
+		gridbag.setConstraints(exposeDatanatureText, c);
+		add(exposeDatanatureText);
+
+		JPanel buttonPanel = createButtonPanel();
+		c.weightx = 1.0;
+		c.weighty = 1.0;
+		c.fill = BOTH;
+		c.insets = new Insets(0, 0, 5, 0);
+		gridbag.setConstraints(buttonPanel, c);
+		add(buttonPanel);
+		return gridbag;
+	}
+
+	private JComponent createDerbyServerStatusComponent() {
+		DialogTextArea textArea = new DialogTextArea();
+		boolean running;
+
+		try (Connection connection = databaseManager.getConnection()) {
+			running = databaseManager.isRunning();
+		} catch (Exception e) {
+			running = false;
+		}
+
+		if (running)
+			textArea.setText("The database is currently running on port: "
+					+ configuration.getCurrentPort() + ".");
+		else
+			textArea.setText("Unable to retrieve a database connection - "
+					+ "the database is not available.");
+
+		textArea.setLineWrap(true);
+		textArea.setWrapStyleWord(true);
+		textArea.setEditable(false);
+		textArea.setFocusable(false);
+		textArea.setOpaque(false);
+		textArea.setAlignmentX(CENTER_ALIGNMENT);
+		textArea.setFont(textArea.getFont().deriveFont(PLAIN, 11));
+		textArea.setVisible(configuration.getStartInternalDerbyServer());
+		return textArea;
+	}
+
+	// for testing only
+//	public static void main(String[] args) {
+//		JDialog dialog = new JDialog();
+//		dialog.add(new DataManagementConfigurationPanel());
+//		dialog.setModal(true);
+//		dialog.setSize(500, 300);
+//		dialog.setVisible(true);
+//		System.exit(0);
+//	}
+
+	public void resetFields() {
+		enableInMemory.setSelected(configuration.isInMemory());
+		enableProvenance.setSelected(configuration.isProvenanceEnabled());
+		exposeDatanatureBox.setSelected(configuration.isExposeDatanature());
+
+		if (ADD_WARNING_LISTENERS) {
+			enableInMemoryTextDisabled.setVisible(enableProvenance.isSelected()
+					&& enableInMemory.isSelected());
+		}
+	}
+
+	/*private boolean workflowInstances() {
+		return DataflowRunsComponent.getInstance().getRunListCount()>0;
+	}*/
+
+	private void applySettings() {
+		configuration.setProvenanceEnabled(enableProvenance.isSelected());
+		configuration.setInMemory(enableInMemory.isSelected());
+		configuration.setExposeDatanature(exposeDatanatureBox.isSelected());
+	}
+
+	private JPanel createButtonPanel() {
+		final JPanel panel = new JPanel();
+
+		helpButton = new JButton(new AbstractAction("Help") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				showHelp(panel);
+			}
+		});
+		panel.add(helpButton);
+
+		resetButton = new JButton(new AbstractAction("Reset") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				resetFields();
+			}
+		});
+		panel.add(resetButton);
+
+		applyButton = new JButton(new AbstractAction("Apply") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				applySettings();
+				resetFields();
+			}
+		});
+		panel.add(applyButton);
+
+		return panel;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationUIFactory.java
----------------------------------------------------------------------
diff --git a/taverna-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationUIFactory.java b/taverna-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationUIFactory.java
new file mode 100644
index 0000000..2799c7e
--- /dev/null
+++ b/taverna-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationUIFactory.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * 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.reference.config;
+
+import javax.swing.JPanel;
+
+import uk.org.taverna.configuration.Configurable;
+import uk.org.taverna.configuration.ConfigurationUIFactory;
+import uk.org.taverna.configuration.database.DatabaseConfiguration;
+import uk.org.taverna.configuration.database.DatabaseManager;
+
+public class DataManagementConfigurationUIFactory implements
+		ConfigurationUIFactory {
+	private DatabaseConfiguration databaseConfiguration;
+	private DatabaseManager databaseManager;
+
+	private DataManagementConfigurationPanel configPanel;
+
+	@Override
+	public boolean canHandle(String uuid) {
+		return uuid.equals(getConfigurable().getUUID());
+	}
+
+	@Override
+	public JPanel getConfigurationPanel() {
+		if (configPanel == null)
+			configPanel = new DataManagementConfigurationPanel(
+					databaseConfiguration, databaseManager);
+		configPanel.resetFields();
+		return configPanel;
+	}
+
+	@Override
+	public Configurable getConfigurable() {
+		return databaseConfiguration;
+	}
+
+	public void setDatabaseConfiguration(
+			DatabaseConfiguration databaseConfiguration) {
+		this.databaseConfiguration = databaseConfiguration;
+	}
+
+	public void setDatabaseManager(DatabaseManager databaseManager) {
+		this.databaseManager = databaseManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-data-management-config-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
----------------------------------------------------------------------
diff --git a/taverna-data-management-config-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-data-management-config-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
new file mode 100644
index 0000000..8afa6ca
--- /dev/null
+++ b/taverna-data-management-config-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.reference.config.DataManagementConfigurationUIFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context-osgi.xml b/taverna-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context-osgi.xml
new file mode 100644
index 0000000..89f84a7
--- /dev/null
+++ b/taverna-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-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="DataManagementConfigurationUIFactory" interface="uk.org.taverna.configuration.ConfigurationUIFactory" />
+
+	<reference id="databaseConfiguration" interface="uk.org.taverna.configuration.database.DatabaseConfiguration" />
+	<reference id="databaseManager" interface="uk.org.taverna.configuration.database.DatabaseManager" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context.xml
----------------------------------------------------------------------
diff --git a/taverna-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context.xml b/taverna-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context.xml
new file mode 100644
index 0000000..f9f40ed
--- /dev/null
+++ b/taverna-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-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="DataManagementConfigurationUIFactory" class="net.sf.taverna.t2.workbench.reference.config.DataManagementConfigurationUIFactory">
+		<property name="databaseConfiguration" ref="databaseConfiguration"/>
+		<property name="databaseManager" ref="databaseManager"/>
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-design-ui/pom.xml b/taverna-design-ui/pom.xml
new file mode 100644
index 0000000..0c549bd
--- /dev/null
+++ b/taverna-design-ui/pom.xml
@@ -0,0 +1,61 @@
+<?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>taverna-design-ui</artifactId>
+	<name>Apache Taverna Design UI</name>
+	<packaging>bundle</packaging>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-activity-icons-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-edits-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-contextual-views-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-selection-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-ui</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+			<version>${taverna.language.version}</version>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddConditionAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddConditionAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddConditionAction.java
new file mode 100644
index 0000000..510775f
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddConditionAction.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+
+import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.AddChildEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.core.BlockingControlLink;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+
+/**
+ * Action for adding a condition to the dataflow.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class AddConditionAction extends DataflowEditAction {
+	private static final Logger logger = Logger.getLogger(AddConditionAction.class);
+	private static final Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	private Processor control;
+	private Processor target;
+
+	public AddConditionAction(Workflow dataflow, Processor control,
+			Processor target, Component component, EditManager editManager,
+			SelectionManager selectionManager,
+			ActivityIconManager activityIconManager) {
+		super(dataflow, component, editManager, selectionManager);
+		this.control = control;
+		this.target = target;
+		ProcessorBinding processorBinding = scufl2Tools
+				.processorBindingForProcessor(control, dataflow.getParent()
+						.getMainProfile());
+		putValue(SMALL_ICON,
+				activityIconManager.iconForActivity(processorBinding
+						.getBoundActivity().getType()));
+		putValue(NAME, control.getName());
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent event) {
+		try {
+			BlockingControlLink controlLink = new BlockingControlLink();
+			controlLink.setUntilFinished(control);
+			controlLink.setBlock(target);
+			editManager.doDataflowEdit(dataflow.getParent(),
+					new AddChildEdit<>(dataflow, controlLink));
+		} catch (EditException e) {
+			logger.debug("Create control link between '" + control.getName()
+					+ "' and '" + target.getName() + "' failed");
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowInputAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowInputAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowInputAction.java
new file mode 100644
index 0000000..ff56997
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowInputAction.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.util.HashSet;
+import java.util.Set;
+
+import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog;
+import net.sf.taverna.t2.workbench.design.ui.DataflowInputPortPanel;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.AddWorkflowInputPortEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+
+/**
+ * Action for adding an input port to the dataflow.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class AddDataflowInputAction extends DataflowEditAction {
+	private static final Logger logger = Logger
+			.getLogger(AddDataflowInputAction.class);
+
+	public AddDataflowInputAction(Workflow dataflow, Component component,
+			EditManager editManager, SelectionManager selectionManager) {
+		super(dataflow, component, editManager, selectionManager);
+		putValue(SMALL_ICON, WorkbenchIcons.inputIcon);
+		putValue(NAME, "Workflow input port");
+		putValue(SHORT_DESCRIPTION, "Add workflow input port");
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent event) {
+		try {
+			Set<String> usedInputPorts = new HashSet<>();
+			for (InputWorkflowPort inputPort : dataflow.getInputPorts())
+				usedInputPorts.add(inputPort.getName());
+
+			DataflowInputPortPanel inputPanel = new DataflowInputPortPanel();
+
+			ValidatingUserInputDialog vuid = new ValidatingUserInputDialog(
+					"Add Workflow Input Port", inputPanel);
+			vuid.addTextComponentValidation(inputPanel.getPortNameField(),
+					"Set the workflow input port name.", usedInputPorts,
+					"Duplicate workflow input port name.",
+					"[\\p{L}\\p{Digit}_.]+",
+					"Invalid workflow input port name.");
+			vuid.addMessageComponent(inputPanel.getSingleValueButton(),
+					"Set the input port type.");
+			vuid.addMessageComponent(inputPanel.getListValueButton(),
+					"Set the input port list depth.");
+			vuid.setSize(new Dimension(400, 250));
+
+			inputPanel.setPortDepth(0);
+
+			if (vuid.show(component)) {
+				InputWorkflowPort dataflowInputPort = new InputWorkflowPort();
+				dataflowInputPort.setName(inputPanel.getPortName());
+				dataflowInputPort.setDepth(inputPanel.getPortDepth());
+				editManager.doDataflowEdit(dataflow.getParent(),
+						new AddWorkflowInputPortEdit(dataflow,
+								dataflowInputPort));
+			}
+		} catch (EditException e) {
+			logger.warn("Adding a new workflow input port failed");
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowOutputAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowOutputAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowOutputAction.java
new file mode 100644
index 0000000..98bf8be
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowOutputAction.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.util.HashSet;
+import java.util.Set;
+
+import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog;
+import net.sf.taverna.t2.workbench.design.ui.DataflowOutputPortPanel;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.AddWorkflowOutputPortEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+
+/**
+ * Action for adding an output port to the dataflow.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class AddDataflowOutputAction extends DataflowEditAction {
+	private static final Logger logger = Logger
+			.getLogger(AddDataflowOutputAction.class);
+
+	public AddDataflowOutputAction(Workflow dataflow, Component component,
+			EditManager editManager, SelectionManager selectionManager) {
+		super(dataflow, component, editManager, selectionManager);
+		putValue(SMALL_ICON, WorkbenchIcons.outputIcon);
+		putValue(NAME, "Workflow output port");
+		putValue(SHORT_DESCRIPTION, "Add workflow output port");
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent event) {
+		try {
+			Set<String> usedOutputPorts = new HashSet<>();
+			for (OutputWorkflowPort outputPort : dataflow.getOutputPorts())
+				usedOutputPorts.add(outputPort.getName());
+
+			DataflowOutputPortPanel inputPanel = new DataflowOutputPortPanel();
+
+			ValidatingUserInputDialog vuid = new ValidatingUserInputDialog(
+					"Add Workflow Output Port", inputPanel);
+			vuid.addTextComponentValidation(inputPanel.getPortNameField(),
+					"Set the workflow output port name.", usedOutputPorts,
+					"Duplicate workflow output port name.",
+					"[\\p{L}\\p{Digit}_.]+",
+					"Invalid workflow output port name.");
+			vuid.setSize(new Dimension(400, 200));
+
+			if (vuid.show(component)) {
+				String portName = inputPanel.getPortName();
+				OutputWorkflowPort dataflowOutputPort = new OutputWorkflowPort();
+				dataflowOutputPort.setName(portName);
+				editManager.doDataflowEdit(dataflow.getParent(),
+						new AddWorkflowOutputPortEdit(dataflow,
+								dataflowOutputPort));
+			}
+		} catch (EditException e) {
+			logger.debug("Create workflow output port failed", e);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/DataflowEditAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/DataflowEditAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/DataflowEditAction.java
new file mode 100644
index 0000000..a2ca5ea
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/DataflowEditAction.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import java.awt.Component;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * Abstract superclass of dataflow edit actions.
+ * 
+ * @author David Withers
+ */
+public abstract class DataflowEditAction extends AbstractAction {
+	private static final long serialVersionUID = -1155192575675025091L;
+
+	protected final SelectionManager selectionManager;
+	protected EditManager editManager;
+	protected DataflowSelectionModel dataflowSelectionModel;
+	protected Workflow dataflow;
+	protected Component component;
+	protected Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	public DataflowEditAction(Workflow dataflow, Component component,
+			EditManager editManager, SelectionManager selectionManager) {
+		this.dataflow = dataflow;
+		this.component = component;
+		this.editManager = editManager;
+		this.selectionManager = selectionManager;
+		dataflowSelectionModel = selectionManager
+				.getDataflowSelectionModel(dataflow.getParent());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowInputPortAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowInputPortAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowInputPortAction.java
new file mode 100644
index 0000000..e4513d2
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowInputPortAction.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog;
+import net.sf.taverna.t2.workbench.design.ui.DataflowInputPortPanel;
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.ChangeDepthEdit;
+import net.sf.taverna.t2.workflow.edits.RenameEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+
+/**
+ * Action for editing a dataflow input port.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class EditDataflowInputPortAction extends DataflowEditAction {
+	private static Logger logger = Logger
+			.getLogger(EditDataflowInputPortAction.class);
+
+	private InputWorkflowPort port;
+
+	public EditDataflowInputPortAction(Workflow dataflow,
+			InputWorkflowPort port, Component component,
+			EditManager editManager, SelectionManager selectionManager) {
+		super(dataflow, component, editManager, selectionManager);
+		this.port = port;
+		putValue(SMALL_ICON, WorkbenchIcons.renameIcon);
+		putValue(NAME, "Edit workflow input port...");
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		Set<String> usedInputPorts = new HashSet<>();
+		for (InputWorkflowPort usedInputPort : dataflow.getInputPorts())
+			if (!usedInputPort.getName().equals(port.getName()))
+				usedInputPorts.add(usedInputPort.getName());
+
+		DataflowInputPortPanel inputPanel = new DataflowInputPortPanel();
+
+		ValidatingUserInputDialog vuid = new ValidatingUserInputDialog(
+				"Edit Workflow Input Port", inputPanel);
+		vuid.addTextComponentValidation(inputPanel.getPortNameField(),
+				"Set the workflow input port name.", usedInputPorts,
+				"Duplicate workflow input port name.", "[\\p{L}\\p{Digit}_.]+",
+				"Invalid workflow input port name.");
+		vuid.addMessageComponent(inputPanel.getSingleValueButton(),
+				"Set the input port type.");
+		vuid.addMessageComponent(inputPanel.getListValueButton(),
+				"Set the input port list depth.");
+		vuid.setSize(new Dimension(400, 250));
+
+		inputPanel.setPortName(port.getName());
+		inputPanel.setPortDepth(port.getDepth());
+
+		try {
+			if (vuid.show(component))
+				changeInputPort(inputPanel);
+		} catch (EditException e1) {
+			logger.warn("Rename workflow input port failed", e1);
+		}
+	}
+
+	private void changeInputPort(DataflowInputPortPanel inputPanel)
+			throws EditException {
+		List<Edit<?>> editList = new ArrayList<>();
+		String portName = inputPanel.getPortName();
+		if (!portName.equals(port.getName()))
+			editList.add(new RenameEdit<>(port, portName));
+		int portDepth = inputPanel.getPortDepth();
+		if (portDepth != port.getDepth())
+			editList.add(new ChangeDepthEdit<>(port, portDepth));
+		if (editList.size() == 1)
+			editManager.doDataflowEdit(dataflow.getParent(), editList.get(0));
+		else if (editList.size() > 1)
+			editManager.doDataflowEdit(dataflow.getParent(), new CompoundEdit(
+					editList));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowOutputPortAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowOutputPortAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowOutputPortAction.java
new file mode 100644
index 0000000..da7c0e2
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowOutputPortAction.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.util.HashSet;
+import java.util.Set;
+
+import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog;
+import net.sf.taverna.t2.workbench.design.ui.DataflowOutputPortPanel;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.RenameEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+
+/**
+ * Action for editing a dataflow output port.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class EditDataflowOutputPortAction extends DataflowEditAction {
+	private static final Logger logger = Logger
+			.getLogger(EditDataflowOutputPortAction.class);
+
+	private OutputWorkflowPort port;
+
+	public EditDataflowOutputPortAction(Workflow dataflow,
+			OutputWorkflowPort port, Component component,
+			EditManager editManager, SelectionManager selectionManager) {
+		super(dataflow, component, editManager, selectionManager);
+		this.port = port;
+		putValue(SMALL_ICON, WorkbenchIcons.renameIcon);
+		putValue(NAME, "Edit workflow output port...");
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		Set<String> usedOutputPorts = new HashSet<>();
+		for (OutputWorkflowPort usedOutputPort : dataflow.getOutputPorts())
+			if (!usedOutputPort.getName().equals(port.getName()))
+				usedOutputPorts.add(usedOutputPort.getName());
+
+		DataflowOutputPortPanel inputPanel = new DataflowOutputPortPanel();
+
+		ValidatingUserInputDialog vuid = new ValidatingUserInputDialog(
+				"Edit Workflow Output Port", inputPanel);
+		vuid.addTextComponentValidation(inputPanel.getPortNameField(),
+				"Set the workflow output port name.", usedOutputPorts,
+				"Duplicate workflow output port name.",
+				"[\\p{L}\\p{Digit}_.]+", "Invalid workflow output port name.");
+		vuid.setSize(new Dimension(400, 200));
+
+		inputPanel.setPortName(port.getName());
+
+		try {
+			if (vuid.show(component))
+				changeOutputPort(inputPanel);
+		} catch (EditException ex) {
+			logger.debug("Rename workflow output port failed", ex);
+		}
+	}
+
+	private void changeOutputPort(DataflowOutputPortPanel inputPanel)
+			throws EditException {
+		editManager.doDataflowEdit(dataflow.getParent(), new RenameEdit<>(port,
+				inputPanel.getPortName()));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveConditionAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveConditionAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveConditionAction.java
new file mode 100644
index 0000000..89036f0
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveConditionAction.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.RemoveChildEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.ControlLink;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * Action for removing a condition from the dataflow.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class RemoveConditionAction extends DataflowEditAction {
+	private static final Logger logger = Logger
+			.getLogger(RemoveConditionAction.class);
+
+	private ControlLink controlLink;
+
+	public RemoveConditionAction(Workflow dataflow, ControlLink controlLink,
+			Component component, EditManager editManager,
+			SelectionManager selectionManager) {
+		super(dataflow, component, editManager, selectionManager);
+		this.controlLink = controlLink;
+		putValue(SMALL_ICON, deleteIcon);
+		putValue(NAME, "Delete control link");
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		try {
+			dataflowSelectionModel.removeSelection(controlLink);
+			editManager.doDataflowEdit(dataflow.getParent(),
+					new RemoveChildEdit<>(dataflow, controlLink));
+		} catch (EditException e1) {
+			logger.debug("Delete control link failed", e1);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowInputPortAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowInputPortAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowInputPortAction.java
new file mode 100644
index 0000000..5483ea5
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowInputPortAction.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit;
+import net.sf.taverna.t2.workflow.edits.RemoveWorkflowInputPortEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+
+/**
+ * Action for removing an input port from the dataflow.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class RemoveDataflowInputPortAction extends DataflowEditAction {
+	private static Logger logger = Logger
+			.getLogger(RemoveDataflowInputPortAction.class);
+
+	private InputWorkflowPort port;
+
+	public RemoveDataflowInputPortAction(Workflow dataflow,
+			InputWorkflowPort port, Component component,
+			EditManager editManager, SelectionManager selectionManager) {
+		super(dataflow, component, editManager, selectionManager);
+		this.port = port;
+		putValue(SMALL_ICON, deleteIcon);
+		putValue(NAME, "Delete workflow input port");
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		try {
+			dataflowSelectionModel.removeSelection(port);
+			List<DataLink> datalinks = scufl2Tools.datalinksFrom(port);
+			if (datalinks.isEmpty())
+				editManager.doDataflowEdit(dataflow.getParent(),
+						new RemoveWorkflowInputPortEdit(dataflow, port));
+			else {
+				List<Edit<?>> editList = new ArrayList<>();
+				for (DataLink datalink : datalinks)
+					editList.add(new RemoveDataLinkEdit(dataflow, datalink));
+				editList.add(new RemoveWorkflowInputPortEdit(dataflow, port));
+				editManager.doDataflowEdit(dataflow.getParent(),
+						new CompoundEdit(editList));
+			}
+		} catch (EditException e1) {
+			logger.debug("Delete workflow input port failed", e1);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowOutputPortAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowOutputPortAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowOutputPortAction.java
new file mode 100644
index 0000000..ed91d41
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowOutputPortAction.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit;
+import net.sf.taverna.t2.workflow.edits.RemoveWorkflowOutputPortEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+
+/**
+ * Action for removing an output port from the dataflow.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class RemoveDataflowOutputPortAction extends DataflowEditAction {
+	private static final Logger logger = Logger
+			.getLogger(RemoveDataflowOutputPortAction.class);
+
+	private OutputWorkflowPort port;
+
+	public RemoveDataflowOutputPortAction(Workflow dataflow,
+			OutputWorkflowPort port, Component component,
+			EditManager editManager, SelectionManager selectionManager) {
+		super(dataflow, component, editManager, selectionManager);
+		this.port = port;
+		putValue(SMALL_ICON, deleteIcon);
+		putValue(NAME, "Delete workflow output port");
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		try {
+			dataflowSelectionModel.removeSelection(port);
+			List<DataLink> datalinks = scufl2Tools.datalinksTo(port);
+			if (datalinks.isEmpty())
+				editManager.doDataflowEdit(dataflow.getParent(),
+						new RemoveWorkflowOutputPortEdit(dataflow, port));
+			else {
+				List<Edit<?>> editList = new ArrayList<>();
+				for (DataLink datalink : datalinks)
+					editList.add(new RemoveDataLinkEdit(dataflow, datalink));
+				editList.add(new RemoveWorkflowOutputPortEdit(dataflow, port));
+				editManager.doDataflowEdit(dataflow.getParent(),
+						new CompoundEdit(editList));
+			}
+		} catch (EditException ex) {
+			logger.debug("Delete workflow output port failed", ex);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDatalinkAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDatalinkAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDatalinkAction.java
new file mode 100644
index 0000000..e4df75d
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDatalinkAction.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * Action for removing a datalink from the dataflow.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class RemoveDatalinkAction extends DataflowEditAction {
+	private static final Logger logger = Logger.getLogger(RemoveDatalinkAction.class);
+
+	private DataLink datalink;
+
+	public RemoveDatalinkAction(Workflow dataflow, DataLink datalink,
+			Component component, EditManager editManager,
+			SelectionManager selectionManager) {
+		super(dataflow, component, editManager, selectionManager);
+		this.datalink = datalink;
+		putValue(SMALL_ICON, deleteIcon);
+		putValue(NAME, "Delete data link");
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent ev) {
+		try {
+			dataflowSelectionModel.removeSelection(datalink);
+			editManager.doDataflowEdit(dataflow.getParent(),
+					new RemoveDataLinkEdit(dataflow, datalink));
+		} catch (EditException ex) {
+			logger.debug("Delete data link failed", ex);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveProcessorAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveProcessorAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveProcessorAction.java
new file mode 100644
index 0000000..063a346
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveProcessorAction.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.RemoveChildEdit;
+import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.NamedSet;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.core.BlockingControlLink;
+import uk.org.taverna.scufl2.api.core.ControlLink;
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+/**
+ * Action for removing a processor from the dataflow.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class RemoveProcessorAction extends DataflowEditAction {
+	private static final Logger logger = Logger
+			.getLogger(RemoveProcessorAction.class);
+
+	private Processor processor;
+
+	public RemoveProcessorAction(Workflow dataflow, Processor processor,
+			Component component, EditManager editManager,
+			SelectionManager selectionManager) {
+		super(dataflow, component, editManager, selectionManager);
+		this.processor = processor;
+		putValue(SMALL_ICON, deleteIcon);
+		putValue(NAME, "Delete service");
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		try {
+			dataflowSelectionModel.removeSelection(processor);
+
+			NamedSet<InputProcessorPort> inputPorts = processor.getInputPorts();
+			NamedSet<OutputProcessorPort> outputPorts = processor
+					.getOutputPorts();
+			List<BlockingControlLink> controlLinksBlocking = scufl2Tools
+					.controlLinksBlocking(processor);
+			List<BlockingControlLink> controlLinksWaitingFor = scufl2Tools
+					.controlLinksWaitingFor(processor);
+			List<Edit<?>> editList = new ArrayList<>();
+			for (InputProcessorPort inputPort : inputPorts)
+				for (DataLink datalink : scufl2Tools.datalinksTo(inputPort))
+					editList.add(new RemoveDataLinkEdit(dataflow, datalink));
+			for (OutputProcessorPort outputPort : outputPorts)
+				for (DataLink datalink : scufl2Tools.datalinksFrom(outputPort))
+					editList.add(new RemoveDataLinkEdit(dataflow, datalink));
+			for (ControlLink controlLink : controlLinksBlocking)
+				editList.add(new RemoveChildEdit<>(dataflow, controlLink));
+			for (ControlLink controlLink : controlLinksWaitingFor)
+				editList.add(new RemoveChildEdit<>(dataflow, controlLink));
+
+			for (Profile profile : dataflow.getParent().getProfiles()) {
+				List<ProcessorBinding> processorBindings = scufl2Tools
+						.processorBindingsForProcessor(processor, profile);
+				for (ProcessorBinding processorBinding : processorBindings) {
+					Activity boundActivity = processorBinding
+							.getBoundActivity();
+					List<ProcessorBinding> processorBindingsToActivity = scufl2Tools
+							.processorBindingsToActivity(boundActivity);
+					if (processorBindingsToActivity.size() == 1) {
+						editList.add(new RemoveChildEdit<>(profile,
+								boundActivity));
+						for (Configuration configuration : scufl2Tools
+								.configurationsFor(boundActivity, profile))
+							editList.add(new RemoveChildEdit<Profile>(profile,
+									configuration));
+					}
+					editList.add(new RemoveChildEdit<Profile>(profile,
+							processorBinding));
+				}
+			}
+			for (Profile profile : dataflow.getParent().getProfiles()) {
+				List<Configuration> configurations = scufl2Tools
+						.configurationsFor(processor, profile);
+				for (Configuration configuration : configurations)
+					editList.add(new RemoveChildEdit<>(profile, configuration));
+			}
+			if (editList.isEmpty())
+				editManager.doDataflowEdit(dataflow.getParent(),
+						new RemoveChildEdit<>(dataflow, processor));
+			else {
+				editList.add(new RemoveChildEdit<>(dataflow, processor));
+				editManager.doDataflowEdit(dataflow.getParent(),
+						new CompoundEdit(editList));
+			}
+		} catch (EditException e1) {
+			logger.error("Delete processor failed", e1);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RenameProcessorAction.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RenameProcessorAction.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RenameProcessorAction.java
new file mode 100644
index 0000000..5b5b733
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RenameProcessorAction.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * 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.design.actions;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.util.HashSet;
+import java.util.Set;
+
+import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog;
+import net.sf.taverna.t2.workbench.design.ui.ProcessorPanel;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.RenameEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * Action for renaming a processor.
+ * 
+ * @author David Withers
+ */
+public class RenameProcessorAction extends DataflowEditAction {
+
+	private static final long serialVersionUID = 1L;
+
+	private static Logger logger = Logger
+			.getLogger(RenameProcessorAction.class);
+
+	private Processor processor;
+
+	public RenameProcessorAction(Workflow dataflow, Processor processor,
+			Component component, EditManager editManager,
+			SelectionManager selectionManager) {
+		super(dataflow, component, editManager, selectionManager);
+		this.processor = processor;
+		putValue(SMALL_ICON, WorkbenchIcons.renameIcon);
+		putValue(NAME, "Rename service...");
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		Set<String> usedProcessors = new HashSet<>();
+		for (Processor usedProcessor : dataflow.getProcessors())
+			if (!usedProcessor.getName().equals(processor.getName()))
+				usedProcessors.add(usedProcessor.getName());
+
+		ProcessorPanel inputPanel = new ProcessorPanel();
+
+		ValidatingUserInputDialog vuid = new ValidatingUserInputDialog(
+				"Rename service", inputPanel);
+		vuid.addTextComponentValidation(inputPanel.getProcessorNameField(),
+				"Set the service name.", usedProcessors, "Duplicate service.",
+				"[\\p{L}\\p{Digit}_.]+", "Invalid service name.");
+		vuid.setSize(new Dimension(400, 200));
+
+		inputPanel.setProcessorName(processor.getName());
+
+		try {
+			if (vuid.show(component))
+				changeProcessorName(inputPanel);
+		} catch (EditException e1) {
+			logger.debug("Rename service (processor) failed", e1);
+		}
+	}
+
+	private void changeProcessorName(ProcessorPanel inputPanel)
+			throws EditException {
+		String processorName = inputPanel.getProcessorName();
+		editManager.doDataflowEdit(dataflow.getParent(), new RenameEdit<>(
+				processor, processorName));
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowInputPortPanel.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowInputPortPanel.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowInputPortPanel.java
new file mode 100644
index 0000000..e578ef2
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowInputPortPanel.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * 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.design.ui;
+
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.NORTHWEST;
+import static java.awt.GridBagConstraints.WEST;
+import static java.awt.event.ItemEvent.SELECTED;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JSpinner;
+import javax.swing.JTextField;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * UI for creating/editing dataflow input ports.
+ * 
+ * @author David Withers
+ */
+public class DataflowInputPortPanel extends JPanel {
+	private static final long serialVersionUID = 2650486705615513458L;
+
+	private JTextField portNameField;
+	private JRadioButton singleValueButton;
+	private JRadioButton listValueButton;
+	private JSpinner listDepthSpinner;
+
+	public DataflowInputPortPanel() {
+		super(new GridBagLayout());
+
+		portNameField = new JTextField();
+		singleValueButton = new JRadioButton("Single value");
+		listValueButton = new JRadioButton("List of depth ");
+		listDepthSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1));
+
+		setBorder(new EmptyBorder(10, 10, 10, 10));
+
+		GridBagConstraints constraints = new GridBagConstraints();
+
+		constraints.anchor = WEST;
+		constraints.gridx = 0;
+		constraints.gridy = 0;
+		constraints.ipadx = 10;
+		add(new JLabel("Name:"), constraints);
+
+		constraints.gridx = 1;
+		constraints.gridwidth = 2;
+		constraints.ipadx = 0;
+		constraints.weightx = 1d;
+		constraints.fill = HORIZONTAL;
+		add(portNameField, constraints);
+
+		constraints.gridx = 0;
+		constraints.gridy = 1;
+		constraints.gridwidth = 1;
+		constraints.weightx = 0d;
+		constraints.fill = NONE;
+		constraints.ipadx = 10;
+		constraints.insets = new Insets(10, 0, 0, 0);
+		add(new JLabel("Type:"), constraints);
+
+		ButtonGroup buttonGroup = new ButtonGroup();
+		buttonGroup.add(singleValueButton);
+		buttonGroup.add(listValueButton);
+
+		final JLabel helpLabel = new JLabel(
+				"Depth 1 is a list, 2 is a list of lists, etc.");
+		helpLabel.setFont(helpLabel.getFont().deriveFont(11f));
+
+		singleValueButton.addItemListener(new ItemListener() {
+			@Override
+			public void itemStateChanged(ItemEvent e) {
+				boolean selected = (e.getStateChange() == SELECTED);
+				listDepthSpinner.setEnabled(!selected);
+				helpLabel.setEnabled(!selected);
+			}
+		});
+
+		constraints.gridx = 1;
+		constraints.gridwidth = 2;
+		constraints.ipadx = 0;
+		add(singleValueButton, constraints);
+		constraints.gridy = 2;
+		constraints.gridwidth = 1;
+		constraints.insets = new Insets(0, 0, 0, 0);
+		add(listValueButton, constraints);
+		constraints.gridx = 2;
+		add(listDepthSpinner, constraints);
+		constraints.gridx = 1;
+		constraints.gridy = 3;
+		constraints.gridwidth = 2;
+		constraints.weighty = 1d;
+		constraints.anchor = NORTHWEST;
+		constraints.insets = new Insets(0, 20, 0, 0);
+		add(helpLabel, constraints);
+	}
+
+	/**
+	 * Returns the portNameField.
+	 * 
+	 * @return the portNameField
+	 */
+	public JTextField getPortNameField() {
+		return portNameField;
+	}
+
+	/**
+	 * Returns the singleValueButton.
+	 * 
+	 * @return the singleValueButton
+	 */
+	public JRadioButton getSingleValueButton() {
+		return singleValueButton;
+	}
+
+	/**
+	 * Returns the listValueButton.
+	 * 
+	 * @return the listValueButton
+	 */
+	public JRadioButton getListValueButton() {
+		return listValueButton;
+	}
+
+	/**
+	 * Returns the port name.
+	 * 
+	 * @return the port name
+	 */
+	public String getPortName() {
+		return portNameField.getText();
+	}
+
+	/**
+	 * Sets the port name.
+	 * 
+	 * @param name
+	 *            the name of the port
+	 */
+	public void setPortName(String name) {
+		portNameField.setText(name);
+		// Select the text
+		if (!name.isEmpty()) {
+			portNameField.setSelectionStart(0);
+			portNameField.setSelectionEnd(name.length());
+		}
+	}
+
+	/**
+	 * Returns the port depth.
+	 * 
+	 * @return the port depth
+	 */
+	public int getPortDepth() {
+		if (singleValueButton.isSelected())
+			return 0;
+		return (Integer) listDepthSpinner.getValue();
+	}
+
+	/**
+	 * Sets the port depth.
+	 * 
+	 * @param depth
+	 *            the depth of the port
+	 */
+	public void setPortDepth(int depth) {
+		if (depth == 0) {
+			singleValueButton.setSelected(true);
+		} else {
+			listValueButton.setSelected(true);
+			listDepthSpinner.setValue(depth);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowOutputPortPanel.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowOutputPortPanel.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowOutputPortPanel.java
new file mode 100644
index 0000000..59ac0f7
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowOutputPortPanel.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * 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.design.ui;
+
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.VERTICAL;
+import static java.awt.GridBagConstraints.WEST;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * UI for creating/editing dataflow output ports.
+ * 
+ * @author David Withers
+ */
+public class DataflowOutputPortPanel extends JPanel {
+	private static final long serialVersionUID = -2542858679939965553L;
+
+	private JTextField portNameField;
+
+	public DataflowOutputPortPanel() {
+		super(new GridBagLayout());
+
+		portNameField = new JTextField();
+ 
+		setBorder(new EmptyBorder(10, 10, 10, 10));
+		
+		GridBagConstraints constraints = new GridBagConstraints();
+
+		constraints.anchor = WEST;
+		constraints.gridx = 0;
+		constraints.gridy = 0;
+		constraints.ipadx = 10;
+		add(new JLabel("Name:"), constraints);
+
+		constraints.gridx = 1;
+		constraints.gridwidth = 2;
+		constraints.ipadx = 0;
+		constraints.weightx = 1d;
+		constraints.fill = HORIZONTAL;
+		add(portNameField, constraints);
+
+		constraints.gridx = 0;
+		constraints.gridy = 1;
+		constraints.fill = VERTICAL;
+		constraints.weighty = 1d;
+		add(new JPanel(), constraints);
+	}
+	
+	/**
+	 * Returns the portNameField.
+	 *
+	 * @return the portNameField
+	 */
+	public JTextField getPortNameField() {
+		return portNameField;
+	}
+
+	/**
+	 * Returns the port name.
+	 *
+	 * @return the port name
+	 */
+	public String getPortName() {
+		return portNameField.getText();
+	}
+	
+	/**
+	 * Sets the port name.
+	 *
+	 * @param name the name of the port
+	 */
+	public void setPortName(String name) {
+		portNameField.setText(name);
+		// Select the text
+		if (!name.isEmpty()) {
+			portNameField.setSelectionStart(0);
+			portNameField.setSelectionEnd(name.length());
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/ProcessorPanel.java
----------------------------------------------------------------------
diff --git a/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/ProcessorPanel.java b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/ProcessorPanel.java
new file mode 100644
index 0000000..4528727
--- /dev/null
+++ b/taverna-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/ProcessorPanel.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * 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.design.ui;
+
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.VERTICAL;
+import static java.awt.GridBagConstraints.WEST;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * UI for editing processors.
+ * 
+ * @author David Withers
+ */
+public class ProcessorPanel extends JPanel {
+	private static final long serialVersionUID = 260705376633425003L;
+
+	private JTextField processorNameField;
+
+	public ProcessorPanel() {
+		super(new GridBagLayout());
+
+		processorNameField = new JTextField();
+
+		setBorder(new EmptyBorder(10, 10, 10, 10));
+
+		GridBagConstraints constraints = new GridBagConstraints();
+
+		constraints.anchor = WEST;
+		constraints.gridx = 0;
+		constraints.gridy = 0;
+		constraints.ipadx = 10;
+		add(new JLabel("Name:"), constraints);
+
+		constraints.gridx = 1;
+		constraints.gridwidth = 2;
+		constraints.ipadx = 0;
+		constraints.weightx = 1d;
+		constraints.fill = HORIZONTAL;
+		add(processorNameField, constraints);
+
+		constraints.gridx = 0;
+		constraints.gridy = 1;
+		constraints.fill = VERTICAL;
+		constraints.weighty = 1d;
+		add(new JPanel(), constraints);
+	}
+
+	/**
+	 * Returns the processorNameField.
+	 * 
+	 * @return the processorNameField
+	 */
+	public JTextField getProcessorNameField() {
+		return processorNameField;
+	}
+
+	/**
+	 * Returns the processor name.
+	 * 
+	 * @return the processor name
+	 */
+	public String getProcessorName() {
+		return processorNameField.getText();
+	}
+
+	/**
+	 * Sets the processor name.
+	 * 
+	 * @param name
+	 *            the name of the processor
+	 */
+	public void setProcessorName(String name) {
+		processorNameField.setText(name);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-edits-api/pom.xml b/taverna-edits-api/pom.xml
new file mode 100644
index 0000000..3ca3a59
--- /dev/null
+++ b/taverna-edits-api/pom.xml
@@ -0,0 +1,47 @@
+<?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>taverna-edits-api</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Edits API</name>
+	<description>API for doing workflow edits and undo.</description>
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+			<version>${taverna.language.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-observer</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>${jackson.version}</version>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/CompoundEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/CompoundEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/CompoundEdit.java
new file mode 100644
index 0000000..58c7add
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/CompoundEdit.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * 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.edits;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import uk.org.taverna.scufl2.api.common.WorkflowBean;
+
+/**
+ * Implementation of Edit which contains an ordered list of child edits. Child
+ * edits are applied collectively and in order, any failure in any child edit
+ * causes an undo of previously applied children and a propogation of the edit
+ * exception.
+ * 
+ * @author Tom Oinn
+ */
+public class CompoundEdit implements Edit<WorkflowBean> {
+	private final transient List<Edit<?>> childEdits;
+	private transient boolean applied = false;
+
+	/**
+	 * Create a new compound edit with no existing Edit objects.
+	 * 
+	 */
+	public CompoundEdit() {
+		this.childEdits = new ArrayList<>();
+	}
+
+	/**
+	 * Create a new compound edit with the specified edits as children.
+	 */
+	public CompoundEdit(List<Edit<?>> edits) {
+		this.childEdits = edits;
+	}
+
+	/**
+	 * Get the list of edits.
+	 * @return a live-editable list.
+	 */
+	public List<Edit<?>> getChildEdits() {
+		return childEdits;
+	}
+
+	/**
+	 * Attempts to call the doEdit method of all child edits. If any of those
+	 * children throws an EditException any successful edits are rolled back and
+	 * the exception is rethrown as the cause of a new EditException from the
+	 * CompoundEdit
+	 */
+	@Override
+	public synchronized WorkflowBean doEdit() throws EditException {
+		if (isApplied())
+			throw new EditException("Cannot apply an edit more than once!");
+		List<Edit<?>> doneEdits = new ArrayList<>();
+		try {
+			for (Edit<?> edit : childEdits) {
+				edit.doEdit();
+				/*
+				 * Insert the done edit at position 0 in the list so we can
+				 * iterate over the list in the normal order if we need to
+				 * rollback, this ensures that the most recent edit is first.
+				 */
+				doneEdits.add(0, edit);
+			}
+			applied = true;
+		} catch (EditException ee) {
+			for (Edit<?> undoMe : doneEdits)
+				undoMe.undo();
+			applied = false;
+			throw new EditException("Failed child of compound edit", ee);
+		}
+		return null;
+	}
+
+	/**
+	 * There is no explicit subject for a compound edit, so this method always
+	 * returns null.
+	 */
+	@Override
+	public Object getSubject() {
+		return null;
+	}
+
+	/**
+	 * Rolls back all child edits in reverse order
+	 */
+	@Override
+	public synchronized void undo() {
+		for (int i = childEdits.size() - 1; i >= 0; i--)
+			// Undo child edits in reverse order
+			childEdits.get(i).undo();
+		applied = false;
+	}
+
+	@Override
+	public boolean isApplied() {
+		return applied;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/Edit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/Edit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/Edit.java
new file mode 100644
index 0000000..fad211e
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/Edit.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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.edits;
+
+import uk.org.taverna.scufl2.api.common.WorkflowBean;
+
+/**
+ * The workflow object model exposed by this API is read only. Properties of the
+ * model can only be changed through implementations of this interface, this
+ * ensures a consistant approach to grouped edits (transactions) and undo / redo
+ * support within the UI. It also potentially allows for capture of editing
+ * provenance where a workflow is repurposed or created from an aggregate of
+ * several others.
+ * 
+ * @author Tom Oinn
+ */
+public interface Edit<TargetType extends WorkflowBean> {
+	/**
+	 * Perform the edit
+	 * 
+	 * @throws EditException
+	 *             if the edit fails. If an edit throws EditException it should
+	 *             try to ensure the subject is unaltered. Where this is
+	 *             impossible consider breaking edits down into a compound edit.
+	 */
+	TargetType doEdit() throws EditException;
+
+	/**
+	 * Undo the edit, reverting the subject to the state it was in prior to the
+	 * edit
+	 */
+	void undo();
+
+	/**
+	 * Return the object to which this edit applies
+	 * 
+	 * @return
+	 */
+	Object getSubject();
+
+	/**
+	 * Has the edit been applied yet?
+	 * 
+	 * @return true if and only if the edit has been successfully applied to the
+	 *         subject
+	 */
+	boolean isApplied();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditException.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditException.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditException.java
new file mode 100644
index 0000000..3c61d84
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditException.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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.edits;
+
+/**
+ * Superclass of all exceptions thrown when altering the workflow model through
+ * the edit manager.
+ * 
+ * @author Tom Oinn
+ */
+@SuppressWarnings("serial")
+public class EditException extends Exception {
+	public EditException(String string) {
+		super(string);
+	}
+
+	public EditException(String string, Throwable cause) {
+		super(string, cause);
+	}
+
+	public EditException(Throwable t) {
+		super(t);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditManager.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditManager.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditManager.java
new file mode 100644
index 0000000..ce8b917
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditManager.java
@@ -0,0 +1,222 @@
+/*******************************************************************************
+ * 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.edits;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+
+/**
+ * Manager that can handle {@link Edit edits} for a {@link WorkflowBundle}.
+ * <p>
+ * Edits to a workflow that are to be undoable or redoable should be created by
+ * using {@link EditManager#getEdits()} to get an {@link Edits} object. Using
+ * this to create {@link Edit}s, instead of calling {@link Edit#doEdit()}, use
+ * {@link EditManager#doDataflowEdit(WorkflowBundle, Edit)} to associate the
+ * edit with the specified Dataflow.
+ * <p>
+ * It is possible to undo a series of edits done on a particular dataflow in
+ * this way by using {@link #undoDataflowEdit(WorkflowBundle)}. If one or more
+ * undoes have been performed, they can be redone step by step using
+ * {@link #redoDataflowEdit(WorkflowBundle)}. Note that it is no longer possible
+ * to call {@link #redoDataflowEdit(WorkflowBundle)} after a
+ * {@link #doDataflowEdit(WorkflowBundle, Edit)}.
+ * <p>
+ * The EditManager is {@link Observable}. If you
+ * {@linkplain Observable#addObserver(net.sf.taverna.t2.lang.observer.Observer)
+ * add an observer} you can be notified on {@linkplain DataflowEditEvent edits},
+ * {@linkplain DataFlowUndoEvent undoes} and {@linkplain DataFlowRedoEvent
+ * redoes}.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public interface EditManager extends Observable<EditManagerEvent> {
+	/**
+	 * <code>true</code> if {@link #redoDataflowEdit(WorkflowBundle)} on the
+	 * given dataflow would redo the last undone edit. If there are no previous
+	 * edits, return <code>false</code>.
+	 * 
+	 * @param dataflow
+	 *            {@link WorkflowBundle} which last affecting edit is to be
+	 *            undone
+	 * @return <code>true</code if and only if
+	 *         {@link #redoDataflowEdit(WorkflowBundle)} would undo
+	 */
+	boolean canRedoDataflowEdit(WorkflowBundle dataflow);
+
+	/**
+	 * <code>true</code> if {@link #undoDataflowEdit(WorkflowBundle)} on the
+	 * given dataflow would undo the last edit. If there are no previous edits,
+	 * return <code>false</code>.
+	 * 
+	 * @param dataflow
+	 *            {@link WorkflowBundle} which last affecting edit is to be
+	 *            undone
+	 * @return <code>true</code if {@link #undoDataflowEdit(WorkflowBundle)}
+	 *         would undo
+	 */
+	boolean canUndoDataflowEdit(WorkflowBundle dataflow);
+
+	/**
+	 * Do an {@link Edit} affecting the given {@link WorkflowBundle}.
+	 * <p>
+	 * The edit is {@link Edit#doEdit() performed} and the edit can later be
+	 * undone using {@link EditManager#undoDataflowEdit(WorkflowBundle)}.
+	 * <p>
+	 * Note that any events previously undone with
+	 * {@link EditManager#undoDataflowEdit(WorkflowBundle)} for the given
+	 * dataflow can no longer be
+	 * {@link EditManager#redoDataflowEdit(WorkflowBundle) redone} after calling
+	 * this method.
+	 * 
+	 * @see EditManager#undoDataflowEdit(WorkflowBundle)
+	 * @param dataflow
+	 *            {@link WorkflowBundle} this edit is affecting
+	 * @param edit
+	 *            {@link Edit} that should be done using {@link Edit#doEdit()}.
+	 * @throws EditException
+	 *             If {@link Edit#doEdit()} fails
+	 */
+	void doDataflowEdit(WorkflowBundle dataflow, Edit<?> edit)
+			throws EditException;
+
+	/**
+	 * Redo the last {@link Edit} that was undone using
+	 * {@link #undoDataflowEdit(WorkflowBundle)}.
+	 * <p>
+	 * Note that the {@link EditManager} might only be able to redo a reasonable
+	 * number of steps.
+	 * <p>
+	 * It is not possible to use {@link #redoDataflowEdit(WorkflowBundle)} after
+	 * a {@link #doDataflowEdit(WorkflowBundle, Edit)} affecting the same
+	 * {@link WorkflowBundle}, or if no edits have been undone yet. No action
+	 * would be taken in these cases.
+	 * 
+	 * @param dataflow
+	 *            {@link WorkflowBundle} which last affecting edit is to be
+	 *            redone
+	 * @throws EditException
+	 *             If {@link Edit#doEdit()} fails
+	 */
+	void redoDataflowEdit(WorkflowBundle dataflow) throws EditException;
+
+	/**
+	 * Undo the last {@link Edit} affecting the given {@link WorkflowBundle}.
+	 * <p>
+	 * This can be called in succession until there are no more known undoes.
+	 * Note that the {@link EditManager} might only be able to undo a reasonable
+	 * number of steps.
+	 * <p>
+	 * The last edit must have been performed using
+	 * {@link EditManager#doDataflowEdit(WorkflowBundle, Edit)} or
+	 * {@link EditManager#redoDataflowEdit(WorkflowBundle)}. The undo is done
+	 * using {@link Edit#undo()}. If no edits have been performed for the
+	 * dataflow yet, no action is taken.
+	 * <p>
+	 * Undoes can be redone using {@link #redoDataflowEdit(WorkflowBundle)}.
+	 * 
+	 * @param dataflow
+	 *            {@link WorkflowBundle} which last affecting edit is to be
+	 *            undone
+	 */
+	void undoDataflowEdit(WorkflowBundle dataflow);
+
+	/**
+	 * An event about an {@link Edit} on a {@link WorkflowBundle}, accessible
+	 * through {@link AbstractDataflowEditEvent#getEdit()} and
+	 * {@link AbstractDataflowEditEvent#getDataFlow()}.
+	 */
+	public static abstract class AbstractDataflowEditEvent implements
+			EditManagerEvent {
+		private final WorkflowBundle dataFlow;
+		private final Edit<?> edit;
+
+		public AbstractDataflowEditEvent(WorkflowBundle dataFlow, Edit<?> edit) {
+			if (dataFlow == null || edit == null)
+				throw new NullPointerException(
+						"Dataflow and/or Edit can't be null");
+			this.dataFlow = dataFlow;
+			this.edit = edit;
+		}
+
+		/**
+		 * The {@link WorkflowBundle} this event affected.
+		 * 
+		 * @return A {@link WorkflowBundle}
+		 */
+		public WorkflowBundle getDataFlow() {
+			return dataFlow;
+		}
+
+		/**
+		 * The {@link Edit} that was performed, undoed or redone on the
+		 * {@link #getDataFlow() dataflow}.
+		 * 
+		 * @return An {@link Edit}
+		 */
+		@Override
+		public Edit<?> getEdit() {
+			return edit;
+		}
+	}
+
+	/**
+	 * An event sent when an {@link Edit} has been performed on a
+	 * {@link WorkflowBundle}.
+	 * 
+	 */
+	public static class DataflowEditEvent extends AbstractDataflowEditEvent {
+		public DataflowEditEvent(WorkflowBundle dataFlow, Edit<?> edit) {
+			super(dataFlow, edit);
+		}
+	}
+
+	/**
+	 * An event sent when a previously undone {@link Edit} has been redone on a
+	 * {@link WorkflowBundle}.
+	 * 
+	 */
+	public static class DataFlowRedoEvent extends AbstractDataflowEditEvent {
+		public DataFlowRedoEvent(WorkflowBundle dataFlow, Edit<?> edit) {
+			super(dataFlow, edit);
+		}
+	}
+
+	/**
+	 * An event sent when an {@link Edit} has been undone on a
+	 * {@link WorkflowBundle}.
+	 * 
+	 */
+	public static class DataFlowUndoEvent extends AbstractDataflowEditEvent {
+		public DataFlowUndoEvent(WorkflowBundle dataFlow, Edit<?> edit) {
+			super(dataFlow, edit);
+		}
+	}
+
+	/**
+	 * An event given to {@link Observer}s registered with
+	 * {@link Observable#addObserver(Observer)}.
+	 */
+	public interface EditManagerEvent {
+		public Edit<?> getEdit();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/package-info.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/package-info.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/package-info.java
new file mode 100644
index 0000000..c1df7c9
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/package-info.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+/**
+ * A {@link net.sf.taverna.t2.workbench.edits.EditManager} that can manage
+ * {@link net.sf.taverna.t2.workflowmodel.Edit}s performed from the UI.
+ * <p>
+ * To perform an edit that is to be undoable, use
+ * {@link EditManager#doDataflowEdit(net.sf.taverna.t2.workflowmodel.Dataflow, net.sf.taverna.t2.workflowmodel.Edit)}
+ * instead of {@link net.sf.taverna.t2.workflowmodel.Edit#doEdit()}. Such edits
+ * can be
+ * {@link EditManager#undoDataflowEdit(net.sf.taverna.t2.workflowmodel.Dataflow) undone}
+ * and
+ * {@link EditManager#redoDataflowEdit(net.sf.taverna.t2.workflowmodel.Dataflow) redone}.
+ * </p>
+ * <p>
+ * Edits are organised by {@link net.sf.taverna.t2.workflowmodel.Dataflow} so
+ * that if a user changes the active workflow in the Workbench and does "Undo" -
+ * that would undo the last undo done related to that workflow.
+ * </p>
+ * <p>
+ * The {@link net.sf.taverna.t2.workbench.edits.impl} implementation of the
+ * EditManager is discovered by {@link net.sf.taverna.t2.workbench.edits.EditManager#getInstance()}. The
+ * implementation also includes {@link net.sf.taverna.t2.ui.menu.MenuComponent}s
+ * for Undo and Redo.
+ * </p>
+ * 
+ * @author Stian Soiland-Reyes
+ * 
+ */
+package net.sf.taverna.t2.workbench.edits;

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AbstractEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AbstractEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AbstractEdit.java
new file mode 100644
index 0000000..e6c8e68
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AbstractEdit.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (C) 2007-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.workflow.edits;
+
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.common.WorkflowBean;
+
+/**
+ * An abstract {@link Edit} implementation that checks if an edit has been
+ * applied or not.
+ *
+ * @author Stian Soiland-Reyes
+ *
+ * @param <Subject>
+ *            Subject of this edit
+ */
+public abstract class AbstractEdit<Subject extends WorkflowBean> implements
+		Edit<Subject> {
+	private boolean applied = false;
+	private final Subject subject;
+	protected final Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	/**
+	 * Construct an AbstractEdit.
+	 *
+	 * @param subjectType
+	 *            The expected implementation type of the subject. The edit will
+	 *            not go through unless the subject is an instance of this type.
+	 *            If the edit don't care about the implementation type, provide
+	 *            the official SubjectInterface instead.
+	 * @param subject
+	 *            The subject of this edit
+	 */
+	public AbstractEdit(Subject subject) {
+		if (subject == null && !isNullSubjectAllowed())
+			throw new IllegalArgumentException(
+					"Cannot construct an edit with null subject");
+		this.subject = subject;
+	}
+
+	protected boolean isNullSubjectAllowed() {
+		return false;
+	}
+
+	@Override
+	public final Subject doEdit() throws EditException {
+		if (applied)
+			throw new EditException("Edit has already been applied!");
+		try {
+			synchronized (subject) {
+				doEditAction(subject);
+				applied = true;
+				return this.subject;
+			}
+		} catch (EditException ee) {
+			applied = false;
+			throw ee;
+		}
+	}
+
+	/**
+	 * Do the actual edit here
+	 *
+	 * @param subject
+	 *            The instance to which the edit applies
+	 * @throws EditException
+	 */
+	protected abstract void doEditAction(Subject subject)
+			throws EditException;
+
+	/**
+	 * Undo any edit effects here
+	 *
+	 * @param subject
+	 *            The instance to which the edit applies
+	 */
+	protected abstract void undoEditAction(Subject subject);
+
+	@Override
+	public final Subject getSubject() {
+		return subject;
+	}
+
+	@Override
+	public final boolean isApplied() {
+		return applied;
+	}
+
+	@Override
+	public final void undo() {
+		if (!applied)
+			throw new RuntimeException(
+					"Attempt to undo edit that was never applied");
+		synchronized (subject) {
+			undoEditAction(subject);
+			applied = false;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityEdit.java
new file mode 100644
index 0000000..c6ffa7c
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityEdit.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+
+/**
+ * Creates a ProcessorBinding binding for the Activity and Processor and adds the binding to the
+ * Profile containing the Activity.
+ *
+ * @author David Withers
+ */
+public class AddActivityEdit extends AbstractEdit<Processor> {
+	private Activity activity;
+	private ProcessorBinding addedProcessorBinding;
+
+	public AddActivityEdit(Processor processor, Activity activity) {
+		super(processor);
+		this.activity = activity;
+	}
+
+	@Override
+	protected void doEditAction(Processor processor) {
+		ProcessorBinding binding = new ProcessorBinding();
+		binding.setBoundProcessor(processor);
+		binding.setBoundActivity(activity);
+		binding.setParent(activity.getParent());
+		addedProcessorBinding = binding;
+	}
+
+	@Override
+	protected void undoEditAction(Processor processor) {
+		addedProcessorBinding.setParent(null);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityInputPortMappingEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityInputPortMappingEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityInputPortMappingEdit.java
new file mode 100644
index 0000000..9a7e8b7
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityInputPortMappingEdit.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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.workflow.edits;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.port.InputActivityPort;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding;
+
+public class AddActivityInputPortMappingEdit extends AbstractEdit<Activity> {
+	private final InputProcessorPort inputProcessorPort;
+	private final InputActivityPort inputActivityPort;
+	private List<ProcessorInputPortBinding> portBindings;
+
+	public AddActivityInputPortMappingEdit(Activity activity,
+			InputProcessorPort inputProcessorPort,
+			InputActivityPort inputActivityPort) {
+		super(activity);
+		this.inputProcessorPort = inputProcessorPort;
+		this.inputActivityPort = inputActivityPort;
+	}
+
+	@Override
+	protected void doEditAction(Activity activity) {
+		portBindings = new ArrayList<>();
+		for (ProcessorBinding binding : scufl2Tools
+				.processorBindingsToActivity(activity))
+			portBindings.add(new ProcessorInputPortBinding(binding,
+					inputProcessorPort, inputActivityPort));
+	}
+
+	@Override
+	protected void undoEditAction(Activity activity) {
+		for (ProcessorInputPortBinding binding : portBindings)
+			binding.setParent(null);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityOutputPortMappingEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityOutputPortMappingEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityOutputPortMappingEdit.java
new file mode 100644
index 0000000..edafe8e
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityOutputPortMappingEdit.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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.workflow.edits;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.port.OutputActivityPort;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding;
+
+public class AddActivityOutputPortMappingEdit extends AbstractEdit<Activity> {
+	private final OutputProcessorPort outputProcessorPort;
+	private final OutputActivityPort outputActivityPort;
+	private List<ProcessorOutputPortBinding> portBindings;
+
+	public AddActivityOutputPortMappingEdit(Activity activity,
+			OutputProcessorPort outputProcessorPort,
+			OutputActivityPort outputActivityPort) {
+		super(activity);
+		this.outputProcessorPort = outputProcessorPort;
+		this.outputActivityPort = outputActivityPort;
+	}
+
+	@Override
+	protected void doEditAction(Activity activity) {
+		portBindings = new ArrayList<>();
+		for (ProcessorBinding binding : scufl2Tools
+				.processorBindingsToActivity(activity))
+			portBindings.add(new ProcessorOutputPortBinding(binding,
+					outputActivityPort, outputProcessorPort));
+	}
+
+	@Override
+	protected void undoEditAction(Activity activity) {
+		for (ProcessorOutputPortBinding binding : portBindings)
+			binding.setParent(null);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddChildEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddChildEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddChildEdit.java
new file mode 100644
index 0000000..abd8d9f
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddChildEdit.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.common.Child;
+import uk.org.taverna.scufl2.api.common.WorkflowBean;
+
+/**
+ * Adds a child to a parent.
+ *
+ * @author David Withers
+ */
+public class AddChildEdit<T extends WorkflowBean> extends AbstractEdit<T> {
+	private Child<T> child;
+
+	public AddChildEdit(T parent, Child<T> child) {
+		super(parent);
+		this.child = child;
+	}
+
+	@Override
+	protected void doEditAction(T parent) {
+		child.setParent(parent);
+	}
+
+	@Override
+	protected void undoEditAction(T parent) {
+		child.setParent(null);
+	}
+
+	public Child<T> getChild() {
+		return child;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddDataLinkEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddDataLinkEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddDataLinkEdit.java
new file mode 100644
index 0000000..10ff290
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddDataLinkEdit.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.workflow.edits;
+
+import java.util.List;
+
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.iterationstrategy.CrossProduct;
+import uk.org.taverna.scufl2.api.iterationstrategy.DotProduct;
+import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyParent;
+import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyTopNode;
+import uk.org.taverna.scufl2.api.iterationstrategy.PortNode;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.port.ReceiverPort;
+
+/**
+ * Adds a DataLink to a Workflow.
+ * <p>
+ * Handles setting the merge position of all dataLinks with the same receiver port.
+ * <p>
+ * Modifies the processor's iteration strategy or when the first DataLink is added.
+ *
+ * @author David Withers
+ */
+public class AddDataLinkEdit extends AbstractEdit<Workflow> {
+	private DataLink dataLink;
+	private PortNode portNode;
+
+	public AddDataLinkEdit(Workflow workflow, DataLink dataLink) {
+		super(workflow);
+		this.dataLink = dataLink;
+	}
+
+	@Override
+	protected void doEditAction(Workflow workflow) {
+		ReceiverPort sink = dataLink.getSendsTo();
+		List<DataLink> datalinksTo = scufl2Tools.datalinksTo(sink);
+		if (datalinksTo.size() > 0) {
+			if (datalinksTo.size() == 1)
+				datalinksTo.get(0).setMergePosition(0);
+			dataLink.setMergePosition(datalinksTo.size());
+		} else {
+			dataLink.setMergePosition(null);
+			if (sink instanceof InputProcessorPort) {
+				InputProcessorPort inputProcessorPort = (InputProcessorPort) sink;
+				for (IterationStrategyTopNode node : inputProcessorPort.getParent().getIterationStrategyStack()) {
+					portNode = new PortNode(node, inputProcessorPort);
+					portNode.setDesiredDepth(inputProcessorPort.getDepth());
+					break;
+				}
+			}
+		}
+		dataLink.setParent(workflow);
+	}
+
+	@Override
+	protected void undoEditAction(Workflow workflow) {
+		dataLink.setParent(null);
+		ReceiverPort sink = dataLink.getSendsTo();
+		List<DataLink> datalinksTo = scufl2Tools.datalinksTo(sink);
+		if (datalinksTo.size() == 1)
+			datalinksTo.get(0).setMergePosition(null);
+		else if (datalinksTo.isEmpty()&&portNode != null) {
+			IterationStrategyParent parent = portNode.getParent();
+			if (parent instanceof DotProduct)
+				((DotProduct) parent).remove(portNode);
+			else if (parent instanceof CrossProduct)
+				((CrossProduct) parent).remove(portNode);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyEdit.java
new file mode 100644
index 0000000..8677352
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyEdit.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (C) 2011 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyStack;
+import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyTopNode;
+
+/**
+ * Adds an IterationStrategyTopNode to an IterationStrategyStack.
+ *
+ * @author David Withers
+ */
+public class AddIterationStrategyEdit extends AbstractEdit<IterationStrategyStack> {
+	private final IterationStrategyTopNode iterationStrategyTopNode;
+
+	public AddIterationStrategyEdit(IterationStrategyStack iterationStrategyStack,
+			IterationStrategyTopNode iterationStrategyTopNode) {
+		super(iterationStrategyStack);
+		this.iterationStrategyTopNode = iterationStrategyTopNode;
+	}
+
+	@Override
+	public void doEditAction(IterationStrategyStack iterationStrategyStack) {
+		iterationStrategyStack.add(iterationStrategyTopNode);
+	}
+
+	@Override
+	public void undoEditAction(IterationStrategyStack iterationStrategyStack) {
+		iterationStrategyStack.remove(iterationStrategyTopNode);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyInputPortEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyInputPortEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyInputPortEdit.java
new file mode 100644
index 0000000..bb51fc6
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyInputPortEdit.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (C) 2011 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyStack;
+import uk.org.taverna.scufl2.api.iterationstrategy.PortNode;
+
+/**
+ * Adds an iteration strategy input port node to an iteration strategy.
+ * 
+ * @author David Withers
+ */
+public class AddIterationStrategyInputPortEdit extends
+		AbstractEdit<IterationStrategyStack> {
+	private final PortNode portNode;
+
+	public AddIterationStrategyInputPortEdit(
+			IterationStrategyStack iterationStrategy, PortNode portNode) {
+		super(iterationStrategy);
+		this.portNode = portNode;
+	}
+
+	@Override
+	public void doEditAction(IterationStrategyStack iterationStrategy) {
+		portNode.setParent(iterationStrategy.get(0));
+	}
+
+	@Override
+	public void undoEditAction(IterationStrategyStack iterationStrategy) {
+		portNode.setParent(null);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorEdit.java
new file mode 100644
index 0000000..04cf73b
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorEdit.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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * Adds a Processor to a Workflow.
+ *
+ * @author Stuart Owen
+ * @author David Withers
+ */
+public class AddProcessorEdit extends AddChildEdit<Workflow> {
+	private Processor processor;
+
+	public AddProcessorEdit(Workflow workflow, Processor processor) {
+		super(workflow, processor);
+		this.processor = processor;
+	}
+
+	@Override
+	protected void doEditAction(Workflow workflow) {
+		getSubject().getProcessors().addWithUniqueName(processor);
+		super.doEditAction(workflow);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorInputPortEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorInputPortEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorInputPortEdit.java
new file mode 100644
index 0000000..f722ddc
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorInputPortEdit.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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+
+/**
+ * Adds an input port to a processor.
+ *
+ * @author Tom Oinn
+ * @author David Withers
+ */
+public class AddProcessorInputPortEdit extends AddChildEdit<Processor> {
+	private final InputProcessorPort port;
+
+	public AddProcessorInputPortEdit(Processor processor, InputProcessorPort port) {
+		super(processor, port);
+		this.port = port;
+	}
+
+	@Override
+	protected void doEditAction(Processor processor) {
+		processor.getInputPorts().addWithUniqueName(port);
+		super.doEditAction(processor);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorOutputPortEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorOutputPortEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorOutputPortEdit.java
new file mode 100644
index 0000000..eec51b1
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorOutputPortEdit.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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+
+/**
+ * Adds an output port to a processor.
+ * 
+ * @author Tom Oinn
+ * @author David Withers
+ */
+public class AddProcessorOutputPortEdit extends AddChildEdit<Processor> {
+	private final OutputProcessorPort port;
+
+	public AddProcessorOutputPortEdit(Processor processor,
+			OutputProcessorPort port) {
+		super(processor, port);
+		this.port = port;
+	}
+
+	@Override
+	protected void doEditAction(Processor processor) {
+		processor.getOutputPorts().addWithUniqueName(port);
+		super.doEditAction(processor);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowInputPortEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowInputPortEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowInputPortEdit.java
new file mode 100644
index 0000000..91a9153
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowInputPortEdit.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (C) 2013 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.workflow.edits;
+
+import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW;
+
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputActivityPort;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Adds an input port to a workflow.
+ * 
+ * @author David Withers
+ */
+public class AddWorkflowInputPortEdit extends AbstractEdit<Workflow> {
+	private final InputWorkflowPort port;
+	private final CompoundEdit nestedPortEdit = new CompoundEdit();
+
+	public AddWorkflowInputPortEdit(Workflow workflow, InputWorkflowPort port) {
+		super(workflow);
+		this.port = port;
+		WorkflowBundle workflowBundle = workflow.getParent();
+		if (workflowBundle != null)
+			for (Profile profile : workflowBundle.getProfiles())
+				for (Activity activity : profile.getActivities())
+					if (activity.getType().equals(NESTED_WORKFLOW))
+						for (Configuration c : scufl2Tools.configurationsFor(
+								activity, profile))
+							defineEditsForOneConfiguration(workflow, port,
+									workflowBundle, activity, c);
+	}
+
+	private void defineEditsForOneConfiguration(Workflow workflow,
+			InputWorkflowPort port, WorkflowBundle workflowBundle,
+			Activity activity, Configuration c) {
+		List<Edit<?>> edits = nestedPortEdit.getChildEdits();
+		JsonNode nested = c.getJson().get("nestedWorkflow");
+		Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName(
+				nested.asText());
+
+		if (nestedWorkflow == workflow) {
+			InputActivityPort activityPort = new InputActivityPort();
+			activityPort.setName(port.getName());
+			activityPort.setDepth(port.getDepth());
+			edits.add(new AddChildEdit<>(activity, activityPort));
+
+			for (ProcessorBinding binding : scufl2Tools
+					.processorBindingsToActivity(activity)) {
+				Processor processor = binding.getBoundProcessor();
+				InputProcessorPort processorPort = new InputProcessorPort();
+				processorPort.setName(port.getName());
+				processorPort.setDepth(port.getDepth());
+				edits.add(new AddProcessorInputPortEdit(processor,
+						processorPort));
+
+				ProcessorInputPortBinding portBinding = new ProcessorInputPortBinding();
+				portBinding.setBoundProcessorPort(processorPort);
+				portBinding.setBoundActivityPort(activityPort);
+				edits.add(new AddChildEdit<>(binding, portBinding));
+			}
+		}
+	}
+
+	@Override
+	protected void doEditAction(Workflow workflow) throws EditException {
+		workflow.getInputPorts().addWithUniqueName(port);
+		port.setParent(workflow);
+		nestedPortEdit.doEdit();
+	}
+
+	@Override
+	protected void undoEditAction(Workflow workflow) {
+		port.setParent(null);
+		nestedPortEdit.undo();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowOutputPortEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowOutputPortEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowOutputPortEdit.java
new file mode 100644
index 0000000..7e505f1
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowOutputPortEdit.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (C) 2013 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.workflow.edits;
+
+import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW;
+
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.OutputActivityPort;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Adds an output port to a workflow.
+ *
+ * @author David Withers
+ */
+public class AddWorkflowOutputPortEdit extends AbstractEdit<Workflow> {
+	private final OutputWorkflowPort port;
+	private final CompoundEdit nestedPortEdit = new CompoundEdit();
+
+	public AddWorkflowOutputPortEdit(Workflow workflow, OutputWorkflowPort port) {
+		super(workflow);
+		this.port = port;
+		WorkflowBundle workflowBundle = workflow.getParent();
+		if (workflowBundle != null)
+			for (Profile profile : workflowBundle.getProfiles())
+				for (Activity activity : profile.getActivities())
+					if (activity.getType().equals(NESTED_WORKFLOW))
+						for (Configuration c : scufl2Tools.configurationsFor(
+								activity, profile))
+							defineEditsForOneConfiguration(workflow, port,
+									workflowBundle, activity, c);
+	}
+
+	private void defineEditsForOneConfiguration(Workflow workflow,
+			OutputWorkflowPort port, WorkflowBundle workflowBundle,
+			Activity activity, Configuration c) {
+		List<Edit<?>> edits = nestedPortEdit.getChildEdits();
+		JsonNode nested = c.getJson().get("nestedWorkflow");
+		Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName(
+				nested.asText());
+		if (nestedWorkflow == workflow) {
+			OutputActivityPort activityPort = new OutputActivityPort();
+			activityPort.setName(port.getName());
+			activityPort.setDepth(0);
+			activityPort.setGranularDepth(0);
+			edits.add(new AddChildEdit<>(activity, activityPort));
+
+			for (ProcessorBinding binding : scufl2Tools
+					.processorBindingsToActivity(activity)) {
+				Processor processor = binding.getBoundProcessor();
+				OutputProcessorPort processorPort = new OutputProcessorPort();
+				processorPort.setName(port.getName());
+				processorPort.setDepth(0);
+				processorPort.setGranularDepth(0);
+				edits.add(new AddProcessorOutputPortEdit(processor,
+						processorPort));
+
+				ProcessorOutputPortBinding portBinding = new ProcessorOutputPortBinding();
+				portBinding.setBoundProcessorPort(processorPort);
+				portBinding.setBoundActivityPort(activityPort);
+				edits.add(new AddChildEdit<>(binding, portBinding));
+			}
+		}
+	}
+
+	@Override
+	protected void doEditAction(Workflow workflow) throws EditException {
+		workflow.getOutputPorts().addWithUniqueName(port);
+		port.setParent(workflow);
+		nestedPortEdit.doEdit();
+	}
+
+	@Override
+	protected void undoEditAction(Workflow workflow) {
+		port.setParent(null);
+		nestedPortEdit.undo();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeDepthEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeDepthEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeDepthEdit.java
new file mode 100644
index 0000000..071b653
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeDepthEdit.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.workflow.edits;
+
+import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.ActivityPort;
+import uk.org.taverna.scufl2.api.port.DepthPort;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Changes the depth of a port.
+ *
+ * @author David Withers
+ */
+public class ChangeDepthEdit<T extends DepthPort> extends AbstractEdit<T> {
+	private Integer newDepth, oldDepth;
+
+	public ChangeDepthEdit(T depthPort, Integer newDepth) {
+		super(depthPort);
+		this.newDepth = newDepth;
+		oldDepth = depthPort.getDepth();
+	}
+
+	@Override
+	protected void doEditAction(T depthPort) {
+		depthPort.setDepth(newDepth);
+		if (depthPort instanceof InputWorkflowPort)
+			checkNestedPortDepths((InputWorkflowPort) depthPort, newDepth);
+	}
+
+	@Override
+	protected void undoEditAction(T depthPort) {
+		depthPort.setDepth(oldDepth);
+		if (depthPort instanceof InputWorkflowPort)
+			checkNestedPortDepths((InputWorkflowPort) depthPort, oldDepth);
+	}
+
+	private void checkNestedPortDepths(InputWorkflowPort workflowPort,
+			Integer depth) {
+		Workflow workflow = workflowPort.getParent();
+		if (workflow != null) {
+			WorkflowBundle workflowBundle = workflow.getParent();
+			if (workflowBundle != null)
+				for (Profile profile : workflowBundle.getProfiles())
+					for (Activity activity : profile.getActivities())
+						if (activity.getType().equals(NESTED_WORKFLOW))
+							for (Configuration c : scufl2Tools
+									.configurationsFor(activity, profile))
+								checkOneConfiguration(workflowPort, depth,
+										workflow, workflowBundle, activity, c);
+		}
+	}
+
+	private void checkOneConfiguration(InputWorkflowPort workflowPort,
+			Integer depth, Workflow workflow, WorkflowBundle workflowBundle,
+			Activity activity, Configuration c) {
+		JsonNode nested = c.getJson().get("nestedWorkflow");
+		Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName(
+				nested.asText());
+		if (nestedWorkflow != workflow)
+			return;
+
+		ActivityPort activityPort = activity.getInputPorts().getByName(
+				workflowPort.getName());
+		activityPort.setDepth(depth);
+		for (ProcessorBinding binding : scufl2Tools
+				.processorBindingsToActivity(activity))
+			for (ProcessorInputPortBinding portBinding : binding
+					.getInputPortBindings())
+				if (portBinding.getBoundActivityPort() == activityPort) {
+					InputProcessorPort processorPort = portBinding
+							.getBoundProcessorPort();
+					processorPort.setDepth(depth);
+				}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeGranularDepthEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeGranularDepthEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeGranularDepthEdit.java
new file mode 100644
index 0000000..e9f1b54
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeGranularDepthEdit.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.port.GranularDepthPort;
+
+/**
+ * Changes the granular depth of a port.
+ * 
+ * @author David Withers
+ */
+public class ChangeGranularDepthEdit<T extends GranularDepthPort> extends
+		AbstractEdit<T> {
+	private Integer newGranularDepth, oldGranularDepth;
+
+	public ChangeGranularDepthEdit(T granularDepthPort, Integer newGranularDepth) {
+		super(granularDepthPort);
+		this.newGranularDepth = newGranularDepth;
+		oldGranularDepth = granularDepthPort.getGranularDepth();
+	}
+
+	@Override
+	protected void doEditAction(T granularDepthPort) {
+		granularDepthPort.setGranularDepth(newGranularDepth);
+	}
+
+	@Override
+	protected void undoEditAction(T granularDepthPort) {
+		granularDepthPort.setGranularDepth(oldGranularDepth);
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualView.java b/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualView.java
new file mode 100644
index 0000000..61f6dd6
--- /dev/null
+++ b/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualView.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (C) 2007-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.ui.views.contextualviews.processor;
+
+import static java.awt.GridBagConstraints.CENTER;
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.LINE_START;
+import static java.awt.GridBagConstraints.NONE;
+import static net.sf.taverna.t2.workbench.ui.Utils.getParentFrame;
+
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.util.List;
+
+import javax.swing.Action;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+
+/**
+ * View of a processor, including it's iteration stack, activities, etc.
+ *
+ * @author Stian Soiland-Reyes
+ * @author Alan R Williams
+ */
+@SuppressWarnings("serial")
+public class ProcessorActivitiesContextualView extends ContextualView {
+	private static final String ABSTRACT_PROCESSOR_MSG = "<strong>Abstract processor</strong><br>"
+			+ "<i>No services. This will not execute.</i>";
+	private Scufl2Tools scufl2Tools = new Scufl2Tools();
+	protected JPanel mainPanel = new JPanel();
+	protected Processor processor;
+	private final ContextualViewFactoryRegistry contextualViewFactoryRegistry;
+	private final SelectionManager selectionManager;
+
+	public ProcessorActivitiesContextualView(Processor processor,
+			ContextualViewFactoryRegistry contextualViewFactoryRegistry,
+			SelectionManager selectionManager) {
+		super();
+		this.processor = processor;
+		this.contextualViewFactoryRegistry = contextualViewFactoryRegistry;
+		this.selectionManager = selectionManager;
+		initialise();
+		initView();
+	}
+
+	@Override
+	public void refreshView() {
+		initialise();
+		this.revalidate();
+	}
+
+	private synchronized void initialise() {
+		mainPanel.removeAll();
+		mainPanel.setLayout(new GridBagLayout());
+
+		GridBagConstraints constraints = new GridBagConstraints();
+		constraints.gridx = 0;
+		constraints.gridy = 0;
+		constraints.weightx = 0.1;
+		constraints.weighty = 0;
+
+		List<ProcessorBinding> processorBindings = scufl2Tools
+				.processorBindingsForProcessor(processor,
+						selectionManager.getSelectedProfile());
+		if (processorBindings.isEmpty()) {
+			JLabel noActivitiesLabel = new JLabel("<html>"
+					+ ABSTRACT_PROCESSOR_MSG + "</html>");
+			constraints.fill = NONE;
+			constraints.anchor = LINE_START;
+			mainPanel.add(noActivitiesLabel, constraints);
+		} else
+			for (ProcessorBinding processorBinding : processorBindings)
+				addViewForBinding(constraints, processorBinding);
+		mainPanel.revalidate();
+		mainPanel.repaint();
+		this.revalidate();
+		this.repaint();
+	}
+
+	private void addViewForBinding(GridBagConstraints constraints,
+			ProcessorBinding processorBinding) {
+		Activity activity = processorBinding.getBoundActivity();
+		List<ContextualViewFactory<? super Activity>> viewFactoryForBeanType = contextualViewFactoryRegistry
+				.getViewFactoriesForObject(activity);
+		if (viewFactoryForBeanType.isEmpty())
+			return;
+		// TODO why a list when we only use the first, twice, and assume non-empty too?
+		ContextualView view = (ContextualView) viewFactoryForBeanType.get(0)
+				.getViews(activity).get(0);
+
+		constraints.anchor = CENTER;
+		constraints.fill = HORIZONTAL;
+		mainPanel.add(view, constraints);
+		Frame frame = getParentFrame(this);
+		Action configureAction = view.getConfigureAction(frame);
+		if (configureAction != null) {
+			constraints.gridy++;
+			constraints.fill = NONE;
+			constraints.anchor = LINE_START;
+			JButton configureButton = new JButton(configureAction);
+			if (configureButton.getText() == null
+					|| configureButton.getText().isEmpty())
+				configureButton.setText("Configure");
+			mainPanel.add(configureButton, constraints);
+		}
+		constraints.gridy++;
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		return mainPanel;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Service";
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 100;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualViewFactory.java b/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualViewFactory.java
new file mode 100644
index 0000000..8e28d4a
--- /dev/null
+++ b/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualViewFactory.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (C) 2007-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.ui.views.contextualviews.processor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+
+/**
+ * SPI factory for creating a {@link ProcessorContextualView}.
+ * 
+ * @author Stian Soiland-Reyes
+ * @author Alan R Williams
+ */
+public class ProcessorActivitiesContextualViewFactory implements
+		ContextualViewFactory<Processor> {
+	private Scufl2Tools scufl2Tools = new Scufl2Tools();
+	private ContextualViewFactoryRegistry contextualViewFactoryRegistry;
+	private SelectionManager selectionManager;
+
+	@Override
+	public boolean canHandle(Object selection) {
+		return selection instanceof Processor;
+	}
+
+	@Override
+	public List<ContextualView> getViews(Processor selection) {
+		List<ContextualView> result = new ArrayList<>();
+		List<ProcessorBinding> processorBindings = scufl2Tools
+				.processorBindingsForProcessor(selection,
+						selectionManager.getSelectedProfile());
+		for (ProcessorBinding processorBinding : processorBindings) {
+			Activity activity = processorBinding.getBoundActivity();
+			for (ContextualViewFactory<? super Activity> cvf : contextualViewFactoryRegistry
+					.getViewFactoriesForObject(activity))
+				result.addAll(cvf.getViews(activity));
+		}
+		return result;
+	}
+
+	public void setContextualViewFactoryRegistry(
+			ContextualViewFactoryRegistry contextualViewFactoryRegistry) {
+		this.contextualViewFactoryRegistry = contextualViewFactoryRegistry;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorPredictedBehaviorContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorPredictedBehaviorContextualViewFactory.java b/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorPredictedBehaviorContextualViewFactory.java
new file mode 100644
index 0000000..0ef4f79
--- /dev/null
+++ b/taverna-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorPredictedBehaviorContextualViewFactory.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (C) 2007-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.ui.views.contextualviews.processor;
+
+import static java.util.Collections.singletonList;
+import static javax.swing.BoxLayout.Y_AXIS;
+
+import java.util.List;
+
+import javax.swing.BoxLayout;
+import javax.swing.JComponent;
+import javax.swing.JEditorPane;
+import javax.swing.JPanel;
+
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import uk.org.taverna.scufl2.api.common.NamedSet;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+
+/**
+ * How to get a panel describing what Taverna predicts the depth of the ports of
+ * a processor to be.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class ProcessorPredictedBehaviorContextualViewFactory implements
+		ContextualViewFactory<Processor> {
+	private Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	@Override
+	public boolean canHandle(Object selection) {
+		return selection instanceof Processor;
+	}
+
+	@Override
+	@SuppressWarnings("serial")
+	public List<ContextualView> getViews(final Processor selection) {
+		class ProcessorPredictedBehaviorContextualView extends ContextualView {
+			protected JPanel mainPanel = new JPanel();
+			protected Processor processor;
+
+			public ProcessorPredictedBehaviorContextualView() {
+				super();
+				refreshView();
+				initView();
+			}
+
+			@Override
+			public void refreshView() {
+				initialise();
+				this.revalidate();
+			}
+
+			private synchronized void initialise() {
+				mainPanel.removeAll();
+				mainPanel.setLayout(new BoxLayout(mainPanel, Y_AXIS));
+
+				StringBuilder html = new StringBuilder("<html><head>");
+				addStyle(html);
+				html.append("</head><body>");
+
+				NamedSet<InputProcessorPort> inputs = processor.getInputPorts();
+				if (!inputs.isEmpty()) {
+					html.append("<table border=1><tr><th>Input Port Name</th>")
+							.append("<th>Size of data</th>").append("</tr>");
+					for (InputProcessorPort ip : inputs) {
+						html.append("<tr><td>").append(ip.getName())
+								.append("</td><td>");
+						List<DataLink> incomingDataLinks = scufl2Tools
+								.datalinksTo(ip);
+						if (incomingDataLinks.isEmpty())
+							html.append("No value");
+						else {
+							int depth = getDepth(incomingDataLinks.get(0));
+							if (depth == -1)
+								html.append("Invalid");
+							else if (depth == 0)
+								html.append("Single value");
+							else
+								html.append("List of depth ").append(depth);
+						}
+						html.append("</td></tr>");
+					}
+					html.append("</table>");
+				}
+				NamedSet<OutputProcessorPort> outputs = processor
+						.getOutputPorts();
+				if (!outputs.isEmpty()) {
+					html.append("<table border=1><tr><th>Output Port Name</th>")
+							.append("<th>Size of data</th>").append("</tr>");
+					for (OutputProcessorPort op : outputs) {
+						html.append("<tr><td>").append(op.getName())
+								.append("</td><td>");
+						List<DataLink> outgoingDataLinks = scufl2Tools
+								.datalinksFrom(op);
+						if (outgoingDataLinks.isEmpty())
+							html.append("No value");
+						else {
+							int depth = getDepth(outgoingDataLinks.get(0));
+							if (depth == -1)
+								html.append("Invalid/unpredicted");
+							else if (depth == 0)
+								html.append("Single value");
+							else
+								html.append("List of depth ").append(depth);
+						}
+						html.append("</td></tr>");
+					}
+					html.append("</table>");
+				}
+				if (inputs.isEmpty() && outputs.isEmpty())
+					html.append("<p>No port behavior predicted</p>");
+				html.append("</body></html>");
+				JEditorPane editorPane = new JEditorPane("text/html",
+						html.toString());
+				editorPane.setEditable(false);
+				mainPanel.add(editorPane);
+
+				mainPanel.revalidate();
+				mainPanel.repaint();
+				this.revalidate();
+				this.repaint();
+			}
+
+			protected void addStyle(StringBuilder html) {
+				html.append("<style type='text/css'>")
+						.append("table {align:center; border:solid black 1px;")
+						.append("width:100%; height:100%; overflow:auto;}")
+						.append("</style>");
+			}
+
+			@Override
+			public JComponent getMainFrame() {
+				return mainPanel;
+			}
+
+			@Override
+			public String getViewTitle() {
+				return "Predicted behavior";
+			}
+
+			@Override
+			public int getPreferredPosition() {
+				return 300;
+			}
+		}
+
+		return singletonList((ContextualView) new ProcessorPredictedBehaviorContextualView());
+	}
+
+	private int getDepth(DataLink datalink) {
+		// TODO calculate actual depth
+		return -1;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
----------------------------------------------------------------------
diff --git a/taverna-contextual-views/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-contextual-views/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
new file mode 100644
index 0000000..3aa7ee0
--- /dev/null
+++ b/taverna-contextual-views/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1,4 @@
+#net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorContextualViewFactory
+net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorDispatchStackContextualViewFactory
+net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorPredictedBehaviorContextualViewFactory
+net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorActivitiesContextualViewFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views/src/main/resources/META-INF/spring/contextual-views-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-contextual-views/src/main/resources/META-INF/spring/contextual-views-context-osgi.xml b/taverna-contextual-views/src/main/resources/META-INF/spring/contextual-views-context-osgi.xml
new file mode 100644
index 0000000..932b541
--- /dev/null
+++ b/taverna-contextual-views/src/main/resources/META-INF/spring/contextual-views-context-osgi.xml
@@ -0,0 +1,15 @@
+<?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="ProcessorPredictedBehaviorContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+	<service ref="ProcessorActivitiesContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+
+	<reference id="contextualViewFactoryRegistry" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry" />
+	<reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views/src/main/resources/META-INF/spring/contextual-views-context.xml
----------------------------------------------------------------------
diff --git a/taverna-contextual-views/src/main/resources/META-INF/spring/contextual-views-context.xml b/taverna-contextual-views/src/main/resources/META-INF/spring/contextual-views-context.xml
new file mode 100644
index 0000000..7f53cb8
--- /dev/null
+++ b/taverna-contextual-views/src/main/resources/META-INF/spring/contextual-views-context.xml
@@ -0,0 +1,12 @@
+<?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="ProcessorPredictedBehaviorContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorPredictedBehaviorContextualViewFactory" />
+	<bean id="ProcessorActivitiesContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorActivitiesContextualViewFactory">
+			<property name="contextualViewFactoryRegistry" ref="contextualViewFactoryRegistry" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/pom.xml b/taverna-credential-manager-ui/pom.xml
new file mode 100644
index 0000000..ad9ff90
--- /dev/null
+++ b/taverna-credential-manager-ui/pom.xml
@@ -0,0 +1,69 @@
+<?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>taverna-credential-manager-ui</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Credential Manager UI</name>
+	<description>
+		Integrates the Credential Manager into the Workbench
+	</description>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-menu-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-helper-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+        <dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-credential-manager</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+     <dependency>
+        <groupId>${project.parent.groupId}</groupId>
+        <artifactId>taverna-ui</artifactId>
+        <version>${project.parent.version}</version>
+    </dependency>
+		<!-- <dependency>
+			<groupId>BrowserLauncher2</groupId>
+			<artifactId>BrowserLauncher2</artifactId>
+			<version>1.3</version>
+		</dependency> -->
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>${commons.io.version}</version>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CMStrings.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CMStrings.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CMStrings.java
new file mode 100644
index 0000000..3f6664c
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CMStrings.java
@@ -0,0 +1,7 @@
+package net.sf.taverna.t2.workbench.ui.credentialmanager;
+
+interface CMStrings {
+	String ALERT_TITLE = "Credential Manager Alert";
+	String ERROR_TITLE = "Credential Manager Error";
+	String WARN_TITLE = "Credential Manager Warning";
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ChangeMasterPasswordDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ChangeMasterPasswordDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ChangeMasterPasswordDialog.java
new file mode 100644
index 0000000..26086bc
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ChangeMasterPasswordDialog.java
@@ -0,0 +1,234 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.Font.PLAIN;
+import static javax.swing.BoxLayout.Y_AXIS;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.WARN_TITLE;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+/**
+ * Dialog used by users to change their master password for the Credential
+ * Manager.
+ */
+@SuppressWarnings("serial")
+public class ChangeMasterPasswordDialog extends NonBlockedHelpEnabledDialog {
+	/** Old password entry field */
+	private JPasswordField oldPasswordField;
+	/** New password entry field */
+	private JPasswordField newPasswordField;
+	/** New password confirmation entry field */
+	private JPasswordField newPasswordConfirmField;
+	/** The entered new password */
+	private String password = null;
+	/** Instructions to the users as to what to do in the dialog */
+	private String instructions;
+	private final CredentialManager credentialManager;
+
+	public ChangeMasterPasswordDialog(JFrame parent, String title,
+			boolean modal, String instructions,
+			CredentialManager credentialManager) {
+		super(parent, title, modal, null);
+		this.instructions = instructions;
+		this.credentialManager = credentialManager;
+		initComponents();
+	}
+
+	private void initComponents() {
+		getContentPane().setLayout(new BorderLayout());
+
+		JLabel instructionsLabel = new JLabel(instructions);
+		instructionsLabel.setFont(new Font(null, PLAIN, 11));
+
+		JPanel instructionsPanel = new JPanel();
+		instructionsPanel.setLayout(new BoxLayout(instructionsPanel, Y_AXIS));
+		instructionsPanel.add(instructionsLabel);
+		instructionsPanel.setBorder(new EmptyBorder(10, 5, 10, 0));
+
+		JLabel oldPasswordLabel = new JLabel("Old master password");
+		oldPasswordLabel.setBorder(new EmptyBorder(0, 5, 0, 0));
+
+		JLabel newPasswordLabel = new JLabel("New master password");
+		newPasswordLabel.setBorder(new EmptyBorder(0, 5, 0, 0));
+
+		JLabel newPasswordConfirmLabel = new JLabel(
+				"Confirm new master password");
+		newPasswordConfirmLabel.setBorder(new EmptyBorder(0, 5, 0, 0));
+
+		oldPasswordField = new JPasswordField(15);
+		newPasswordField = new JPasswordField(15);
+		newPasswordConfirmField = new JPasswordField(15);
+
+		JPanel jpPassword = new JPanel(new GridLayout(0, 2, 5, 5));
+		jpPassword.add(oldPasswordLabel);
+		jpPassword.add(oldPasswordField);
+		jpPassword.add(newPasswordLabel);
+		jpPassword.add(newPasswordField);
+		jpPassword.add(newPasswordConfirmLabel);
+		jpPassword.add(newPasswordConfirmField);
+
+		JPanel mainPanel = new JPanel(new BorderLayout());
+		mainPanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, 10),
+				new EtchedBorder()));
+		mainPanel.add(instructionsPanel, NORTH);
+		mainPanel.add(jpPassword, CENTER);
+
+		JButton okButton = new JButton("OK");
+		okButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				okPressed();
+			}
+		});
+
+		JButton cancelButton = new JButton("Cancel");
+		cancelButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				cancelPressed();
+			}
+		});
+		JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		buttonsPanel.add(okButton);
+		buttonsPanel.add(cancelButton);
+
+		getContentPane().add(mainPanel, CENTER);
+		getContentPane().add(buttonsPanel, SOUTH);
+
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeDialog();
+			}
+		});
+
+		setResizable(false);
+		getRootPane().setDefaultButton(okButton);
+		pack();
+	}
+
+	/**
+	 * Get the password set in the dialog or null if none was set.
+	 */
+	public String getPassword() {
+		return password;
+	}
+
+	/**
+	 * Check that the user has provided the correct old master password, that
+	 * the user has supplied the new password and confirmed it and that it is
+	 * not empty. If all is OK, stores the new password in the password field.
+	 * 
+	 */
+	private boolean checkPassword() {
+		String oldPassword = new String(oldPasswordField.getPassword());
+
+		if (oldPassword.length() == 0) {
+			// old password must not be empty
+			showMessageDialog(this,
+					"You must provide your current master password",
+					WARN_TITLE, WARNING_MESSAGE);
+			return false;
+		}
+
+		try {
+			if (!credentialManager.confirmMasterPassword(oldPassword)) {
+				showMessageDialog(this,
+						"You have provided an incorrect master password",
+						WARN_TITLE, WARNING_MESSAGE);
+				return false;
+			}
+		} catch (Exception e) {
+			showMessageDialog(
+					this,
+					"Credential Manager could not verify your current master password",
+					WARN_TITLE, WARNING_MESSAGE);
+			return false;
+		}
+
+		String newPassword = new String(newPasswordField.getPassword());
+		String newPasswordConfirm = new String(
+				newPasswordConfirmField.getPassword());
+
+		if (!newPassword.equals(newPasswordConfirm)) {
+			// passwords do not match
+			showMessageDialog(this, "Passwords do not match", WARN_TITLE,
+					WARNING_MESSAGE);
+			return false;
+		}
+
+		if (newPassword.isEmpty()) {
+			// passwords match but are empty
+			showMessageDialog(this, "The new master password cannot be empty",
+					WARN_TITLE, WARNING_MESSAGE);
+			return false;
+		}
+
+		// passwords match and not empty
+		password = newPassword;
+		return true;
+	}
+
+	private void okPressed() {
+		if (checkPassword())
+			closeDialog();
+	}
+
+	private void cancelPressed() {
+		/*
+		 * Set the password to null as it might have changed in the meantime if
+		 * user entered something then cancelled.
+		 */
+		password = null;
+		closeDialog();
+	}
+
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateDialog.java
new file mode 100644
index 0000000..6558562
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateDialog.java
@@ -0,0 +1,520 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.Color.WHITE;
+import static java.awt.Font.BOLD;
+import static java.awt.Font.PLAIN;
+import static java.awt.GridBagConstraints.LINE_START;
+import static javax.security.auth.x500.X500Principal.RFC2253;
+
+import java.awt.BorderLayout;
+import java.awt.Dialog;
+import java.awt.FlowLayout;
+import java.awt.Font;
+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.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.math.BigInteger;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+
+import net.sf.taverna.t2.lang.ui.DialogTextArea;
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser;
+import net.sf.taverna.t2.security.credentialmanager.ParsedDistinguishedName;
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Displays the details of a X.509 certificate and asks user if they want to
+ * trust it. This is normally invoked by the Taverna's TrustManager when trying 
+ * to confirm the trust in the remote server during SSL handshake.
+ */
+@SuppressWarnings("serial")
+public class ConfirmTrustedCertificateDialog extends NonBlockedHelpEnabledDialog {
+	private static Logger logger = Logger.getLogger(ConfirmTrustedCertificateDialog.class);
+
+	/** The certificate to display */
+	private X509Certificate cert;
+	/** User's decision as whether to trust this service's certificate or not */
+	private boolean shouldTrust;
+	/**
+	 * Should the decision also be saved in Credential Manager? Actually - it is
+	 * always saved now as it was really hard to implement trusting for one
+	 * connection only - so we can either "trust" or "not" trust but not
+	 * "trust once".
+	 */
+	private boolean shouldSave = false;
+	private final DistinguishedNameParser dnParser;
+
+	public ConfirmTrustedCertificateDialog(Frame parent, String title,
+			boolean modal, X509Certificate crt, DistinguishedNameParser dnParser) {
+		super(parent, title, modal);
+		this.cert = crt;
+		this.dnParser = dnParser;
+		initComponents();
+	}
+
+	public ConfirmTrustedCertificateDialog(Dialog parent, String title,
+			boolean modal, X509Certificate crt, DistinguishedNameParser dnParser)
+			throws CMException {
+		super(parent, title, modal);
+		this.cert = crt;
+		this.dnParser = dnParser;
+		initComponents();
+	}
+
+	private void initComponents(){		
+		// title panel
+		JPanel titlePanel = new JPanel(new BorderLayout());
+		titlePanel.setBackground(WHITE);
+		JLabel titleLabel = new JLabel("View service's certificate");
+		titleLabel.setFont(titleLabel.getFont().deriveFont(BOLD, 13.5f));
+		titleLabel.setBorder(new EmptyBorder(10, 10, 0, 10));
+
+		DialogTextArea titleMessage = new DialogTextArea();
+		titleMessage.setMargin(new Insets(5, 20, 10, 10));
+		titleMessage.setFont(titleMessage.getFont().deriveFont(11f));
+		titleMessage.setEditable(false);
+		titleMessage.setFocusable(false);
+		titlePanel.setBorder( new EmptyBorder(10, 10, 0, 10));
+		titlePanel.add(titleLabel, NORTH);
+		titlePanel.add(titleMessage, CENTER);
+		
+		// Certificate details:
+
+		ParsedDistinguishedName subjectDN = dnParser.parseDN(cert
+				.getSubjectX500Principal().getName(RFC2253));
+		ParsedDistinguishedName issuerDN = dnParser.parseDN(cert
+				.getIssuerX500Principal().getName(RFC2253));
+		JPanel certificatePanel = createCertificateDetailsPanel(subjectDN, issuerDN);
+		titleMessage.setText("The service host " + subjectDN.getCN() + " requires HTTPS connection and has identified itself with the certificate below.\n" +
+				"Do you want to trust this service? (Refusing to trust means you will not be able to invoke services on this host from a workflow.)");
+
+		// OK button
+		JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+
+//		final JButton trustButton = new JButton("Trust once");
+//		trustButton.addActionListener(new ActionListener() {
+//			public void actionPerformed(ActionEvent evt) {
+//				trustPressed();
+//			}
+//		});
+		
+		//final JButton trustAlwaysButton = new JButton("Trust always");
+		final JButton trustAlwaysButton = new JButton("Trust");
+		trustAlwaysButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				trustAlwaysPressed();
+			}
+		});
+		
+		final JButton dontTrustButton = new JButton("Do not trust");
+		dontTrustButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				dontTrustPressed();
+			}
+		});
+
+		//jpButtons.add(trustButton);
+		buttonsPanel.add(trustAlwaysButton);
+		buttonsPanel.add(dontTrustButton);
+
+		getContentPane().add(titlePanel, NORTH);
+		getContentPane().add(certificatePanel, CENTER);
+		getContentPane().add(buttonsPanel, SOUTH);
+
+		setResizable(false);
+
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeDialog();
+			}
+		});
+
+		getRootPane().setDefaultButton(trustAlwaysButton);
+		pack();
+	}
+
+	private JPanel createCertificateDetailsPanel(ParsedDistinguishedName subjectDN, ParsedDistinguishedName issuerDN) {
+		/*
+		 * Grid Bag Constraints templates for labels (column 1) and values
+		 * (column 2) of certificate details
+		 */
+		GridBagConstraints gbc_labels = new GridBagConstraints();
+		gbc_labels.gridx = 0;
+		gbc_labels.ipadx = 20;
+		gbc_labels.gridwidth = 1;
+		gbc_labels.gridheight = 1;
+		gbc_labels.insets = new Insets(2, 15, 2, 2);
+		gbc_labels.anchor = LINE_START;
+
+		GridBagConstraints gbc_values = new GridBagConstraints();
+		gbc_values.gridx = 1;
+		gbc_values.gridwidth = 1;
+		gbc_values.gridheight = 1;
+		gbc_values.insets = new Insets(2, 5, 2, 2);
+		gbc_values.anchor = LINE_START;
+
+		/*
+		 * Netscape Certificate Type non-critical extension (if any) defines the
+		 * intended uses of the certificate - to make it look like Firefox's
+		 * view certificate dialog
+		 * 
+		 * From openssl's documentation: "The [above] extension is non standard,
+		 * Netscape specific and largely obsolete. Their use in new applications
+		 * is discouraged."
+		 * 
+		 * TODO replace with "basicConstraints, keyUsage and extended key usage
+		 * extensions which are now used instead."
+		 */
+//		byte[] intendedUses = cert.getExtensionValue("2.16.840.1.113730.1.1"); // Netscape Certificate Type OID
+//		JLabel intendedUsesLabel = null;
+//		JTextField intendedUsesTextField = null;
+//		JPanel intendedUsesPanel = null;
+//		GridBagConstraints gbc_intendedUsesLabel = null;
+//		if (intendedUses != null) {
+//			intendedUsesLabel = new JLabel(
+//					"This certificate has been approved for the following uses:");
+//			intendedUsesLabel.setFont(new Font(null, Font.BOLD, 11));
+//			intendedUsesLabel.setBorder(new EmptyBorder(5, 5, 5, 5));
+//
+//			intendedUsesTextField = new JTextField(45);
+//			intendedUsesTextField.setText(CMUtils.getIntendedCertificateUses(intendedUses));
+//			intendedUsesTextField.setEditable(false);
+//			intendedUsesTextField.setFont(new Font(null, Font.PLAIN, 11));
+//
+//			intendedUsesPanel = new JPanel(new BorderLayout());
+//			intendedUsesPanel.add(intendedUsesLabel, BorderLayout.NORTH);
+//			intendedUsesPanel.add(intendedUsesTextField, BorderLayout.CENTER);
+//			JSeparator separator = new JSeparator(JSeparator.HORIZONTAL);
+//			intendedUsesPanel.add(separator, BorderLayout.SOUTH);
+//
+//			gbc_intendedUsesLabel = (GridBagConstraints) gbc_labels.clone();
+//			gbc_intendedUsesLabel.gridy = 0;
+//			gbc_intendedUsesLabel.gridwidth = 2; // takes two columns
+//			gbc_intendedUsesLabel.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets
+//		}
+
+		// Issued To
+		JLabel issuedToLabel = new JLabel("Issued To");
+		issuedToLabel.setFont(new Font(null, BOLD, 11));
+		GridBagConstraints gbc_issuedTo = (GridBagConstraints) gbc_labels
+				.clone();
+		gbc_issuedTo.gridy = 1;
+		gbc_issuedTo.gridwidth = 2; // takes two columns
+		gbc_issuedTo.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets
+		// Subject's Distinguished Name (DN)
+		// Extract the CN, O, OU and EMAILADDRESS fields
+		String subjectCN = subjectDN.getCN();
+		String subjectOrg = subjectDN.getO();
+		String subjectOU = subjectDN.getOU();
+		// String sEMAILADDRESS = CMUtils.getEmilAddress();
+		// Subject's Common Name (CN)
+		JLabel subjectCNLabel = new JLabel("Common Name (CN)");
+		subjectCNLabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_subjectCNLabel = (GridBagConstraints) gbc_labels.clone();
+		gbc_subjectCNLabel.gridy = 2;
+		JLabel subjectCNValue = new JLabel(subjectCN);
+		subjectCNValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_subjectCNValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_subjectCNValue.gridy = 2;
+		// Subject's Organisation (O)
+		JLabel subjectOrgLabel = new JLabel("Organisation (O)");
+		subjectOrgLabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_subjectOrgLabel = (GridBagConstraints) gbc_labels.clone();
+		gbc_subjectOrgLabel.gridy = 3;
+		JLabel subjectOrgValue = new JLabel(subjectOrg);
+		subjectOrgValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_subjectOrgValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_subjectOrgValue.gridy = 3;
+		// Subject's Organisation Unit (OU)
+		JLabel subjectOULabel = new JLabel("Organisation Unit (OU)");
+		subjectOULabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_subjectOULabel = (GridBagConstraints) gbc_labels.clone();
+		gbc_subjectOULabel.gridy = 4;
+		JLabel subjectOUValue = new JLabel(subjectOU);
+		subjectOUValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_subjectOUValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_subjectOUValue.gridy = 4;
+		// E-mail Address
+		// JLabel jlEmail = new JLabel("E-mail Address");
+		// jlEmail.setFont(new Font(null, Font.PLAIN, 11));
+		// GridBagConstraints gbc_jlEmail = (GridBagConstraints)
+		// gbcLabel.clone();
+		// gbc_jlEmail.gridy = 5;
+		// JLabel jlEmailValue = new JLabel(sEMAILADDRESS);
+		// jlEmailValue.setFont(new Font(null, Font.PLAIN, 11));
+		// GridBagConstraints gbc_jlEmailValue = (GridBagConstraints)
+		// gbcValue.clone();
+		// gbc_jlEmailValue.gridy = 5;
+		// Serial Number
+		JLabel snLabel = new JLabel("Serial Number");
+		snLabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_snLabel = (GridBagConstraints) gbc_labels.clone();
+		gbc_snLabel.gridy = 6;
+		JLabel snValue = new JLabel();
+		// Get the hexadecimal serial number
+		StringBuilder strBuff = new StringBuilder(new BigInteger(1, cert
+				.getSerialNumber().toByteArray()).toString(16).toUpperCase());
+		// Place colons at every two hexadecimal characters
+		if (strBuff.length() > 2)
+			for (int iCnt = 2; iCnt < strBuff.length(); iCnt += 3)
+				strBuff.insert(iCnt, ':');
+		snValue.setText(strBuff.toString());
+		snValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_snValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_snValue.gridy = 6;
+		// Certificate version number
+		JLabel versionLabel = new JLabel("Version");
+		versionLabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_versionLabel = (GridBagConstraints) gbc_labels
+				.clone();
+		gbc_versionLabel.gridy = 7;
+		JLabel versionValue = new JLabel(Integer.toString(cert.getVersion()));
+		versionValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_versionValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_versionValue.gridy = 7;
+
+		// Issued By
+		JLabel issuedByLabel = new JLabel("Issued By");
+		issuedByLabel.setFont(new Font(null, BOLD, 11));
+		GridBagConstraints gbc_issuedByLabel = (GridBagConstraints) gbc_labels
+				.clone();
+		gbc_issuedByLabel.gridy = 8;
+		gbc_issuedByLabel.gridwidth = 2; // takes two columns
+		gbc_issuedByLabel.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets
+		// Issuer's Distinguished Name (DN)
+		// Extract the CN, O and OU fields for the issuer
+		String issuerCN = issuerDN.getCN();
+		String issuerOrg = issuerDN.getO();
+		String issuerOU = issuerDN.getOU();
+		// Issuer's Common Name (CN)
+		JLabel issuerCNLabel = new JLabel("Common Name (CN)");
+		issuerCNLabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_issuerCNLabel = (GridBagConstraints) gbc_labels.clone();
+		gbc_issuerCNLabel.gridy = 9;
+		JLabel issuerCNValue = new JLabel(issuerCN);
+		issuerCNValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_issuerCNValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_issuerCNValue.gridy = 9;
+		// Issuer's Organisation (O)
+		JLabel issuerOrgLabel = new JLabel("Organisation (O)");
+		issuerOrgLabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_issuerOrgLabel = (GridBagConstraints) gbc_labels.clone();
+		gbc_issuerOrgLabel.gridy = 10;
+		JLabel issuerOrgValue = new JLabel(issuerOrg);
+		issuerOrgValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_issuerOrgValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_issuerOrgValue.gridy = 10;
+		// Issuer's Organisation Unit (OU)
+		JLabel issuerOULabel = new JLabel("Organisation Unit (OU)");
+		issuerOULabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_issuerOULabel = (GridBagConstraints) gbc_labels.clone();
+		gbc_issuerOULabel.gridy = 11;
+		JLabel issuerOUValue = new JLabel(issuerOU);
+		issuerOUValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_issuerOUValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_issuerOUValue.gridy = 11;
+		
+		// Validity
+		JLabel validityLabel = new JLabel("Validity");
+		validityLabel.setFont(new Font(null, BOLD, 11));
+		GridBagConstraints gbc_validityLabel = (GridBagConstraints) gbc_labels
+				.clone();
+		gbc_validityLabel.gridy = 12;
+		gbc_validityLabel.gridwidth = 2; // takes two columns
+		gbc_validityLabel.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets
+		// Issued On
+		JLabel issuedOnLabel = new JLabel("Issued On");
+		issuedOnLabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_issuedOnLabel = (GridBagConstraints) gbc_labels
+				.clone();
+		gbc_issuedOnLabel.gridy = 13;
+		JLabel issuedOnValue = new JLabel(cert.getNotBefore().toString());
+		issuedOnValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_issuedOnValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_issuedOnValue.gridy = 13;
+		// Expires On
+		JLabel expiresOnLabel = new JLabel("Expires On");
+		expiresOnLabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_expiresOnLabel = (GridBagConstraints) gbc_labels
+				.clone();
+		gbc_expiresOnLabel.gridy = 14;
+		JLabel expiresOnValue = new JLabel(cert.getNotAfter().toString());
+		expiresOnValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_expiresOnValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_expiresOnValue.gridy = 14;
+
+		// Fingerprints
+		byte[] binaryCertificateEncoding = new byte[0];
+		try {
+			// each certificate has one binary encoding; for X.509 certs it is DER
+			binaryCertificateEncoding = cert.getEncoded();
+		} catch (CertificateEncodingException ex) {
+			logger.error("Could not get the encoded form of the certificate.", ex);
+		}
+		JLabel fingerprintsLabel = new JLabel("Fingerprints");
+		fingerprintsLabel.setFont(new Font(null, BOLD, 11));
+		GridBagConstraints gbc_fingerprintsLabel = (GridBagConstraints) gbc_labels
+				.clone();
+		gbc_fingerprintsLabel.gridy = 15;
+		gbc_fingerprintsLabel.gridwidth = 2; // takes two columns
+		gbc_fingerprintsLabel.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets
+		// SHA-1 Fingerprint
+		JLabel sha1FingerprintLabel = new JLabel("SHA1 Fingerprint");
+		sha1FingerprintLabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_sha1FingerprintLabel = (GridBagConstraints) gbc_labels
+				.clone();
+		gbc_sha1FingerprintLabel.gridy = 16;
+		JLabel sha1FingerprintValue = new JLabel(
+				dnParser.getMessageDigestAsFormattedString(
+						binaryCertificateEncoding, "SHA1"));
+		sha1FingerprintValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_sha1FingerprintValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_sha1FingerprintValue.gridy = 16;
+		// MD5 Fingerprint
+		JLabel md5FingerprintLabel = new JLabel("MD5 Fingerprint");
+		md5FingerprintLabel.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_md5FingerprinLabel = (GridBagConstraints) gbc_labels
+				.clone();
+		gbc_md5FingerprinLabel.gridy = 17;
+		JLabel md5FingerprintValue = new JLabel(
+				dnParser.getMessageDigestAsFormattedString(
+						binaryCertificateEncoding, "MD5"));
+		md5FingerprintValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_md5FingerprintValue = (GridBagConstraints) gbc_values
+				.clone();
+		gbc_md5FingerprintValue.gridy = 17;
+
+		/*
+		 * Empty label to add a bit space at the bottom of the panel to make it
+		 * look like Firefox's view certificate dialog
+		 */
+		JLabel emptyLabel = new JLabel("");
+		GridBagConstraints gbc_emptyLabel = (GridBagConstraints) gbc_labels.clone();
+		gbc_emptyLabel.gridy = 18;
+		gbc_emptyLabel.gridwidth = 2; // takes two columns
+		gbc_emptyLabel.ipady = 40;
+
+		JPanel certificatePanel = new JPanel(new GridBagLayout());
+		certificatePanel.setBorder(new CompoundBorder(new EmptyBorder(15, 15, 15,
+				15), new EtchedBorder()));
+
+//		if (intendedUses != null)
+//			certificatePanel.add(intendedUsesPanel, gbc_intendedUsesLabel);
+		certificatePanel.add(issuedToLabel, gbc_issuedTo); // Issued To
+		certificatePanel.add(subjectCNLabel, gbc_subjectCNLabel);
+		certificatePanel.add(subjectCNValue, gbc_subjectCNValue);
+		certificatePanel.add(subjectOrgLabel, gbc_subjectOrgLabel);
+		certificatePanel.add(subjectOrgValue, gbc_subjectOrgValue);
+		certificatePanel.add(subjectOULabel, gbc_subjectOULabel);
+		certificatePanel.add(subjectOUValue, gbc_subjectOUValue);
+		// jpCertificate.add(jlEmail, gbc_jlEmail);
+		// jpCertificate.add(jlEmailValue, gbc_jlEmailValue);
+		certificatePanel.add(snLabel, gbc_snLabel);
+		certificatePanel.add(snValue, gbc_snValue);
+		certificatePanel.add(versionLabel, gbc_versionLabel);
+		certificatePanel.add(versionValue, gbc_versionValue);
+		certificatePanel.add(issuedByLabel, gbc_issuedByLabel); // Issued By
+		certificatePanel.add(issuerCNLabel, gbc_issuerCNLabel);
+		certificatePanel.add(issuerCNValue, gbc_issuerCNValue);
+		certificatePanel.add(issuerOrgLabel, gbc_issuerOrgLabel);
+		certificatePanel.add(issuerOrgValue, gbc_issuerOrgValue);
+		certificatePanel.add(issuerOULabel, gbc_issuerOULabel);
+		certificatePanel.add(issuerOUValue, gbc_issuerOUValue);
+		certificatePanel.add(validityLabel, gbc_validityLabel); // Validity
+		certificatePanel.add(issuedOnLabel, gbc_issuedOnLabel);
+		certificatePanel.add(issuedOnValue, gbc_issuedOnValue);
+		certificatePanel.add(expiresOnLabel, gbc_expiresOnLabel);
+		certificatePanel.add(expiresOnValue, gbc_expiresOnValue);
+		certificatePanel.add(fingerprintsLabel, gbc_fingerprintsLabel); // Fingerprints
+		certificatePanel.add(sha1FingerprintLabel, gbc_sha1FingerprintLabel);
+		certificatePanel.add(sha1FingerprintValue, gbc_sha1FingerprintValue);
+		certificatePanel.add(md5FingerprintLabel, gbc_md5FingerprinLabel);
+		certificatePanel.add(md5FingerprintValue, gbc_md5FingerprintValue);
+		// Empty label to get some vertical space on the frame
+		certificatePanel.add(emptyLabel, gbc_emptyLabel);
+		return certificatePanel;
+	}
+
+//	private void trustPressed() {
+//		shouldTrust = true;
+//		shouldSave = false;
+//		closeDialog();
+//	}
+
+	private void trustAlwaysPressed() {
+		shouldTrust = true;
+		shouldSave = true;
+		closeDialog();
+	}
+
+	private void dontTrustPressed() {
+		shouldTrust = false;
+		shouldSave = false;
+		closeDialog();
+	}
+
+	public void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+
+	public boolean shouldTrust() {
+		return shouldTrust;
+	}
+
+	public boolean shouldSave() {
+		return shouldSave;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateUI.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateUI.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateUI.java
new file mode 100644
index 0000000..0845543
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateUI.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+
+import java.awt.Frame;
+import java.security.cert.X509Certificate;
+
+import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser;
+import net.sf.taverna.t2.security.credentialmanager.TrustConfirmationProvider;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Stian Soiland-Reyes
+ */
+public class ConfirmTrustedCertificateUI implements TrustConfirmationProvider {
+	private static Logger logger = Logger
+			.getLogger(ConfirmTrustedCertificateUI.class);
+
+	private DistinguishedNameParser dnParser;
+
+	@Override
+	public Boolean shouldTrustCertificate(X509Certificate[] chain) {
+		boolean trustConfirm = false;
+		logger.info("Asking the user if they want to trust a certificate.");
+		// Ask user if they want to trust this service
+		ConfirmTrustedCertificateDialog confirmCertTrustDialog = new ConfirmTrustedCertificateDialog(
+				(Frame) null, "Untrusted HTTPS connection", true,
+				(X509Certificate) chain[0], dnParser);
+		confirmCertTrustDialog.setLocationRelativeTo(null);
+		confirmCertTrustDialog.setVisible(true);
+		trustConfirm = confirmCertTrustDialog.shouldTrust();
+//		trustConfirm.setShouldSave(confirmCertTrustDialog.shouldSave());
+		if (!confirmCertTrustDialog.shouldTrust())
+			showMessageDialog(
+					null,
+					"As you refused to trust this host, you will not be able to use its services from a workflow.",
+					"Untrusted HTTPS connection", INFORMATION_MESSAGE);
+
+		return trustConfirm;
+	}
+
+	/**
+	 * @param dnParser
+	 *            the dnParser to set
+	 */
+	public void setDistinguishedNameParser(DistinguishedNameParser dnParser) {
+		this.dnParser = dnParser;
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationPanel.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationPanel.java
new file mode 100644
index 0000000..397ad1b
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationPanel.java
@@ -0,0 +1,360 @@
+/*******************************************************************************
+ * Copyright (C) 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.views.graph.config;
+
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.NORTHWEST;
+import static java.awt.GridBagConstraints.RELATIVE;
+import static java.awt.GridBagConstraints.REMAINDER;
+import static java.awt.GridBagConstraints.WEST;
+import static javax.swing.SwingConstants.LEFT;
+import static net.sf.taverna.t2.workbench.helper.Helper.showHelp;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.allportIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.blobIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.horizontalIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.noportIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.verticalIcon;
+import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ALIGNMENT;
+import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ANIMATION_ENABLED;
+import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ANIMATION_SPEED;
+import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.PORT_STYLE;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.Hashtable;
+
+import javax.swing.AbstractAction;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JSlider;
+import javax.swing.JTextArea;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment;
+import net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle;
+
+/**
+ * UI for GraphViewConfiguration.
+ * 
+ * @author David Withers
+ */
+public class GraphViewConfigurationPanel extends JPanel {
+	private static final long serialVersionUID = 3779779432230124131L;
+	private static final int ANIMATION_SPEED_MIN = 100;
+	private static final int ANIMATION_SPEED_MAX = 3100;
+
+	private GraphViewConfiguration configuration;
+	private JRadioButton noPorts;
+	private JRadioButton allPorts;
+	private JRadioButton blobs;
+	private JRadioButton vertical;
+	private JRadioButton horizontal;
+	private JCheckBox animation;
+	private JLabel animationSpeedLabel;
+	private JSlider animationSpeedSlider;
+
+	public GraphViewConfigurationPanel(GraphViewConfiguration configuration) {
+		this.configuration = configuration;
+		GridBagLayout gridbag = new GridBagLayout();
+		GridBagConstraints c = new GridBagConstraints();
+		setLayout(gridbag);
+
+		// Title describing what kind of settings we are configuring here
+		JTextArea descriptionText = new JTextArea(
+				"Default settings for the workflow diagram");
+		descriptionText.setLineWrap(true);
+		descriptionText.setWrapStyleWord(true);
+		descriptionText.setEditable(false);
+		descriptionText.setFocusable(false);
+		descriptionText.setBorder(new EmptyBorder(10, 10, 10, 10));
+
+		JLabel defaultLayoutLabel = new JLabel("Service display");
+
+		noPorts = new JRadioButton();
+		allPorts = new JRadioButton();
+		blobs = new JRadioButton();
+
+		JLabel noPortsLabel = new JLabel("Name only", noportIcon, LEFT);
+		JLabel allPortsLabel = new JLabel("Name and ports", allportIcon, LEFT);
+		JLabel blobsLabel = new JLabel("No text", blobIcon, LEFT);
+
+		ButtonGroup buttonGroup = new ButtonGroup();
+		buttonGroup.add(noPorts);
+		buttonGroup.add(allPorts);
+		buttonGroup.add(blobs);
+
+		JLabel defaultAlignmentLabel = new JLabel("Diagram alignment");
+
+		vertical = new JRadioButton();
+		horizontal = new JRadioButton();
+
+		JLabel verticalLabel = new JLabel("Vertical", verticalIcon, LEFT);
+		JLabel horizontalLabel = new JLabel("Horizontal", horizontalIcon, LEFT);
+
+		ButtonGroup alignmentButtonGroup = new ButtonGroup();
+		alignmentButtonGroup.add(horizontal);
+		alignmentButtonGroup.add(vertical);
+
+		animation = new JCheckBox("Enable animation");
+
+		animationSpeedLabel = new JLabel("Animation speed");
+
+		animationSpeedSlider = new JSlider(ANIMATION_SPEED_MIN,
+				ANIMATION_SPEED_MAX);
+		animationSpeedSlider.setMajorTickSpacing(500);
+		animationSpeedSlider.setMinorTickSpacing(100);
+		animationSpeedSlider.setPaintTicks(true);
+		animationSpeedSlider.setPaintLabels(true);
+		animationSpeedSlider.setInverted(true);
+		animationSpeedSlider.setSnapToTicks(true);
+
+		Hashtable<Integer, JLabel> labelTable = new Hashtable<>();
+		labelTable.put(new Integer(ANIMATION_SPEED_MIN), new JLabel("Fast"));
+		labelTable.put(new Integer(
+				((ANIMATION_SPEED_MAX - ANIMATION_SPEED_MIN) / 2)
+						+ ANIMATION_SPEED_MIN), new JLabel("Medium"));
+		labelTable.put(new Integer(ANIMATION_SPEED_MAX), new JLabel("Slow"));
+		animationSpeedSlider.setLabelTable(labelTable);
+
+		animation.addItemListener(new ItemListener() {
+			@Override
+			public void itemStateChanged(ItemEvent e) {
+				boolean animationEnabled = animation.isSelected();
+				animationSpeedLabel.setEnabled(animationEnabled);
+				animationSpeedSlider.setEnabled(animationEnabled);
+			}
+		});
+
+		// Set current configuration values
+		setFields(configuration);
+
+		c.anchor = WEST;
+		c.gridx = 0;
+		c.gridwidth = REMAINDER;
+		c.weightx = 1d;
+		c.weighty = 0d;
+		c.fill = HORIZONTAL;
+
+		add(descriptionText, c);
+
+		c.insets = new Insets(10, 0, 10, 0);
+		add(defaultLayoutLabel, c);
+
+		c.insets = new Insets(0, 20, 0, 0);
+		c.gridwidth = 1;
+		c.weightx = 0d;
+		add(noPorts, c);
+		c.insets = new Insets(0, 5, 0, 0);
+		c.gridx = RELATIVE;
+		add(noPortsLabel, c);
+
+		c.insets = new Insets(0, 10, 0, 0);
+		add(allPorts, c);
+		c.insets = new Insets(0, 5, 0, 0);
+		add(allPortsLabel, c);
+
+		c.insets = new Insets(0, 10, 0, 0);
+		add(blobs, c);
+		c.insets = new Insets(0, 5, 0, 0);
+		c.gridwidth = REMAINDER;
+		c.weightx = 1d;
+		add(blobsLabel, c);
+
+		// alignment
+		c.insets = new Insets(20, 0, 10, 0);
+		c.gridx = 0;
+		add(defaultAlignmentLabel, c);
+
+		c.insets = new Insets(0, 20, 0, 0);
+		c.gridx = 0;
+		c.gridwidth = 1;
+		c.weightx = 0d;
+		add(vertical, c);
+		c.insets = new Insets(0, 5, 0, 0);
+		c.gridx = RELATIVE;
+		add(verticalLabel, c);
+
+		c.insets = new Insets(0, 10, 0, 0);
+		add(horizontal, c);
+		c.insets = new Insets(0, 5, 0, 0);
+		c.gridwidth = REMAINDER;
+		c.weightx = 1d;
+		add(horizontalLabel, c);
+
+		// animation
+		c.gridx = 0;
+		c.gridwidth = REMAINDER;
+		c.insets = new Insets(20, 0, 10, 0);
+		add(animation, c);
+
+		c.insets = new Insets(0, 20, 0, 0);
+		add(animationSpeedLabel, c);
+
+		c.insets = new Insets(0, 20, 10, 30);
+		c.anchor = NORTHWEST;
+		c.weighty = 0d;
+		add(animationSpeedSlider, c);
+
+		// Buttons
+		c.gridx = 0;
+		c.insets = new Insets(0, 20, 10, 30);
+		c.anchor = NORTHWEST;
+		c.weighty = 1d;
+		add(createButtonPanel(), c);
+	}
+
+	/**
+	 * Create the panel with the buttons.
+	 */
+	@SuppressWarnings("serial")
+	private JPanel createButtonPanel() {
+		final JPanel panel = new JPanel();
+
+		/**
+		 * The helpButton shows help about the current component
+		 */
+		JButton helpButton = new JButton(new AbstractAction("Help") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				showHelp(panel);
+			}
+		});
+		panel.add(helpButton);
+
+		/**
+		 * The resetButton changes the property values shown to those
+		 * corresponding to the configuration currently applied.
+		 */
+		JButton resetButton = new JButton(new AbstractAction("Reset") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				setFields(configuration);
+			}
+		});
+		panel.add(resetButton);
+
+		/**
+		 * The applyButton applies the shown field values to the
+		 * {@link HttpProxyConfiguration} and saves them for future.
+		 */
+		JButton applyButton = new JButton(new AbstractAction("Apply") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				applySettings();
+				setFields(configuration);
+			}
+		});
+		panel.add(applyButton);
+
+		return panel;
+	}
+
+	/**
+	 * Save the currently set field values to the {@link GraphViewConfiguration}
+	 * . Also apply those values to the currently running Taverna.
+	 */
+	private void applySettings() {
+		// Service display
+		if (noPorts.isSelected()) {
+			configuration.setProperty(PORT_STYLE, PortStyle.NONE.toString());
+		} else if (allPorts.isSelected()) {
+			configuration.setProperty(PORT_STYLE, PortStyle.ALL.toString());
+		} else if (blobs.isSelected()) {
+			configuration.setProperty(PORT_STYLE, PortStyle.BLOB.toString());
+		}
+
+		// Diagram alignment
+		if (vertical.isSelected()) {
+			configuration.setProperty(ALIGNMENT, Alignment.VERTICAL.toString());
+		} else if (horizontal.isSelected()) {
+			configuration.setProperty(ALIGNMENT,
+					Alignment.HORIZONTAL.toString());
+		}
+
+		// Animation and its speed
+		if (animation.isSelected()) {
+			configuration.setProperty(ANIMATION_ENABLED, String.valueOf(true));
+		} else {
+			configuration.setProperty(ANIMATION_ENABLED, String.valueOf(false));
+		}
+		int speed = animationSpeedSlider.getValue();
+		configuration.setProperty(ANIMATION_SPEED, String.valueOf(speed));
+	}
+
+	/**
+	 * Set the shown configuration field values to those currently in use (i.e.
+	 * last saved configuration).
+	 */
+	private void setFields(GraphViewConfiguration configurable) {
+		PortStyle portStyle = PortStyle.valueOf(configurable
+				.getProperty(PORT_STYLE));
+		if (portStyle.equals(PortStyle.NONE)) {
+			noPorts.setSelected(true);
+		} else if (portStyle.equals(PortStyle.ALL)) {
+			allPorts.setSelected(true);
+		} else {
+			blobs.setSelected(true);
+		}
+
+		Alignment alignment = Alignment.valueOf(configurable
+				.getProperty(ALIGNMENT));
+		if (alignment.equals(Alignment.VERTICAL)) {
+			vertical.setSelected(true);
+		} else {
+			horizontal.setSelected(true);
+		}
+
+		boolean animationEnabled = Boolean.parseBoolean(configurable
+				.getProperty(ANIMATION_ENABLED));
+		animation.setSelected(animationEnabled);
+
+		Integer animationSpeed = Integer.valueOf(configurable
+				.getProperty(ANIMATION_SPEED));
+		if (animationSpeed > ANIMATION_SPEED_MAX) {
+			animationSpeed = ANIMATION_SPEED_MAX;
+		} else if (animationSpeed < ANIMATION_SPEED_MIN) {
+			animationSpeed = ANIMATION_SPEED_MIN;
+		}
+		animationSpeedSlider.setValue(animationSpeed);
+		animationSpeedSlider.setEnabled(animationEnabled);
+
+		animationSpeedLabel.setEnabled(animationEnabled);
+	}
+
+	// for testing only
+	public static void main(String[] args) {
+		JDialog dialog = new JDialog();
+		dialog.add(new GraphViewConfigurationPanel(null));
+		dialog.setModal(true);
+		dialog.setSize(500, 400);
+		dialog.setVisible(true);
+		System.exit(0);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationUIFactory.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationUIFactory.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationUIFactory.java
new file mode 100644
index 0000000..959b598
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationUIFactory.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (C) 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.views.graph.config;
+
+import javax.swing.JPanel;
+
+import uk.org.taverna.configuration.Configurable;
+import uk.org.taverna.configuration.ConfigurationUIFactory;
+
+/**
+ * ConfigurationFactory for the GraphViewConfiguration.
+ * 
+ * @author David Withers
+ */
+public class GraphViewConfigurationUIFactory implements ConfigurationUIFactory {
+	private GraphViewConfiguration graphViewConfiguration;
+
+	@Override
+	public boolean canHandle(String uuid) {
+		return uuid.equals(getConfigurable().getUUID());
+	}
+
+	@Override
+	public JPanel getConfigurationPanel() {
+		return new GraphViewConfigurationPanel(graphViewConfiguration);
+	}
+
+	@Override
+	public Configurable getConfigurable() {
+		return graphViewConfiguration;
+	}
+
+	public void setGraphViewConfiguration(
+			GraphViewConfiguration graphViewConfiguration) {
+		this.graphViewConfiguration = graphViewConfiguration;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFInputMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFInputMenuAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFInputMenuAction.java
new file mode 100644
index 0000000..65448c3
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFInputMenuAction.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.InsertMenu.INSERT;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.views.graph.actions.AddWFInputAction;
+
+/**
+ * @author Alex Nenadic
+ */
+public class AddWFInputMenuAction extends AbstractMenuAction {
+	private static final URI ADD_WF_INPUT_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuAddWFInput");
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public AddWFInputMenuAction() {
+		super(INSERT, 10, ADD_WF_INPUT_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new AddWFInputAction(editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFOutputMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFOutputMenuAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFOutputMenuAction.java
new file mode 100644
index 0000000..522c841
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFOutputMenuAction.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.InsertMenu.INSERT;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.views.graph.actions.AddWFOutputAction;
+
+/**
+ * @author Alex Nenadic
+ */
+public class AddWFOutputMenuAction extends AbstractMenuAction {
+	private static final URI ADD_WF_OUTPUT_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuAddWFOutput");
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public AddWFOutputMenuAction() {
+		super(INSERT, 20, ADD_WF_OUTPUT_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new AddWFOutputAction(editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DeleteGraphComponentMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DeleteGraphComponentMenuAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DeleteGraphComponentMenuAction.java
new file mode 100644
index 0000000..654078f
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DeleteGraphComponentMenuAction.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.GraphDeleteMenuSection.GRAPH_DELETE_MENU_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.views.graph.actions.DeleteGraphComponentAction;
+
+/**
+ * @author Alex Nenadic
+ * @author Alan R Williams
+ */
+public class DeleteGraphComponentMenuAction extends AbstractMenuAction {
+	private static final URI DELETE_GRAPH_COMPONENT_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuDeleteGraphComponent");
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public DeleteGraphComponentMenuAction() {
+		super(GRAPH_DELETE_MENU_SECTION, 10, DELETE_GRAPH_COMPONENT_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new DeleteGraphComponentAction(editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramMenu.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramMenu.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramMenu.java
new file mode 100644
index 0000000..02c71d8
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramMenu.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static java.awt.event.KeyEvent.VK_V;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenu;
+
+public class DiagramMenu extends AbstractMenu {
+	public static final URI DIAGRAM = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#diagram");
+
+	public DiagramMenu() {
+		super(DEFAULT_MENU_BAR, 65, DIAGRAM, "View");
+	}
+
+	public static DummyAction makeAction() {
+		DummyAction action = new DummyAction("View");
+		action.putValue(MNEMONIC_KEY, VK_V);
+		return action;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramSaveMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramSaveMenuSection.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramSaveMenuSection.java
new file mode 100644
index 0000000..5ebb770
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramSaveMenuSection.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramMenu.DIAGRAM;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author Alex Nenadic
+ */
+public class DiagramSaveMenuSection extends AbstractMenuSection {
+	public static final URI DIAGRAM_SAVE_MENU_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#diagramSaveMenuSection");
+
+	public DiagramSaveMenuSection() {
+		super(DIAGRAM, 40, DIAGRAM_SAVE_MENU_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramZoomMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramZoomMenuSection.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramZoomMenuSection.java
new file mode 100644
index 0000000..639deee
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramZoomMenuSection.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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramMenu.DIAGRAM;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author Alex Nenadic
+ * @author Alan R Williams
+ */
+public class DiagramZoomMenuSection extends AbstractMenuSection {
+	public static final URI DIAGRAM_ZOOM_MENU_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#diagramZoomMenuSection");
+
+	public DiagramZoomMenuSection() {
+		super(DIAGRAM, 20, DIAGRAM_ZOOM_MENU_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphCopyMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphCopyMenuSection.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphCopyMenuSection.java
new file mode 100644
index 0000000..70cc462
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphCopyMenuSection.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection.GRAPH_MENU_SECTION;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * ???
+ */
+public class GraphCopyMenuSection extends AbstractMenuSection {
+	public static final URI GRAPH_COPY_MENU_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphCopyMenuSection");
+
+	public GraphCopyMenuSection() {
+		super(GRAPH_MENU_SECTION, 15, GRAPH_COPY_MENU_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDeleteMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDeleteMenuSection.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDeleteMenuSection.java
new file mode 100644
index 0000000..28d2144
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDeleteMenuSection.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection.GRAPH_MENU_SECTION;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author Alex Nenadic
+ */
+public class GraphDeleteMenuSection extends AbstractMenuSection {
+	public static final URI GRAPH_DELETE_MENU_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphDeleteMenuSection");
+
+	public GraphDeleteMenuSection() {
+		super(GRAPH_MENU_SECTION, 30, GRAPH_DELETE_MENU_SECTION);
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDetailsMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDetailsMenuSection.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDetailsMenuSection.java
new file mode 100644
index 0000000..f2b6af1
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDetailsMenuSection.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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection.GRAPH_MENU_SECTION;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author Alex Nenadic
+ * @author Alan R Williams
+ */
+public class GraphDetailsMenuSection extends AbstractMenuSection {
+	public static final URI GRAPH_DETAILS_MENU_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphDetailsMenuSection");
+
+	public GraphDetailsMenuSection() {
+		super(GRAPH_MENU_SECTION, 25, GRAPH_DETAILS_MENU_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphEditMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphEditMenuSection.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphEditMenuSection.java
new file mode 100644
index 0000000..1a487b1
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphEditMenuSection.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection.GRAPH_MENU_SECTION;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author Alex Nenadic
+ */
+public class GraphEditMenuSection extends AbstractMenuSection {
+	public static final URI GRAPH_EDIT_MENU_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphEditMenuSection");
+
+	public GraphEditMenuSection() {
+		super(GRAPH_MENU_SECTION, 20, GRAPH_EDIT_MENU_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphMenuSection.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphMenuSection.java
new file mode 100644
index 0000000..4030d34
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphMenuSection.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author Alex Nenadic
+ */
+public class GraphMenuSection extends AbstractMenuSection {
+	public static final URI GRAPH_MENU_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuSection");
+	public static final URI EDIT_MENU_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#edit");
+
+	public GraphMenuSection() {
+		super(EDIT_MENU_URI, 20, GRAPH_MENU_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/InsertMenu.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/InsertMenu.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/InsertMenu.java
new file mode 100644
index 0000000..9b498e5
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/InsertMenu.java
@@ -0,0 +1,30 @@
+/**
+ * 
+ */
+package net.sf.taverna.t2.workbench.views.graph.menu;
+
+import static java.awt.event.KeyEvent.VK_I;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenu;
+
+/**
+ * @author alanrw
+ */
+public class InsertMenu extends AbstractMenu {
+	public static final URI INSERT = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#insert");
+
+	public InsertMenu() {
+		super(DEFAULT_MENU_BAR, 64, INSERT, makeAction());
+	}
+
+	public static DummyAction makeAction() {
+		DummyAction action = new DummyAction("Insert");
+		action.putValue(MNEMONIC_KEY, VK_I);
+		return action;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/RenameWFInputOutputProcessorMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/RenameWFInputOutputProcessorMenuAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/RenameWFInputOutputProcessorMenuAction.java
new file mode 100644
index 0000000..3cf9f66
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/RenameWFInputOutputProcessorMenuAction.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.GraphDetailsMenuSection.GRAPH_DETAILS_MENU_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.views.graph.actions.RenameWFInputOutputProcessorAction;
+
+/**
+ * @author Alex Nenadic
+ */
+public class RenameWFInputOutputProcessorMenuAction extends AbstractMenuAction {
+	private static final URI RENAME_WF_INPUT_OUTPUT_PROCESSOR_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuRenameWFInputOutputProcessor");
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public RenameWFInputOutputProcessorMenuAction() {
+		super(GRAPH_DETAILS_MENU_SECTION, 30,
+				RENAME_WF_INPUT_OUTPUT_PROCESSOR_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new RenameWFInputOutputProcessorAction(editManager,
+				selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramAction.java
new file mode 100644
index 0000000..9fbd452
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramAction.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_0;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.refreshIcon;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.DesignOrResultsAction;
+
+@SuppressWarnings("serial")
+public class ResetDiagramAction extends AbstractAction implements
+		DesignOrResultsAction {
+	private static Action designAction = null;
+	@SuppressWarnings("unused")
+	private static Action resultsAction = null;
+
+	public static void setResultsAction(Action resultsAction) {
+		ResetDiagramAction.resultsAction = resultsAction;
+	}
+
+	public static void setDesignAction(Action designAction) {
+		ResetDiagramAction.designAction = designAction;
+	}
+
+	public ResetDiagramAction() {
+		super("Reset diagram", refreshIcon);
+		putValue(
+				ACCELERATOR_KEY,
+				getKeyStroke(VK_0, getDefaultToolkit().getMenuShortcutKeyMask()));
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+//		if (isWorkflowPerspective() && (designAction != null))
+			designAction.actionPerformed(e);
+//		else if (isResultsPerspective() && (resultsAction != null))
+//			resultsAction.actionPerformed(e);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramMenuAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramMenuAction.java
new file mode 100644
index 0000000..c4b402e
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramMenuAction.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramZoomMenuSection.DIAGRAM_ZOOM_MENU_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+/**
+ * An action that zooms a diagram image
+ * 
+ * @author Alex Nenadic
+ * @author Tom Oinn
+ * @author Alan R Williams
+ */
+public class ResetDiagramMenuAction extends AbstractMenuAction {
+	public static final URI RESET_DIAGRAM_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#diagramMenuResetDiagram");
+
+	public ResetDiagramMenuAction() {
+		super(DIAGRAM_ZOOM_MENU_SECTION, 5, RESET_DIAGRAM_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new ResetDiagramAction();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/SaveGraphImageSubMenu.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/SaveGraphImageSubMenu.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/SaveGraphImageSubMenu.java
new file mode 100644
index 0000000..49f948a
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/SaveGraphImageSubMenu.java
@@ -0,0 +1,315 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramSaveMenuSection.DIAGRAM_SAVE_MENU_SECTION;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URL;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+import javax.swing.ImageIcon;
+import javax.swing.JFileChooser;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+
+import net.sf.taverna.t2.lang.io.StreamCopier;
+import net.sf.taverna.t2.lang.io.StreamDevourer;
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.lang.ui.ExtensionFileFilter;
+import net.sf.taverna.t2.ui.menu.AbstractMenuCustom;
+import net.sf.taverna.t2.ui.menu.DesignOnlyAction;
+import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.models.graph.DotWriter;
+import net.sf.taverna.t2.workbench.models.graph.GraphController;
+import net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.PerspectiveSelectionEvent;
+import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
+import net.sf.taverna.t2.workbench.views.graph.GraphViewComponent;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * An action that saves graph diagram image.
+ *
+ * @author Alex Nenadic
+ * @author Tom Oinn
+ */
+public class SaveGraphImageSubMenu extends AbstractMenuCustom {
+	private static final Logger logger = Logger
+			.getLogger(SaveGraphImageSubMenu.class);
+	private static final String[] saveTypes = { "dot", "png", "svg", "ps",
+			"ps2" };
+	private static final String[] saveExtensions = { "dot", "png", "svg", "ps",
+			"ps" };
+	private static final String[] saveTypeNames = { "dot text", "PNG bitmap",
+			"scalable vector graphics", "postscript", "postscript for PDF" };	
+	public static final URI SAVE_GRAPH_IMAGE_MENU_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuSaveGraphImage");
+
+	private JMenu saveDiagramMenu;
+	private FileManager fileManager;
+	private SelectionManager selectionManager;
+	private WorkbenchConfiguration workbenchConfiguration;
+	private GraphViewComponent graphViewComponent;
+
+	public SaveGraphImageSubMenu() {
+		super(DIAGRAM_SAVE_MENU_SECTION, 70, SAVE_GRAPH_IMAGE_MENU_URI);
+	}
+
+	@Override
+	protected Component createCustomComponent() {
+		saveDiagramMenu = new JMenu("Export diagram");
+		saveDiagramMenu
+				.setToolTipText("Open this menu to export the diagram in various formats");
+		for (int i = 0; i < saveTypes.length; i++) {
+			String type = saveTypes[i];
+			String extension = saveExtensions[i];
+			ImageIcon icon = new ImageIcon(
+					WorkbenchIcons.class.getResource("graph/saveAs"
+							+ type.toUpperCase() + ".png"));
+			JMenuItem item = new JMenuItem(new DotInvoker("Export as "
+					+ saveTypeNames[i], icon, type, extension));
+			saveDiagramMenu.add(item);
+		}
+		return saveDiagramMenu;
+	}
+
+	@SuppressWarnings("serial")
+	class DotInvoker extends AbstractAction implements DesignOnlyAction {
+		String type = "dot";
+		String extension = "dot";
+
+		public DotInvoker(String name, ImageIcon icon, String type,
+				String extension) {
+			super(name, icon);
+			this.type = type;
+			this.extension = extension;
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			Workflow workflow = selectionManager.getSelectedWorkflow();
+			if (workflow == null) {
+				showMessageDialog(null, "Cannot export an empty diagram.",
+						"Warning", WARNING_MESSAGE);
+				return;
+			}
+
+			File file = saveDialogue(null, workflow, extension,
+					"Export workflow diagram");
+			if (file == null)
+				// User cancelled
+				return;
+
+			try {
+				GraphController graphController = graphViewComponent
+						.getGraphController(workflow);
+
+				if (type.equals("dot")) {
+					// Just write out the dot text, no processing required
+					PrintWriter out = new PrintWriter(new FileWriter(file));
+					DotWriter dotWriter = new DotWriter(out);
+					dotWriter.writeGraph(graphController.generateGraph());
+					out.flush();
+					out.close();
+				} else {
+					String dotLocation = (String) workbenchConfiguration
+							.getProperty("taverna.dotlocation");
+					if (dotLocation == null)
+						dotLocation = "dot";
+					logger.debug("GraphViewComponent: Invoking dot...");
+					Process dotProcess = Runtime.getRuntime().exec(
+							new String[] { dotLocation, "-T" + type });
+
+					FileOutputStream fos = new FileOutputStream(file);
+
+					StringWriter stringWriter = new StringWriter();
+					DotWriter dotWriter = new DotWriter(stringWriter);
+					dotWriter.writeGraph(graphController.generateGraph());
+
+					OutputStream dotOut = dotProcess.getOutputStream();
+					dotOut.write(SVGUtil.getDot(stringWriter.toString(),
+							workbenchConfiguration).getBytes());
+					dotOut.flush();
+					dotOut.close();
+					new StreamDevourer(dotProcess.getErrorStream()).start();
+					new StreamCopier(dotProcess.getInputStream(), fos).start();
+				}
+			} catch (Exception ex) {
+				logger.warn("GraphViewComponent: Could not export diagram to " + file, ex);
+				showMessageDialog(null,
+						"Problem saving diagram : \n" + ex.getMessage(),
+						"Error!", ERROR_MESSAGE);					
+			}
+		}
+	}
+
+	/**
+	 * Pop up a save dialogue relating to the given workflow. This method can be
+	 * used, for example, for saving the workflow diagram as .png, and will use
+	 * the existing workflow title as a base for suggesting a filename.
+	 *
+	 * @param parentComponent
+	 *            Parent component for dialogue window
+	 * @param model
+	 *            Workflow to save
+	 * @param extension
+	 *            Extension for filename, such as "jpg"
+	 * @param windowTitle
+	 *            Title for dialogue box, such as "Save workflow diagram"
+	 * @return File instance for the selected abstract filename, or null if the
+	 *         dialogue was cancelled.
+	 */
+	private File saveDialogue(Component parentComponent, Workflow workflow,
+			String extension, String windowTitle) {
+		JFileChooser fc = new JFileChooser();
+		Preferences prefs = Preferences
+				.userNodeForPackage(SaveGraphImageSubMenu.class);
+		String curDir = prefs
+				.get("currentDir", System.getProperty("user.home"));
+		String suggestedFileName = "";
+		// Get the source the workflow was loaded from - can be File, URL, or InputStream
+		Object source = fileManager.getDataflowSource(workflow.getParent());
+		if (source instanceof File) {
+			suggestedFileName = ((File) source).getName();
+			// remove the file extension
+			suggestedFileName = suggestedFileName.substring(0,
+					suggestedFileName.lastIndexOf("."));
+		} else if (source instanceof URL) {
+			suggestedFileName = ((URL) source).getPath();
+			// remove the file extension
+			suggestedFileName = suggestedFileName.substring(0,
+					suggestedFileName.lastIndexOf("."));
+		} else {
+			// We cannot suggest the file name if workflow was read from an InputStream
+		}
+
+		fc.setDialogTitle(windowTitle);
+		fc.resetChoosableFileFilters();
+		fc.setFileFilter(new ExtensionFileFilter(new String[] { extension }));
+		if (suggestedFileName.isEmpty())
+			// No file suggestion, just the directory
+			fc.setCurrentDirectory(new File(curDir));
+		else
+			// Suggest a filename from the workflow file name
+			fc.setSelectedFile(new File(curDir, suggestedFileName + "." + extension));
+
+		while (true) {
+			if (fc.showSaveDialog(parentComponent) != APPROVE_OPTION) {
+				logger.info("GraphViewComponent: Aborting diagram export to "
+						+ suggestedFileName);
+				return null;
+			}
+
+			File file = fixExtension(fc.getSelectedFile(), extension);
+			logger.debug("GraphViewComponent: Selected " + file + " as export target");
+			prefs.put("currentDir", fc.getCurrentDirectory().toString());
+
+			// If file doesn't exist, we may write it! (Well, probably...)
+			if (!file.exists())
+				return file;
+
+			// Ask the user if they want to overwrite the file
+			String msg = file.getAbsolutePath()
+					+ " already exists. Do you want to overwrite it?";
+			if (showConfirmDialog(null, msg, "File already exists",
+					YES_NO_OPTION) == JOptionPane.YES_OPTION)
+				return file;
+		}
+	}
+
+	/**
+	 * Make sure given File has the given extension. If it has no extension,
+	 * a new File instance will be returned. Otherwise, the passed instance is
+	 * returned unchanged.
+	 *
+	 * @param file
+	 *            File which extension is to be checked
+	 * @param extension
+	 *            Extension desired, example: "xml"
+	 * @return file parameter if the extension was OK, or a new File instance
+	 *         with the correct extension
+	 */
+	private File fixExtension(File file, String extension) {
+		if (file.getName().endsWith("." + extension))
+			return file;
+		// Append the extension (keep the existing one)
+		String name = file.getName();
+		return new File(file.getParent(), name + "." + extension);
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+	}
+
+	public void setWorkbenchConfiguration(
+			WorkbenchConfiguration workbenchConfiguration) {
+		this.workbenchConfiguration = workbenchConfiguration;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setGraphViewComponent(GraphViewComponent graphViewComponent) {
+		this.graphViewComponent = graphViewComponent;
+	}
+
+	private static final String DESIGN_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.design.DesignPerspective";
+
+	@SuppressWarnings("unused")
+	private final class SelectionManagerObserver extends
+			SwingAwareObserver<SelectionManagerEvent> {
+		@Override
+		public void notifySwing(Observable<SelectionManagerEvent> sender,
+				SelectionManagerEvent message) {
+			if (!(message instanceof PerspectiveSelectionEvent))
+				return;
+			PerspectiveSelectionEvent event = (PerspectiveSelectionEvent) message;
+
+			saveDiagramMenu.setEnabled((DESIGN_PERSPECTIVE_ID.equals(event
+					.getSelectedPerspective().getID())));
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInAction.java
new file mode 100644
index 0000000..b8735c9
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInAction.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_EQUALS;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomInIcon;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.DesignOrResultsAction;
+
+import org.apache.log4j.Logger;
+
+@SuppressWarnings("serial")
+public class ZoomInAction extends AbstractAction implements
+		DesignOrResultsAction {
+	@SuppressWarnings("unused")
+	private static Logger logger = Logger.getLogger(ZoomInAction.class);
+	private static Action designAction = null;
+	@SuppressWarnings("unused")
+	private static Action resultsAction = null;
+
+	public static void setResultsAction(Action resultsAction) {
+		ZoomInAction.resultsAction = resultsAction;
+	}
+
+	public static void setDesignAction(Action designAction) {
+		ZoomInAction.designAction = designAction;
+	}
+
+	ZoomInAction() {
+		super("Zoom in", zoomInIcon);
+		putValue(
+				ACCELERATOR_KEY,
+				getKeyStroke(VK_EQUALS, getDefaultToolkit()
+						.getMenuShortcutKeyMask()));
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+//		if (isWorkflowPerspective()) {
+//			if (designAction != null)
+				designAction.actionPerformed(e);
+//			else
+//				logger.error("ZoomInAction.designAction is null");
+//		} else if (isResultsPerspective() && (resultsAction != null))
+//			resultsAction.actionPerformed(e);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInMenuAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInMenuAction.java
new file mode 100644
index 0000000..89eea7d
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInMenuAction.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramZoomMenuSection.DIAGRAM_ZOOM_MENU_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+/**
+ * An action that zooms a diagram image
+ * 
+ * @author Alex Nenadic
+ * @author Tom Oinn
+ * @author Alan R Williams
+ */
+public class ZoomInMenuAction extends AbstractMenuAction {
+	public static final URI ZOOM_IN_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#diagramMenuZoomIn");
+
+	public ZoomInMenuAction() {
+		super(DIAGRAM_ZOOM_MENU_SECTION, 10, ZOOM_IN_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new ZoomInAction();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutAction.java
new file mode 100644
index 0000000..bd2a2b9
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutAction.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_MINUS;
+import static javax.swing.KeyStroke.getKeyStroke;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.DesignOrResultsAction;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+@SuppressWarnings("serial")
+public class ZoomOutAction extends AbstractAction implements
+		DesignOrResultsAction {
+	private static Action designAction = null;
+	@SuppressWarnings("unused")
+	private static Action resultsAction = null;
+
+	public static void setResultsAction(Action resultsAction) {
+		ZoomOutAction.resultsAction = resultsAction;
+	}
+
+	public static void setDesignAction(Action designAction) {
+		ZoomOutAction.designAction = designAction;
+	}
+
+	ZoomOutAction() {
+		super("Zoom out", WorkbenchIcons.zoomOutIcon);
+		putValue(
+				ACCELERATOR_KEY,
+				getKeyStroke(VK_MINUS, getDefaultToolkit()
+						.getMenuShortcutKeyMask()));
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+//		if (isWorkflowPerspective() && (designAction != null))
+			designAction.actionPerformed(e);
+//		else if (isResultsPerspective() && (resultsAction != null))
+//			resultsAction.actionPerformed(e);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutMenuAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutMenuAction.java
new file mode 100644
index 0000000..bc34252
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutMenuAction.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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.views.graph.menu;
+
+import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramZoomMenuSection.DIAGRAM_ZOOM_MENU_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+/**
+ * An action that zooms a diagram image
+ * 
+ * @author Alex Nenadic
+ * @author Tom Oinn
+ * @author Alan R Williams
+ */
+public class ZoomOutMenuAction extends AbstractMenuAction {
+	public static final URI ZOOM_OUT_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#diagramMenuZoomOut");
+
+	public ZoomOutMenuAction() {
+		super(DIAGRAM_ZOOM_MENU_SECTION, 20, ZOOM_OUT_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new ZoomOutAction();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFInputToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFInputToolbarAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFInputToolbarAction.java
new file mode 100644
index 0000000..736ba8d
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFInputToolbarAction.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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.views.graph.toolbar;
+
+import static net.sf.taverna.t2.workbench.views.graph.toolbar.GraphEditToolbarSection.GRAPH_EDIT_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.views.graph.actions.AddWFInputAction;
+
+/**
+ * @author Alex Nenadic
+ */
+public class AddWFInputToolbarAction extends AbstractMenuAction {
+	private static final URI ADD_WF_INPUT_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphToolbarAddWFInput");
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public AddWFInputToolbarAction() {
+		super(GRAPH_EDIT_TOOLBAR_SECTION, 10, ADD_WF_INPUT_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new AddWFInputAction(editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFOutputToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFOutputToolbarAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFOutputToolbarAction.java
new file mode 100644
index 0000000..ae7d5d0
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFOutputToolbarAction.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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.views.graph.toolbar;
+
+import static net.sf.taverna.t2.workbench.views.graph.toolbar.GraphEditToolbarSection.GRAPH_EDIT_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.views.graph.actions.AddWFOutputAction;
+
+/**
+ * @author Alex Nenadic
+ */
+public class AddWFOutputToolbarAction extends AbstractMenuAction {
+	private static final URI ADD_WF_OUTPUT_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphToolbarAddWFOutput");
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public AddWFOutputToolbarAction() {
+		super(GRAPH_EDIT_TOOLBAR_SECTION, 20, ADD_WF_OUTPUT_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new AddWFOutputAction(editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/DeleteGraphComponentToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/DeleteGraphComponentToolbarAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/DeleteGraphComponentToolbarAction.java
new file mode 100644
index 0000000..068c530
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/DeleteGraphComponentToolbarAction.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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.views.graph.toolbar;
+
+import static net.sf.taverna.t2.workbench.views.graph.toolbar.GraphDeleteToolbarSection.GRAPH_DELETE_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.views.graph.actions.DeleteGraphComponentAction;
+
+/**
+ * @author Alex Nenadic
+ */
+public class DeleteGraphComponentToolbarAction extends AbstractMenuAction {
+	private static final URI DELETE_GRAPH_COMPONENT_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphToolbarDeleteGraphComponent");
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public DeleteGraphComponentToolbarAction() {
+		super(GRAPH_DELETE_TOOLBAR_SECTION, 10, DELETE_GRAPH_COMPONENT_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new DeleteGraphComponentAction(editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphDeleteToolbarSection.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphDeleteToolbarSection.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphDeleteToolbarSection.java
new file mode 100644
index 0000000..794cf1f
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphDeleteToolbarSection.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.views.graph.toolbar;
+
+import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author Alex Nenadic
+ */
+public class GraphDeleteToolbarSection extends AbstractMenuSection {
+	public static final URI GRAPH_DELETE_TOOLBAR_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphDeleteToolbarSection");
+
+	public GraphDeleteToolbarSection() {
+		super(DEFAULT_TOOL_BAR, 80, GRAPH_DELETE_TOOLBAR_SECTION);
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualView.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualView.java
new file mode 100644
index 0000000..3f17a65
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualView.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.dataflowinputport;
+
+import static java.awt.FlowLayout.LEFT;
+
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+
+/**
+ * Contextual view for dataflow's input ports.
+ *
+ * @author Alex Nenadic
+ */
+class DataflowInputPortContextualView extends ContextualView{
+	private static final long serialVersionUID = -8746856072335775933L;
+
+	private InputWorkflowPort dataflowInputPort;
+	private JPanel dataflowInputPortView;
+	@SuppressWarnings("unused")
+	private FileManager fileManager;
+
+	public DataflowInputPortContextualView(InputWorkflowPort inputport,
+			FileManager fileManager) {
+		this.dataflowInputPort = inputport;
+		this.fileManager = fileManager;
+		initView();
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		refreshView();
+		return dataflowInputPortView;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Workflow input port: " + dataflowInputPort.getName();
+	}
+
+	@Override
+	public void refreshView() {
+		dataflowInputPortView = new JPanel(new FlowLayout(LEFT));
+		dataflowInputPortView.setBorder(new EmptyBorder(5, 5, 5, 5));
+		JLabel label = new JLabel(getTextFromDepth("port",
+				dataflowInputPort.getDepth()));
+		dataflowInputPortView.add(label);
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	public Action getConfigureAction(Frame owner) {
+		return new AbstractAction("Update prediction") {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				// fileManager.getCurrentDataflow().checkValidity();
+				refreshView();
+			}
+		};
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 100;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualViewFactory.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualViewFactory.java
new file mode 100644
index 0000000..5dc5434
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualViewFactory.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.dataflowinputport;
+
+import java.util.Arrays;
+import java.util.List;
+
+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;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+
+/**
+ * A factory of contextual views for dataflow's input ports.
+ *
+ * @author Alex Nenadic
+ */
+public class DataflowInputPortContextualViewFactory implements
+		ContextualViewFactory<InputWorkflowPort> {
+	private FileManager fileManager;
+
+	@Override
+	public boolean canHandle(Object object) {
+		return object instanceof InputWorkflowPort;
+	}
+
+	@Override
+	public List<ContextualView> getViews(InputWorkflowPort inputport) {
+		return Arrays.asList(new ContextualView[] {
+				new DataflowInputPortContextualView(inputport, fileManager)});
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualView.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualView.java
new file mode 100644
index 0000000..9ba55fe
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualView.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.dataflowoutputport;
+
+import static java.awt.FlowLayout.LEFT;
+
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+
+/**
+ * Contextual view for dataflow's output ports.
+ *
+ * @author Alex Nenadic
+ */
+public class DataflowOutputPortContextualView extends ContextualView {
+	private static final long serialVersionUID = 5496014085110553051L;
+
+	private OutputWorkflowPort dataflowOutputPort;
+	private JPanel dataflowOutputPortView;
+	@SuppressWarnings("unused")
+	private FileManager fileManager;
+
+	public DataflowOutputPortContextualView(OutputWorkflowPort outputport,
+			FileManager fileManager) {
+		this.dataflowOutputPort = outputport;
+		this.fileManager = fileManager;
+		initView();
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		refreshView();
+		return dataflowOutputPortView;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Workflow output port: " + dataflowOutputPort.getName();
+	}
+
+	@Override
+	public void refreshView() {
+		dataflowOutputPortView = new JPanel(new FlowLayout(LEFT));
+		dataflowOutputPortView.setBorder(new EmptyBorder(5,5,5,5));
+		JLabel label = new JLabel(getTextForLabel());
+		dataflowOutputPortView.add(label);
+	}
+
+	private String getTextForLabel() {
+		//FIXME
+		//return getTextFromDepth("port", dataflowOutputPort.getDepth());
+		return "Fix depth for OutputWorkflowPort";
+	}
+
+	private void updatePrediction() {
+		//FIXME
+		// fileManager.getCurrentDataflow().checkValidity();
+	}
+
+	@Override
+	@SuppressWarnings("serial")
+	public Action getConfigureAction(Frame owner) {
+		return new AbstractAction("Update prediction") {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				updatePrediction();
+				refreshView();
+			}
+		};
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 100;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualViewFactory.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualViewFactory.java
new file mode 100644
index 0000000..20ac960
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualViewFactory.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.dataflowoutputport;
+
+import java.util.Arrays;
+import java.util.List;
+
+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;
+import net.sf.taverna.t2.workflowmodel.DataflowOutputPort;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+
+/**
+ * A factory of contextual views for dataflow's output ports.
+ *
+ * @author Alex Nenadic
+ */
+public class DataflowOutputPortContextualViewFactory implements
+		ContextualViewFactory<OutputWorkflowPort> {
+	private FileManager fileManager;
+
+	@Override
+	public boolean canHandle(Object object) {
+		return object instanceof DataflowOutputPort;
+	}
+
+	@Override
+	public List<ContextualView> getViews(OutputWorkflowPort outputport) {
+		return Arrays.asList(new ContextualView[] {
+				new DataflowOutputPortContextualView(outputport, fileManager)});
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualView.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualView.java
new file mode 100644
index 0000000..daa3414
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualView.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.datalink;
+
+import static java.awt.FlowLayout.LEFT;
+
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+
+import uk.org.taverna.scufl2.api.core.DataLink;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+
+/**
+ * Contextual view for dataflow's datalinks.
+ *
+ * @author Alex Nenadic
+ * @author Alan R Williams
+ */
+class DatalinkContextualView extends ContextualView {
+	private static final long serialVersionUID = -5031256519235454876L;
+
+	private DataLink datalink;
+	private JPanel datalinkView;
+	@SuppressWarnings("unused")
+	private final FileManager fileManager;
+
+	public DatalinkContextualView(DataLink datalink, FileManager fileManager) {
+		this.datalink = datalink;
+		this.fileManager = fileManager;
+		initView();
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		refreshView();
+		return datalinkView;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Data link: " + datalink.getReceivesFrom().getName() + " -> " + datalink.getSendsTo().getName();
+	}
+
+	@Override
+	public void refreshView() {
+		datalinkView = new JPanel(new FlowLayout(LEFT));
+		datalinkView.setBorder(new EmptyBorder(5,5,5,5));
+		JLabel label = new JLabel (getTextForLabel());
+		datalinkView.add(label);
+	}
+
+	private String getTextForLabel() {
+		//FIXME
+		// return getTextFromDepth("link", datalink.getResolvedDepth());
+		return "Fix DataLink resolved depth";
+	}
+
+	private void updatePrediction() {
+		//FIXME
+		// fileManager.getCurrentDataflow().checkValidity();
+	}
+
+	@Override
+	@SuppressWarnings("serial")
+	public Action getConfigureAction(Frame owner) {
+		return new AbstractAction("Update prediction") {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				updatePrediction();
+				refreshView();
+			}
+		};
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 100;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualViewFactory.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualViewFactory.java
new file mode 100644
index 0000000..fa8bf96
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualViewFactory.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.datalink;
+
+import java.util.Arrays;
+import java.util.List;
+
+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;
+import net.sf.taverna.t2.workflowmodel.Datalink;
+import uk.org.taverna.scufl2.api.core.DataLink;
+
+/**
+ * A factory of contextual views for dataflow's datalinks.
+ *
+ * @author Alex Nenadic
+ */
+public class DatalinkContextualViewFactory implements
+		ContextualViewFactory<DataLink> {
+	private FileManager fileManager;
+
+	@Override
+	public boolean canHandle(Object object) {
+		return object instanceof Datalink;
+	}
+
+	@Override
+	public List<ContextualView> getViews(DataLink datalink) {
+		return Arrays.asList(new ContextualView[] {
+				new DatalinkContextualView(datalink, fileManager)});
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponent.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponent.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponent.java
new file mode 100644
index 0000000..11306d0
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponent.java
@@ -0,0 +1,389 @@
+package net.sf.taverna.t2.workbench.ui.views.contextualviews.impl;
+
+import static java.awt.GridBagConstraints.BOTH;
+import static java.awt.GridBagConstraints.CENTER;
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.LINE_START;
+import static java.awt.GridBagConstraints.NONE;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.BLUE;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.GREEN;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.ORANGE;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.minusIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.plusIcon;
+
+import java.awt.Color;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.swing.Action;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage;
+import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent;
+import net.sf.taverna.t2.workbench.ui.Utils;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+@SuppressWarnings("serial")
+public class ContextualViewComponent extends JScrollPane implements UIComponentSPI {
+	/** delay before contextual view is redrawn */
+	private static final int DELAY = 250;
+	private static final Color[] colors = new Color[] { BLUE, GREEN, ORANGE };
+	// HACK ALERT!
+	public static boolean selfGenerated = false;
+
+	private Observer<DataflowSelectionMessage> dataflowSelectionListener = new DataflowSelectionListener();
+	private SelectionManager selectionManager;
+	private ContextualViewFactoryRegistry contextualViewFactoryRegistry;
+	GridBagConstraints gbc;
+	protected Map<JPanel, SectionLabel> panelToLabelMap = new HashMap<>();
+	private String lastOpenedSectionName = "";
+	private JPanel mainPanel;
+	private List<JPanel> shownComponents = null;
+	int colorIndex = 0;
+	private Timer updateSelectionTimer = null;
+	private Object lastSelectedObject = null;
+
+	private static final Comparator<ContextualView> viewComparator = new Comparator<ContextualView>() {
+		@Override
+		public int compare(ContextualView o1, ContextualView o2) {
+			return o1.getPreferredPosition() - o2.getPreferredPosition();
+		}
+	};
+
+	public ContextualViewComponent(EditManager editManager,
+			SelectionManager selectionManager,
+			ContextualViewFactoryRegistry contextualViewFactoryRegistry) {
+		this.selectionManager = selectionManager;
+		this.contextualViewFactoryRegistry = contextualViewFactoryRegistry;
+		updateSelectionTimer = new Timer(DELAY, updateSelectionListener);
+		updateSelectionTimer.setRepeats(false);
+
+		initialise();
+
+		editManager.addObserver(new EditManagerObserver());
+		selectionManager.addObserver(new SelectionManagerObserver());
+	}
+
+	@Override
+	public ImageIcon getIcon() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public String getName() {
+		return "Details";
+	}
+
+	private void initialise() {
+		mainPanel = new JPanel(new GridBagLayout());
+		this.setViewportView(mainPanel);
+	}
+
+	@Override
+	public void onDisplay() {
+	}
+
+	@Override
+	public void onDispose() {
+		updateSelectionTimer.stop();
+	}
+
+	@SuppressWarnings("unchecked")
+	private void updateContextualView(List<ContextualViewFactory<?>> viewFactories,
+			Object selection) {
+		if (selection == lastSelectedObject)
+			return;
+		lastSelectedObject = selection;
+		mainPanel = new JPanel(new GridBagLayout());
+		panelToLabelMap.clear();
+
+		GridBagConstraints gbc = new GridBagConstraints();
+		gbc.gridx = 0;
+		gbc.weightx = 0.1;
+		gbc.fill = HORIZONTAL;
+
+		gbc.gridy = 0;
+		shownComponents = new ArrayList<>();
+		List<ContextualView> views = new ArrayList<>();
+		for (ContextualViewFactory<?> cvf : viewFactories)
+			views.addAll(((ContextualViewFactory<Object>) cvf)
+					.getViews(selection));
+		Collections.sort(views, viewComparator);
+		colorIndex = 0;
+		if (views.isEmpty())
+			mainPanel.add(new JLabel("No details available"));
+		else
+			populateContextualView(viewFactories, gbc, views);
+		gbc.weighty = 0.1;
+		gbc.fill = BOTH;
+		mainPanel.add(new JPanel(), gbc);
+		// mainPanel.revalidate();
+		// mainPanel.repaint();
+		this.setViewportView(mainPanel);
+		// this.revalidate();
+		// this.repaint();
+	}
+
+	private void populateContextualView(
+			List<ContextualViewFactory<?>> viewFactories,
+			GridBagConstraints gbc, List<ContextualView> views) {
+		JPanel firstPanel = null;
+		JPanel lastOpenedSection = null;
+		for (ContextualView view : views) {
+			SectionLabel label = new SectionLabel(view.getViewTitle(), nextColor());
+			mainPanel.add(label, gbc);
+			gbc.gridy++;
+			JPanel subPanel = new JPanel();
+			if (view.getViewTitle().equals(lastOpenedSectionName))
+				lastOpenedSection = subPanel;
+			subPanel.setLayout(new GridBagLayout());
+
+			GridBagConstraints constraints = new GridBagConstraints();
+			constraints.gridx = 0;
+			constraints.gridy = 0;
+			constraints.weightx = 0.1;
+			constraints.weighty = 0;
+			constraints.anchor = CENTER;
+			constraints.fill = HORIZONTAL;
+
+			subPanel.add(view, constraints);
+			Frame frame = Utils.getParentFrame(this);
+			Action configureAction = view.getConfigureAction(frame);
+			if (configureAction != null) {
+				JButton configButton = new JButton(configureAction);
+				if (configButton.getText() == null
+						|| configButton.getText().isEmpty())
+					configButton.setText("Configure");
+				constraints.gridy++;
+				constraints.fill = NONE;
+				constraints.anchor = LINE_START;
+				subPanel.add(configButton, constraints);
+			}
+			if (firstPanel == null)
+				firstPanel = subPanel;
+			mainPanel.add(subPanel, gbc);
+			shownComponents.add(subPanel);
+			gbc.gridy++;
+			if (viewFactories.size() != 1)
+				makeCloseable(subPanel, label);
+			else {
+				lastOpenedSectionName = label.getText();
+				lastOpenedSection = subPanel;
+				panelToLabelMap.put(subPanel, label);
+				subPanel.setVisible(false);
+			}
+		}
+		if (lastOpenedSection != null)
+			openSection(lastOpenedSection);
+		else if (firstPanel != null)
+			openSection(firstPanel);
+	}
+
+	private void clearContextualView() {
+		lastSelectedObject = null;
+		mainPanel = new JPanel(new GridBagLayout());
+		mainPanel.add(new JLabel("No details available"));
+		this.setViewportView(mainPanel);
+		this.revalidate();
+	}
+
+	public void updateSelection(Object selectedItem) {
+		findContextualView(selectedItem);
+	}
+
+	private Runnable updateSelectionRunnable = new Runnable() {
+		@Override
+		public void run() {
+			Object selection = getSelection();
+			if (selection == null)
+				clearContextualView();
+			else
+				updateSelection(selection);
+		}
+	};
+
+	private ActionListener updateSelectionListener = new ActionListener() {
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			SwingUtilities.invokeLater(updateSelectionRunnable);
+		}
+	};
+
+	public void updateSelection() {
+		updateSelectionTimer.restart();
+	}
+
+	private Object getSelection() {
+		WorkflowBundle workflowBundle = selectionManager.getSelectedWorkflowBundle();
+
+		/*
+		 * If there is no currently opened dataflow, clear the contextual view
+		 * panel
+		 */
+		if (workflowBundle == null) {
+			return null;
+		}
+		DataflowSelectionModel selectionModel = selectionManager
+				.getDataflowSelectionModel(workflowBundle);
+		Set<Object> selection = selectionModel.getSelection();
+
+		/*
+		 * If the dataflow is opened but no component of the dataflow is
+		 * selected, clear the contextual view panel
+		 */
+		if (selection.isEmpty())
+			return null;
+		return selection.iterator().next();
+	}
+
+	private void findContextualView(Object selection) {
+		List<ContextualViewFactory<?>> viewFactoriesForBeanType = contextualViewFactoryRegistry
+				.getViewFactoriesForObject(selection);
+		updateContextualView(viewFactoriesForBeanType, selection);
+	}
+
+	private final class SelectionManagerObserver extends SwingAwareObserver<SelectionManagerEvent> {
+		@Override
+		public void notifySwing(Observable<SelectionManagerEvent> sender, SelectionManagerEvent message) {
+			if (message instanceof WorkflowBundleSelectionEvent)
+				bundleSelected((WorkflowBundleSelectionEvent) message);
+		}
+
+		private void bundleSelected(WorkflowBundleSelectionEvent event) {
+			WorkflowBundle oldBundle = event
+					.getPreviouslySelectedWorkflowBundle();
+			WorkflowBundle newBundle = event.getSelectedWorkflowBundle();
+
+			if (oldBundle != null)
+				selectionManager.getDataflowSelectionModel(oldBundle)
+						.removeObserver(dataflowSelectionListener);
+			if (newBundle != null)
+				selectionManager.getDataflowSelectionModel(newBundle)
+						.addObserver(dataflowSelectionListener);
+			lastSelectedObject = null;
+			updateSelection();
+		}
+	}
+
+	private final class DataflowSelectionListener extends SwingAwareObserver<DataflowSelectionMessage> {
+		@Override
+		public void notifySwing(Observable<DataflowSelectionMessage> sender,
+				DataflowSelectionMessage message) {
+			updateSelection();
+		}
+	}
+
+	private final class EditManagerObserver extends SwingAwareObserver<EditManagerEvent> {
+		@Override
+		public void notifySwing(Observable<EditManagerEvent> sender, EditManagerEvent message) {
+			Object selection = getSelection();
+			if ((selection != lastSelectedObject) && !selfGenerated) {
+				lastSelectedObject = null;
+				refreshView();
+			}
+		}
+	}
+
+	public void refreshView() {
+		if (mainPanel != null)
+			updateSelection();
+	}
+
+	private final class SectionLabel extends ShadedLabel {
+		private JLabel expand;
+
+		private SectionLabel(String text, Color colour) {
+			super(text, colour);
+			expand = new JLabel(minusIcon);
+			add(expand, 0);
+			setExpanded(true);
+		}
+
+		public void setExpanded(boolean expanded) {
+			if (expanded)
+				expand.setIcon(minusIcon);
+			else
+				expand.setIcon(plusIcon);
+		}
+	}
+
+	private void makeCloseable(JPanel panel, SectionLabel label) {
+		panel.setVisible(false);
+		if (panelToLabelMap.get(panel) != label) {
+			panelToLabelMap.put(panel, label);
+			// Only add mouse listener once
+			label.addMouseListener(new SectionOpener(panel));
+		}
+	}
+
+	protected class SectionOpener extends MouseAdapter {
+		private final JPanel sectionToOpen;
+
+		public SectionOpener(JPanel sectionToOpen) {
+			this.sectionToOpen = sectionToOpen;
+		}
+
+		@Override
+		public void mouseClicked(MouseEvent e) {
+			openSection(sectionToOpen);
+		}
+	}
+
+	public synchronized void openSection(JPanel sectionToOpen) {
+		lastOpenedSectionName = "";
+		for (Entry<JPanel, SectionLabel> entry : panelToLabelMap.entrySet()) {
+			JPanel section = entry.getKey();
+			SectionLabel sectionLabel = entry.getValue();
+
+			if (section != sectionToOpen)
+				section.setVisible(false);
+			else {
+				section.setVisible(!section.isVisible());
+				if (section.isVisible())
+					lastOpenedSectionName = sectionLabel.getText();
+			}
+			sectionLabel.setExpanded(section.isVisible());
+		}
+		this.revalidate();
+		this.repaint();
+	}
+
+	private Color nextColor() {
+		if (colorIndex >= colors.length)
+			colorIndex = 0;
+		return colors[colorIndex++];
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponentFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponentFactory.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponentFactory.java
new file mode 100644
index 0000000..db43a0d
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponentFactory.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (C) 2007-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.ui.views.contextualviews.impl;
+
+import javax.swing.ImageIcon;
+
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+
+public class ContextualViewComponentFactory implements UIComponentFactorySPI {
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+	private ContextualViewFactoryRegistry contextualViewFactoryRegistry;
+
+	@Override
+	public UIComponentSPI getComponent() {
+		return new ContextualViewComponent(editManager, selectionManager,
+				contextualViewFactoryRegistry);
+	}
+
+	@Override
+	public ImageIcon getIcon() {
+		return null;
+	}
+
+	@Override
+	public String getName() {
+		return "Details";
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setContextualViewFactoryRegistry(
+			ContextualViewFactoryRegistry contextualViewFactoryRegistry) {
+		this.contextualViewFactoryRegistry = contextualViewFactoryRegistry;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualView.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualView.java
new file mode 100644
index 0000000..c1b3d06
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualView.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.inputport;
+
+import static java.awt.FlowLayout.LEFT;
+
+import java.awt.FlowLayout;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+
+import uk.org.taverna.scufl2.api.port.InputActivityPort;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+
+/**
+ * Contextual view for dataflow procerssor's input ports.
+ * 
+ * @author Alex Nenadic
+ */
+class InputPortContextualView extends ContextualView {
+	private static final String NO_DETAILS_AVAILABLE_HTML = "<html><body>"
+			+ "<i>No details available.</i>" + "</body><html>";
+	private static final long serialVersionUID = -7743029534480678624L;
+
+	private InputActivityPort inputPort;
+	private JPanel inputPortView;
+
+	public InputPortContextualView(InputActivityPort inputport) {
+		this.inputPort = inputport;
+		initView();
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		refreshView();
+		return inputPortView;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Service input port: " + inputPort.getName();
+	}
+
+	@Override
+	public void refreshView() {
+		inputPortView = new JPanel(new FlowLayout(LEFT));
+		inputPortView.setBorder(new EmptyBorder(5, 5, 5, 5));
+		JLabel label = new JLabel(NO_DETAILS_AVAILABLE_HTML);
+		inputPortView.add(label);
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 100;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualViewFactory.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualViewFactory.java
new file mode 100644
index 0000000..490e5b7
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualViewFactory.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.inputport;
+
+import java.util.Arrays;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import uk.org.taverna.scufl2.api.port.InputActivityPort;
+
+/**
+ * A factory of contextual views for dataflow proessor's (i.e. its associated
+ * activity's) input ports.
+ *
+ * @author Alex Nenadic
+ */
+public class InputPortContextualViewFactory implements
+		ContextualViewFactory<InputActivityPort> {
+	@Override
+	public boolean canHandle(Object object) {
+		return object instanceof InputActivityPort;
+	}
+
+	@Override
+	public List<ContextualView> getViews(InputActivityPort inputport) {
+		return Arrays.asList(new ContextualView[] {
+				new InputPortContextualView(inputport)});
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationAction.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationAction.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationAction.java
new file mode 100644
index 0000000..567cc4b
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationAction.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.merge;
+
+import java.awt.event.ActionEvent;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.ReorderMergePositionsEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.DataLink;
+
+/**
+ * Configuration action for a Merge. This action changes the order of
+ * merge's incoming ports.
+ *
+ * @author Alex Nenadic
+ *
+ */
+@SuppressWarnings("serial")
+class MergeConfigurationAction extends AbstractAction {
+	private static Logger logger = Logger
+			.getLogger(MergeConfigurationAction.class);
+
+	private final List<DataLink> reorderedDataLinksList;
+	private final List<DataLink> datalinks;
+	private final EditManager editManager;
+	private final SelectionManager selectionManager;
+
+	MergeConfigurationAction(List<DataLink> datalinks,
+			List<DataLink> reorderedDataLinksList, EditManager editManager,
+			SelectionManager selectionManager) {
+		this.datalinks = datalinks;
+		this.reorderedDataLinksList = reorderedDataLinksList;
+		this.editManager = editManager;
+		this.selectionManager = selectionManager;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		ReorderMergePositionsEdit edit = new ReorderMergePositionsEdit(
+				datalinks, reorderedDataLinksList);
+
+		WorkflowBundle bundle = selectionManager.getSelectedWorkflowBundle();
+
+		try {
+			editManager.doDataflowEdit(bundle, edit);
+		} catch (IllegalStateException ex1) {
+			logger.error("Could not configure merge", ex1);
+		} catch (EditException ex2) {
+			logger.error("Could not configure merge", ex2);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationView.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationView.java
new file mode 100644
index 0000000..66eeb3e
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationView.java
@@ -0,0 +1,233 @@
+package net.sf.taverna.t2.workbench.ui.views.contextualviews.merge;
+
+import static java.awt.BorderLayout.EAST;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.lang.Math.max;
+import static javax.swing.BoxLayout.Y_AXIS;
+import static javax.swing.ListSelectionModel.SINGLE_SELECTION;
+import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS;
+import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS;
+import static javax.swing.SwingConstants.CENTER;
+import static javax.swing.SwingConstants.LEFT;
+import static javax.swing.SwingConstants.RIGHT;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.downArrowIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.upArrowIcon;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.FontMetrics;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.BoxLayout;
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.DataLink;
+
+@SuppressWarnings("serial")
+public class MergeConfigurationView extends HelpEnabledDialog {
+	private static final String TITLE = "<html><body><b>Order of incoming links</b></body></html>";
+
+	private List<DataLink> dataLinks;
+	private List<DataLink> reorderedDataLinks;
+	/** Ordered list of labels for dataLinks to be displayed to the user */
+	private DefaultListModel<String> labelListModel;
+	/** JList that displays the labelListModel */
+	JList<String> list;
+	/** Button to push the dataLink up the list */
+	private JButton upButton;
+	/** Button to push the dataLink down the list */
+	private JButton downButton;
+	private final EditManager editManager;
+	private final SelectionManager selectionManager;
+
+	public MergeConfigurationView(List<DataLink> dataLinks, EditManager editManager,
+			SelectionManager selectionManager) {
+		super((Frame)null, "Merge Configuration", true);
+
+		this.dataLinks = new ArrayList<>(dataLinks);
+		reorderedDataLinks = new ArrayList<>(dataLinks);
+		this.editManager = editManager;
+		this.selectionManager = selectionManager;
+		labelListModel = new DefaultListModel<>();
+		for (DataLink dataLink : dataLinks)
+			labelListModel.addElement(dataLink.toString());
+
+		initComponents();
+	}
+
+	private void initComponents() {
+        getContentPane().setLayout(new BorderLayout());
+
+		JPanel listPanel = new JPanel();
+		listPanel.setLayout(new BorderLayout());
+		listPanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, 10),
+				new EtchedBorder()));
+
+		JLabel title = new JLabel(TITLE);
+		title.setBorder(new EmptyBorder(5, 5, 5, 5));
+		listPanel.add(title, NORTH);
+
+		list = new JList<>(labelListModel);
+		list.setSelectionMode(SINGLE_SELECTION);
+		list.setVisibleRowCount(-1);
+		list.addListSelectionListener(new ListSelectionListener() {
+			/**
+			 * Enable and disable up and down buttons based on which item in the
+			 * list is selected
+			 */
+			@Override
+			public void valueChanged(ListSelectionEvent e) {
+				int index = list.getSelectedIndex();
+				if ((index == -1) || (index == 0 && labelListModel.size() == 0)) {
+					// nothing selected or only one item in the list
+					upButton.setEnabled(false);
+					downButton.setEnabled(false);
+				} else {
+					upButton.setEnabled(index > 0);
+					downButton.setEnabled(index < labelListModel.size() - 1);
+				}
+			}
+		});
+
+		final JScrollPane listScroller = new JScrollPane(list);
+		listScroller.setBorder(new EmptyBorder(5, 5, 5, 5));
+		listScroller.setBackground(listPanel.getBackground());
+		listScroller.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_ALWAYS);
+		listScroller.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS);
+		// Set the size of scroll pane to make all list items visible
+		FontMetrics fm = listScroller.getFontMetrics(this.getFont());
+		int listScrollerHeight = fm.getHeight() * labelListModel.size() + 75; //+75 just in case
+		listScroller.setPreferredSize(new Dimension(listScroller
+				.getPreferredSize().width, max(listScrollerHeight,
+				listScroller.getPreferredSize().height)));
+		listPanel.add(listScroller, BorderLayout.CENTER);
+
+		JPanel upDownButtonPanel = new JPanel();
+		upDownButtonPanel.setLayout(new BoxLayout(upDownButtonPanel, Y_AXIS));
+		upDownButtonPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
+
+		upButton = new JButton(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				int index = list.getSelectedIndex();
+				if (index != -1) {
+					// Swap the labels
+					String label = (String) labelListModel.elementAt(index);
+					labelListModel.set(index, labelListModel.get(index - 1));
+					labelListModel.set(index - 1, label);
+					// Swap the dataLinks
+					DataLink dataLink = reorderedDataLinks.get(index);
+					reorderedDataLinks.set(index,
+							reorderedDataLinks.get(index - 1));
+					reorderedDataLinks.set(index - 1, dataLink);
+					// Make the pushed item selected
+					list.setSelectedIndex(index - 1);
+					// Refresh the list
+					listScroller.repaint();
+					listScroller.revalidate();
+				}
+			}
+		});
+		upButton.setIcon(upArrowIcon);
+		upButton.setText("Up");
+	    // Place text to the right of icon, vertically centered
+		upButton.setVerticalTextPosition(CENTER);
+		upButton.setHorizontalTextPosition(RIGHT);
+		// Set the horizontal alignment of the icon and text
+		upButton.setHorizontalAlignment(LEFT);
+		upButton.setEnabled(false);
+		upDownButtonPanel.add(upButton);
+
+		downButton = new JButton(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				int index = list.getSelectedIndex();
+				if (index != -1) {
+					// Swap the labels
+					String label = (String) labelListModel.elementAt(index);
+					labelListModel.set(index, labelListModel.get(index + 1));
+					labelListModel.set(index + 1, label);
+					// Swap the dataLinks
+					DataLink dataLink = reorderedDataLinks.get(index);
+					reorderedDataLinks.set(index,
+							reorderedDataLinks.get(index + 1));
+					reorderedDataLinks.set(index + 1, dataLink);
+					// Make the pushed item selected
+					list.setSelectedIndex(index + 1);
+					// Refresh the list
+					list.repaint();
+					listScroller.revalidate();
+				}
+			}
+		});
+		downButton.setIcon(downArrowIcon);
+		downButton.setText("Down");
+	    // Place text to the right of icon, vertically centered
+		downButton.setVerticalTextPosition(CENTER);
+		downButton.setHorizontalTextPosition(RIGHT);
+		// Set the horizontal alignment of the icon and text
+		downButton.setHorizontalAlignment(LEFT);
+		downButton.setEnabled(false);
+		// set the up button to be of the same size as down button
+		upButton.setPreferredSize(downButton.getPreferredSize());
+		upButton.setMaximumSize(downButton.getPreferredSize());
+		upButton.setMinimumSize(downButton.getPreferredSize());
+		upDownButtonPanel.add(downButton);
+
+		listPanel.add(upDownButtonPanel, EAST);
+
+		JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+
+		JButton jbOK = new JButton("OK");
+		jbOK.addActionListener(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				new MergeConfigurationAction(dataLinks, reorderedDataLinks,
+						editManager, selectionManager).actionPerformed(e);
+				closeDialog();
+			}
+		});
+
+		JButton jbCancel = new JButton("Cancel");
+		jbCancel.addActionListener(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				closeDialog();
+			}
+		});
+
+        buttonPanel.add(jbOK);
+        buttonPanel.add(jbCancel);
+
+        getContentPane().add(listPanel, BorderLayout.CENTER);
+        getContentPane().add(buttonPanel, SOUTH);
+        pack();
+	}
+
+	/**
+	 * Close the dialog.
+	 */
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualView.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualView.java
new file mode 100644
index 0000000..deb09fb
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualView.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.merge;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.FlowLayout.LEFT;
+import static net.sf.taverna.t2.lang.ui.HtmlUtils.buildTableOpeningTag;
+import static net.sf.taverna.t2.lang.ui.HtmlUtils.createEditorPane;
+import static net.sf.taverna.t2.lang.ui.HtmlUtils.getHtmlHead;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JEditorPane;
+import javax.swing.JPanel;
+
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workflowmodel.Merge;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.DataLink;
+
+/**
+ * Contextual view for a {@link Merge}.
+ * 
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+class MergeContextualView extends ContextualView {
+	@SuppressWarnings("unused")
+	private DataLink dataLink;
+	private List<DataLink> datalinks;
+	@SuppressWarnings("unused")
+	private WorkflowBundle workflow;
+	private JEditorPane editorPane;
+	private final EditManager editManager;
+	private final ColourManager colourManager;
+	private final SelectionManager selectionManager;
+
+	// TODO inject from Spring via factory?
+	private Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	public MergeContextualView(DataLink dataLink, EditManager editManager,
+			SelectionManager selectionManager, ColourManager colourManager) {
+		this.dataLink = dataLink;
+		this.selectionManager = selectionManager;
+		datalinks = scufl2Tools.datalinksTo(dataLink.getSendsTo());
+		this.editManager = editManager;
+		this.colourManager = colourManager;
+		workflow = selectionManager.getSelectedWorkflowBundle();
+		initView();
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		editorPane = createEditorPane(buildHtml());
+		return panelForHtml(editorPane);
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Merge Position";
+	}
+
+	/**
+	 * Update the view with the latest information from the configuration bean.
+	 */
+	@Override
+	public void refreshView() {
+		editorPane.setText(buildHtml());
+		repaint();
+	}
+
+	private String buildHtml() {
+		StringBuilder html = new StringBuilder(
+				getHtmlHead(getBackgroundColour()));
+		html.append(buildTableOpeningTag())
+				.append("<tr><td colspan=\"2\"><b>")
+				.append(getViewTitle())
+				.append("</b></td></tr>")
+				.append("<tr><td colspan=\"2\"><b>Ordered incoming links</b></td></tr>");
+
+		int counter = 1;
+		for (DataLink datalink : datalinks)
+			html.append("<tr><td>").append(counter++).append(".</td><td>")
+					.append(datalink).append("</td></tr>");
+
+		return html.append("</table>").append("</body></html>").toString();
+	}
+
+	protected JPanel panelForHtml(JEditorPane editorPane) {
+		final JPanel panel = new JPanel();
+
+		JPanel buttonPanel = new JPanel(new FlowLayout(LEFT));
+
+		JButton configureButton = new JButton(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				MergeConfigurationView mergeConfigurationView = new MergeConfigurationView(
+						datalinks, editManager, selectionManager);
+				mergeConfigurationView.setLocationRelativeTo(panel);
+				mergeConfigurationView.setVisible(true);
+			}
+		});
+		configureButton.setText("Configure");
+		buttonPanel.add(configureButton);
+
+		panel.setLayout(new BorderLayout());
+		panel.add(editorPane, CENTER);
+		panel.add(buttonPanel, SOUTH);
+		return panel;
+	}
+
+	public String getBackgroundColour() {
+		return colourManager.getDefaultPropertyMap().get(
+				"net.sf.taverna.t2.workflowmodel.Merge");
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 100;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualViewFactory.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualViewFactory.java
new file mode 100644
index 0000000..712b183
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualViewFactory.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.merge;
+
+import java.util.Arrays;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import uk.org.taverna.scufl2.api.core.DataLink;
+
+/**
+ * A factory of contextual views for dataflow's merges.
+ *
+ * @author Alex Nenadic
+ */
+public class MergeContextualViewFactory implements ContextualViewFactory<DataLink> {
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+	private ColourManager colourManager;
+
+	@Override
+	public boolean canHandle(Object object) {
+		return object instanceof DataLink
+				&& ((DataLink) object).getMergePosition() != null;
+	}
+
+	@Override
+	public List<ContextualView> getViews(DataLink merge) {
+		return Arrays.asList(new ContextualView[] {
+				new MergeContextualView(merge, editManager, selectionManager, colourManager)});
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setColourManager(ColourManager colourManager) {
+		this.colourManager = colourManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualView.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualView.java
new file mode 100644
index 0000000..f2c7861
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualView.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.outputport;
+
+import static java.awt.FlowLayout.LEFT;
+
+import java.awt.FlowLayout;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityOutputPort;
+
+/**
+ * Contextual view for dataflow procerssor's output ports.
+ * 
+ * @author Alex Nenadic
+ */
+public class OutputPortContextualView extends ContextualView {
+	private static final String NO_DETAILS_AVAILABLE_HTML = "<html><body>"
+			+ "<i>No details available.</i>" + "</body><html>";
+	private static final long serialVersionUID = -7743029534480678624L;
+
+	private ActivityOutputPort outputPort;
+	private JPanel outputPortView;
+
+	public OutputPortContextualView(ActivityOutputPort outputport) {
+		this.outputPort = outputport;
+		initView();
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		refreshView();
+		return outputPortView;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Service output port: " + outputPort.getName();
+	}
+
+	@Override
+	public void refreshView() {
+		outputPortView = new JPanel(new FlowLayout(LEFT));
+		outputPortView.setBorder(new EmptyBorder(5,5,5,5));
+		JLabel label = new JLabel(NO_DETAILS_AVAILABLE_HTML);
+		outputPortView.add(label);
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 100;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualViewFactory.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualViewFactory.java
new file mode 100644
index 0000000..71e2cd4
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualViewFactory.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.outputport;
+
+import java.util.Arrays;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityOutputPort;
+
+/**
+ * A factory of contextual views for dataflow proessor's (i.e. its associated
+ * activity's) output ports.
+ *
+ * @author Alex Nenadic
+ */
+public class OutputPortContextualViewFactory implements
+		ContextualViewFactory<ActivityOutputPort> {
+	@Override
+	public boolean canHandle(Object object) {
+		return object instanceof ActivityOutputPort;
+	}
+
+	@Override
+	public List<ContextualView> getViews(ActivityOutputPort outputport) {
+		return Arrays.asList(new ContextualView[] {
+				new OutputPortContextualView(outputport)});
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
new file mode 100644
index 0000000..7744cb3
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1,9 @@
+net.sf.taverna.t2.workbench.ui.views.contextualviews.outputport.OutputPortContextualViewFactory
+net.sf.taverna.t2.workbench.ui.views.contextualviews.inputport.InputPortContextualViewFactory
+net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowoutputport.DataflowOutputPortContextualViewFactory
+net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowinputport.DataflowInputPortContextualViewFactory
+net.sf.taverna.t2.workbench.ui.views.contextualviews.datalink.DatalinkContextualViewFactory
+net.sf.taverna.t2.workbench.ui.views.contextualviews.condition.ConditionContextualViewFactory
+net.sf.taverna.t2.workbench.ui.views.contextualviews.merge.MergeContextualViewFactory
+net.sf.taverna.t2.workbench.ui.views.contextualviews.annotated.AnnotatedContextualViewFactory
+net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflow.DataflowContextualViewFactory

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI b/taverna-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
new file mode 100644
index 0000000..a564691
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualViewComponentFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context-osgi.xml b/taverna-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context-osgi.xml
new file mode 100644
index 0000000..767943d
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context-osgi.xml
@@ -0,0 +1,31 @@
+<?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="ContextualViewComponentFactory" interface="net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI" />
+
+	<service ref="OutputPortContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+	<service ref="InputPortContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+	<service ref="DataflowOutputPortContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+	<service ref="DataflowInputPortContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+	<service ref="DatalinkContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+	<service ref="ConditionContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+	<service ref="MergeContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+	<service ref="AnnotatedContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+	<service ref="DataflowContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+
+	<service ref="ContextualViewFactoryRegistry" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry" />
+
+	<reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" />
+	<reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" />
+	<reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" />
+	<reference id="colourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" />
+
+	<list id="annotationBeans" interface="net.sf.taverna.t2.annotation.AnnotationBeanSPI" />
+	<list id="contextualViewFactories" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" cardinality="0..N" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context.xml
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context.xml b/taverna-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context.xml
new file mode 100644
index 0000000..18bbd36
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context.xml
@@ -0,0 +1,43 @@
+<?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="ContextualViewComponentFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.impl.ContextualViewComponentFactory">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+			<property name="contextualViewFactoryRegistry" ref="ContextualViewFactoryRegistry"/>
+	</bean>
+
+	<bean id="OutputPortContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.outputport.OutputPortContextualViewFactory" />
+	<bean id="InputPortContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.inputport.InputPortContextualViewFactory" />
+	<bean id="DataflowOutputPortContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowoutputport.DataflowOutputPortContextualViewFactory">
+			<property name="fileManager" ref="fileManager" />
+	</bean>
+	<bean id="DataflowInputPortContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowinputport.DataflowInputPortContextualViewFactory">
+			<property name="fileManager" ref="fileManager" />
+	</bean>
+	<bean id="DatalinkContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.datalink.DatalinkContextualViewFactory">
+			<property name="fileManager" ref="fileManager" />
+	</bean>
+	<bean id="ConditionContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.condition.ConditionContextualViewFactory" />
+	<bean id="MergeContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.merge.MergeContextualViewFactory">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+			<property name="colourManager" ref="colourManager" />
+	</bean>
+	<bean id="AnnotatedContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.annotated.AnnotatedContextualViewFactory">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+			<property name="annotationBeans" ref ="annotationBeans"/>
+	</bean>
+	<bean id="DataflowContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflow.DataflowContextualViewFactory">
+			<property name="fileManager" ref="fileManager" />
+			<property name="colourManager" ref="colourManager" />
+	</bean>
+
+	<bean id="ContextualViewFactoryRegistry" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.impl.ContextualViewFactoryRegistryImpl">
+			<property name="contextualViewFactories" ref="contextualViewFactories" />
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/resources/annotatedcontextualview.properties
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/resources/annotatedcontextualview.properties b/taverna-contextual-views-impl/src/main/resources/annotatedcontextualview.properties
new file mode 100644
index 0000000..e3196f6
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/resources/annotatedcontextualview.properties
@@ -0,0 +1,4 @@
+net.sf.taverna.t2.annotation.annotationbeans.FreeTextDescription: Description
+net.sf.taverna.t2.annotation.annotationbeans.Author: Author
+net.sf.taverna.t2.annotation.annotationbeans.DescriptiveTitle: Title
+net.sf.taverna.t2.annotation.annotationbeans.ExampleValue: Example
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-contextual-views/pom.xml b/taverna-contextual-views/pom.xml
new file mode 100644
index 0000000..13a8746
--- /dev/null
+++ b/taverna-contextual-views/pom.xml
@@ -0,0 +1,57 @@
+<?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>taverna-contextual-views</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Contextual views</name>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-contextual-views-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-edits-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-file-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna=selection-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>


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

Posted by st...@apache.org.
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/NotMatches.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotMatches.java b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotMatches.java
new file mode 100644
index 0000000..803d5d7
--- /dev/null
+++ b/taverna-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotMatches.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 NotMatches extends Comparison {
+
+	public String getId() {
+		return "NotMatches";
+	}
+
+	public String getName() {
+		return "does not match";
+	}
+
+	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/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..1956a3f
--- /dev/null
+++ b/taverna-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,3 @@
+net.sf.taverna.t2.workbench.loop.LoopConfigureMenuAction
+net.sf.taverna.t2.workbench.loop.LoopAddMenuAction
+net.sf.taverna.t2.workbench.loop.LoopRemoveMenuAction

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.AddLayerFactorySPI
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.AddLayerFactorySPI b/taverna-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.AddLayerFactorySPI
new file mode 100644
index 0000000..52eafc4
--- /dev/null
+++ b/taverna-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.AddLayerFactorySPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.loop.AddLoopFactory

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
new file mode 100644
index 0000000..9150066
--- /dev/null
+++ b/taverna-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.loop.LoopContextualViewFactory

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/resources/META-INF/spring/loop-ui-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/resources/META-INF/spring/loop-ui-context-osgi.xml b/taverna-loop-ui/src/main/resources/META-INF/spring/loop-ui-context-osgi.xml
new file mode 100644
index 0000000..4abb75f
--- /dev/null
+++ b/taverna-loop-ui/src/main/resources/META-INF/spring/loop-ui-context-osgi.xml
@@ -0,0 +1,21 @@
+<?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="AddLoopFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.AddLayerFactorySPI" />
+
+	<service ref="LoopConfigureMenuAction" auto-export="interfaces" />
+	<service ref="LoopAddMenuAction" auto-export="interfaces" />
+	<service ref="LoopRemoveMenuAction" auto-export="interfaces" />
+
+	<service ref="LoopContextualViewFactory" 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" />
+    <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" />
+    <reference id="applicationConfig" interface="uk.org.taverna.configuration.app.ApplicationConfiguration"/>
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/main/resources/META-INF/spring/loop-ui-context.xml
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/main/resources/META-INF/spring/loop-ui-context.xml b/taverna-loop-ui/src/main/resources/META-INF/spring/loop-ui-context.xml
new file mode 100644
index 0000000..4c2133c
--- /dev/null
+++ b/taverna-loop-ui/src/main/resources/META-INF/spring/loop-ui-context.xml
@@ -0,0 +1,32 @@
+<?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="AddLoopFactory" class="net.sf.taverna.t2.workbench.loop.AddLoopFactory">
+			<property name="editManager" ref="editManager" />
+			<property name="fileManager" ref="fileManager" />
+            <property name="selectionManager" ref="selectionManager" />
+            <property name="applicationConfig" ref="applicationConfig" />            
+	</bean>
+
+	<bean id="LoopConfigureMenuAction" class="net.sf.taverna.t2.workbench.loop.LoopConfigureMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="fileManager" ref="fileManager" />
+	</bean>
+	<bean id="LoopAddMenuAction" class="net.sf.taverna.t2.workbench.loop.LoopAddMenuAction">
+			<property name="addLoopFactory">
+				<ref local="AddLoopFactory"/>
+			</property>
+	</bean>
+	<bean id="LoopRemoveMenuAction" class="net.sf.taverna.t2.workbench.loop.LoopRemoveMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="fileManager" ref="fileManager" />
+	</bean>
+
+	<bean id="LoopContextualViewFactory" class="net.sf.taverna.t2.workbench.loop.LoopContextualViewFactory">
+			<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-loop-ui/src/test/java/net/sf/taverna/t2/workbench/loop/ShowContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/test/java/net/sf/taverna/t2/workbench/loop/ShowContextualView.java b/taverna-loop-ui/src/test/java/net/sf/taverna/t2/workbench/loop/ShowContextualView.java
new file mode 100644
index 0000000..3e3d121
--- /dev/null
+++ b/taverna-loop-ui/src/test/java/net/sf/taverna/t2/workbench/loop/ShowContextualView.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * 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.Component;
+import java.awt.event.ActionEvent;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Processor;
+
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.impl.EditManagerImpl;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.FileManagerImpl;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.impl.SelectionManagerImpl;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.impl.ContextualViewComponent;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.impl.ContextualViewFactoryRegistryImpl;
+
+/**
+ * A standalone application to show contextual views
+ * <p>
+ * The application shows a JFrame containing a contextual view, together with
+ * buttons which will select items in the {@link SelectionManager} for a
+ * (rather) empty current dataflow.
+ *
+ * @author Stian Soiland-Reyes.
+ *
+ */
+public class ShowContextualView {
+
+	public static void main(String[] args) throws Exception {
+		EditManager editManager = new EditManagerImpl();
+		FileManager fileManager = new FileManagerImpl(editManager);
+		ContextualViewFactoryRegistry contextualViewFactoryRegistry = new ContextualViewFactoryRegistryImpl();
+		SelectionManagerImpl selectionMan = new SelectionManagerImpl();
+		selectionMan.setFileManager(fileManager);
+		selectionMan.setEditManager(editManager);
+		new ShowContextualView(editManager, fileManager,selectionMan, contextualViewFactoryRegistry).showFrame();
+	}
+
+	private SelectionManager selectionManager;
+	private FileManager fileManager;
+	private EditManager editManager;
+	private ContextualViewFactoryRegistry contextualViewFactoryRegistry;
+
+	private uk.org.taverna.scufl2.api.core.Processor processor;
+
+	private WorkflowBundle currentDataflow;
+
+	public ShowContextualView(EditManager editManager, FileManager fileManager, final SelectionManager selectionManager, ContextualViewFactoryRegistry contextualViewFactoryRegistry) {
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+		this.selectionManager = selectionManager;
+		this.contextualViewFactoryRegistry = contextualViewFactoryRegistry;
+		currentDataflow = fileManager.newDataflow();
+		makeProcessor();
+
+	}
+
+	private void makeProcessor() {
+	    processor = new Processor(currentDataflow.getMainWorkflow(), "Hello");
+	}
+
+	private List getSelections() {
+		return Arrays.asList(processor, currentDataflow);
+	}
+
+	private Component makeSelectionButtons() {
+		JPanel buttons = new JPanel();
+		for (final Object selection : getSelections()) {
+			buttons.add(new JButton(new AbstractAction("" + selection) {
+				public void actionPerformed(ActionEvent e) {
+					selectionManager.getDataflowSelectionModel(
+							currentDataflow).setSelection(
+							Collections.<Object> singleton(selection));
+				}
+			}));
+		}
+		return buttons;
+	}
+
+	protected void showFrame() {
+		JFrame frame = new JFrame(getClass().getName());
+		ContextualViewComponent contextualViewComponent = new ContextualViewComponent(editManager, selectionManager, contextualViewFactoryRegistry);
+		frame.add(contextualViewComponent, BorderLayout.CENTER);
+
+		frame.add(makeSelectionButtons(), BorderLayout.NORTH);
+		frame.setSize(400, 400);
+		frame.setVisible(true);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-loop-ui/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/taverna-loop-ui/src/test/resources/log4j.properties b/taverna-loop-ui/src/test/resources/log4j.properties
new file mode 100644
index 0000000..850ede3
--- /dev/null
+++ b/taverna-loop-ui/src/test/resources/log4j.properties
@@ -0,0 +1,10 @@
+log4j.rootLogger=WARN, CONSOLE
+log4j.logger.net.sf.taverna.t2=INFO
+#log4j.logger.net.sf.taverna.t2.ui=DEBUG
+
+#log4j.logger.org.apache.commons.httpclient=ERROR
+
+# Default output to console
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%-5p %d{ISO8601} (%c:%L) - %m%n
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-api/pom.xml b/taverna-menu-api/pom.xml
new file mode 100644
index 0000000..ad0e3d5
--- /dev/null
+++ b/taverna-menu-api/pom.xml
@@ -0,0 +1,43 @@
+<?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>menu-api</artifactId>
+	<packaging>bundle</packaging>
+	<name>Menu generation API</name>
+	<description>An SPI system for building UI menus</description>
+
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>selection-api</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>net.sf.taverna.t2.lang</groupId>
+			<artifactId>taverna-observer</artifactId>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractContextualMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractContextualMenuAction.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractContextualMenuAction.java
new file mode 100644
index 0000000..7209cae
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractContextualMenuAction.java
@@ -0,0 +1,64 @@
+/**********************************************************************
+ * 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.ui.menu;
+
+import java.net.URI;
+
+/**
+ * An {@link AbstractMenuAction} that is {@link ContextualMenuComponent} aware.
+ * The contextual selection can be retrieved from
+ * {@link #getContextualSelection()}.
+ * <p>
+ * The cached action will be flushed everytime the contextual selection changes,
+ * forcing a new call to {@link #createAction()} - given that
+ * {@link #isEnabled()} returns true.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public abstract class AbstractContextualMenuAction extends AbstractMenuAction
+		implements ContextualMenuComponent {
+
+	private ContextualSelection contextualSelection;
+
+	public AbstractContextualMenuAction(URI parentId, int positionHint) {
+		super(parentId, positionHint);
+	}
+
+	public AbstractContextualMenuAction(URI parentId, int positionHint, URI id) {
+		super(parentId, positionHint, id);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return contextualSelection != null;
+	}
+
+	@Override
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+		// Force new createAction() call
+		action = null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenu.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenu.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenu.java
new file mode 100644
index 0000000..07eb8d2
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenu.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import static net.sf.taverna.t2.ui.menu.MenuComponent.MenuType.menu;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+/**
+ * A {@link MenuComponent} of the type {@link MenuType#menu menu}.
+ * <p>
+ * Subclass to create an SPI implementation for the {@link MenuManager} of a
+ * menu. The definition of "menu" includes both the menu bar and sub menus. A
+ * menu can contain {@linkplain AbstractMenuAction actions},
+ * {@linkplain AbstractMenuToggle toggles} or {@linkplain AbstractMenuCustom
+ * custom components}, or any of the above grouped in a
+ * {@linkplain AbstractMenuSection section},
+ * {@linkplain AbstractMenuOptionGroup option group} or a
+ * {@linkplain AbstractMenu submenu}.
+ * <p>
+ * Menu components are linked together using URIs, avoiding the need for compile
+ * time dependencies between SPI implementations. To add components to a menu,
+ * use the {@link URI} identifying this menu as their parent id.
+ * <p>
+ * <strong>Note:</strong> To avoid conflicts with other plugins, use a unique
+ * URI root that is related to the Java package name, for instance
+ * <code>http://cs.university.ac.uk/myplugin/2008/menu</code>, and use hash
+ * identifiers for each menu item, for instance
+ * <code>http://cs.university.ac.uk/myplugin/2008/menu#run</code> for a "Run"
+ * item. Use flat URI namespaces, don't base a child's URI on the parent's URI,
+ * as this might make it difficult to relocate the parent menu.
+ * <p>
+ * You need to list the {@linkplain Class#getName() fully qualified class name}
+ * (for example <code>com.example.t2plugin.menu.MyMenu</code>) of the menu
+ * implementation in the SPI description resource file
+ * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so
+ * that it can be discovered by the {@link MenuManager}. This requirement also
+ * applies to parent menu components (except {@link DefaultToolBar} and
+ * {@link DefaultMenuBar}, but ensure they are only listed once.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public abstract class AbstractMenu extends AbstractMenuItem {
+	/**
+	 * Construct a menu bar (does not have a parent). This menu bar can be built
+	 * and used through {@link MenuManager#createMenuBar(URI)}. There is a
+	 * default menu bar implementation in {@link DefaultMenuBar} that can be
+	 * built using {@link MenuManager#createMenuBar()}, but in case you need
+	 * several menu bars for different windows or modes, use this constructor.
+	 * 
+	 * @param id
+	 *            The {@link URI} to identify this menu bar. Use this as the
+	 *            parent ID for menu components to appear in this menu.
+	 */
+	public AbstractMenu(URI id) {
+		super(menu, (URI) null, id);
+	}
+
+	/**
+	 * Construct a submenu.
+	 * 
+	 * @param parentId
+	 *            The {@link URI} of the parent menu. The parent should be of
+	 *            type
+	 *            {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#menu}.
+	 * @param positionHint
+	 *            The position hint to determine the position of this submenu
+	 *            among its siblings in the parent menu. For extensibility, use
+	 *            BASIC style numbering such as 10, 20, etc.
+	 * @param id
+	 *            The {@link URI} to identify this menu bar. Use this as the
+	 *            parent ID for menu components to appear in this submenu.
+	 * @param label
+	 *            The label for presenting this sub-menu in the parent menu.
+	 */
+	public AbstractMenu(URI parentId, int positionHint, URI id, String label) {
+		this(parentId, positionHint, id, new DummyAction(label));
+	}
+
+	/**
+	 * Construct a submenu.
+	 * 
+	 * @param parentId
+	 *            The {@link URI} of the parent menu. The parent should be of
+	 *            type
+	 *            {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#menu}.
+	 * @param positionHint
+	 *            The position hint to determine the position of this submenu
+	 *            among its siblings in the parent menu. For extensibility, use
+	 *            BASIC style numbering such as 10, 20, etc.
+	 * @param id
+	 *            The {@link URI} to identify this menu bar. Use this as the
+	 *            parent ID for menu components to appear in this submenu.
+	 * @param action
+	 *            The action containing a label and icon for presenting this
+	 *            sub-menu in the parent menu.
+	 */
+	public AbstractMenu(URI parentId, int positionHint, URI id, Action action) {
+		super(menu, parentId, id);
+		this.action = action;
+		this.positionHint = positionHint;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuAction.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuAction.java
new file mode 100644
index 0000000..446a2ec
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuAction.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+/**
+ * A {@link MenuComponent} of the type {@link MenuType#action action}.
+ * <p>
+ * Subclass to create an SPI implementation for the {@link MenuManager} of an
+ * action. An action is an item within a menu or toolbar that can be
+ * clicked/selected to invoke some action.
+ * <p>
+ * This action can have as an parent a {@linkplain AbstractMenu menu} or
+ * {@linkplain AbstractToolBar toolbar}, or grouped within an
+ * {@linkplain AbstractMenuSection section} or
+ * {@linkplain AbstractMenuOptionGroup option group}.
+ * <p>
+ * To define the {@link Action}, implement {@link #createAction()}. The action
+ * should provide both the label/icon (representation) and
+ * {@link ActionListener#actionPerformed(ActionEvent)}.
+ * <p>
+ * You need to list the {@linkplain Class#getName() fully qualified class name}
+ * (for example <code>com.example.t2plugin.menu.MyMenuAction</code>) of the menu
+ * action implementation in the SPI description resource file
+ * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so
+ * that it can be discovered by the {@link MenuManager}. This requirement also
+ * applies to parent menu components (except {@link DefaultToolBar} and
+ * {@link DefaultMenuBar}, but ensure they are only listed once.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public abstract class AbstractMenuAction extends AbstractMenuItem {
+	/**
+	 * Construct a menu action to appear within the specified menu component.
+	 * 
+	 * @param parentId
+	 *            The {@link URI} of the parent menu component. The component
+	 *            should be a {@linkplain MenuComponent.MenuType#isParentType()
+	 *            parent type} and must have been registered separately as an
+	 *            SPI.
+	 * @param positionHint
+	 *            The position hint to determine the position of this action
+	 *            among its siblings in the parent menu, section or toolbar. For
+	 *            extensibility, use BASIC style numbering such as 10, 20, etc.
+	 *            (Note that position hints are local to each parent, so each
+	 *            {@linkplain AbstractMenuSection section} have their own
+	 *            position hint scheme.)
+	 */
+	public AbstractMenuAction(URI parentId, int positionHint) {
+		this(parentId, positionHint, null);
+	}
+
+	/**
+	 * Construct a menu action to appear within the specified menu component.
+	 * 
+	 * @param parentId
+	 *            The {@link URI} of the parent menu component. The component
+	 *            should be a {@linkplain MenuComponent.MenuType#isParentType()
+	 *            parent type} and must have been registered separately as an
+	 *            SPI.
+	 * @param positionHint
+	 *            The position hint to determine the position of this action
+	 *            among its siblings in the parent menu, section or toolbar. For
+	 *            extensibility, use BASIC style numbering such as 10, 20, etc.
+	 *            (Note that position hints are local to each parent, so each
+	 *            {@linkplain AbstractMenuSection section} have their own
+	 *            position hint scheme.)
+	 * @param id
+	 *            The {@link URI} to identify this action. Although no
+	 *            components can have an action as their parent, this URI can be
+	 *            used to retrieve the realisation of this component using
+	 *            {@link MenuManager#getComponentByURI(URI)}. This ID might also
+	 *            be registered as a help identifier with the help system.
+	 */
+	public AbstractMenuAction(URI parentId, int positionHint, URI id) {
+		super(MenuType.action, parentId, id);
+		this.positionHint = positionHint;
+	}
+
+	/**
+	 * Call {@link #createAction()} on first call, after that return cached
+	 * action.
+	 * 
+	 * @see #createAction()
+	 */
+	@Override
+	public synchronized Action getAction() {
+		if (action == null)
+			action = createAction();
+		return action;
+	}
+
+	/**
+	 * Create the {@link Action} that labels this menu action, in addition to
+	 * performing the desired action on
+	 * {@link ActionListener#actionPerformed(ActionEvent)}.
+	 * <p>
+	 * This method will be called by {@link #getAction()} on the first call.
+	 * Concurrent calls to <tt>getAction()</tt> will return the same action.
+	 * <p>
+	 * Implementations might use {@link AbstractAction} as a superclass for menu
+	 * actions. It is recommended to make the action a top level class so that
+	 * it can be used both within an {@link AbstractMenuAction} of a menu bar
+	 * and within an {@link AbstractMenuAction} of a tool bar.
+	 * 
+	 * @see #getAction()
+	 * @return A configured {@link Action} that should at least have a label or
+	 *         icon.
+	 */
+	protected abstract Action createAction();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuCustom.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuCustom.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuCustom.java
new file mode 100644
index 0000000..9a64130
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuCustom.java
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.awt.Component;
+import java.net.URI;
+
+import javax.swing.JButton;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+/**
+ * A {@link MenuComponent} of the type {@link MenuType#custom}.
+ * <p>
+ * Subclass to create an SPI implementation for the {@link MenuManager} of a
+ * custom menu or toolbar {@link Component}, for instance a {@link JMenu},
+ * {@link JMenuItem} or {@link JButton}.
+ * <p>
+ * This type of component can be useful for adding third party components that
+ * are built using other menu systems, or to provide dynamic menus such as a
+ * list of open files. This is the recommended way to customise the menus,
+ * although it is also possible to modify the components returned using
+ * {@link MenuManager#getComponentByURI(URI)}, but as the components built by
+ * the menu manager might be refreshed by various actions forcing an update to
+ * the SPI registry, such as installing a plugin. By using a custom menu
+ * component it is possible to avoid these problems and to provide the
+ * {@link Component} to be inserted into the menu/toolbar as built by the
+ * {@link MenuManager}.
+ * <p>
+ * This component can have as an parent any menu component that
+ * {@linkplain MenuType#isParentType() is a parent type}. Note that although you
+ * can specify an {@link URI} to identify the custom component (to be used with
+ * {@link MenuManager#getComponentByURI(URI)} a custom component can't have
+ * children. Such children would have to be created manually and added to the
+ * component.
+ * <p>
+ * You need to list the {@linkplain Class#getName() fully qualified class name}
+ * (for example <code>com.example.t2plugin.menu.MyMenuAction</code>) of the menu
+ * action implementation in the SPI description resource file
+ * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so
+ * that it can be discovered by the {@link MenuManager}. This requirement also
+ * applies to parent menu components (except {@link DefaultToolBar} and
+ * {@link DefaultMenuBar}, but ensure they are only listed once.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public abstract class AbstractMenuCustom extends AbstractMenuItem {
+	/**
+	 * Construct a menu action to appear within the specified menu component.
+	 * 
+	 * @param parentId
+	 *            The {@link URI} of the parent menu component. The component
+	 *            should be a {@link MenuType#isParentType() parent type} and
+	 *            must have been registered separately as an SPI.
+	 * @param positionHint
+	 *            The position hint to determine the position of this action
+	 *            among its siblings in the parent menu, section or toolbar. For
+	 *            extensibility, use BASIC style numbering such as 10, 20, etc.
+	 *            (Note that position hints are local to each parent, so each
+	 *            {@linkplain AbstractMenuSection section} have their own
+	 *            position hint scheme.)
+	 */
+	public AbstractMenuCustom(URI parentId, int positionHint) {
+		this(parentId, positionHint, null);
+	}
+
+	/**
+	 * Construct a menu action to appear within the specified menu component.
+	 * 
+	 * @param parentId
+	 *            The {@link URI} of the parent menu component. The component
+	 *            should be a {@linkplain MenuType#isParentType() parent type}
+	 *            and must have been registered separately as an SPI.
+	 * @param positionHint
+	 *            The position hint to determine the position of this action
+	 *            among its siblings in the parent menu, section or toolbar. For
+	 *            extensibility, use BASIC style numbering such as 10, 20, etc.
+	 *            (Note that position hints are local to each parent, so each
+	 *            {@linkplain AbstractMenuSection section} have their own
+	 *            position hint scheme.)
+	 * @param id
+	 *            The {@link URI} to identify this action. Although no
+	 *            components can have an action as their parent, this URI can be
+	 *            used to retrieve the realisation of this component using
+	 *            {@link MenuManager#getComponentByURI(URI)}. This ID might also
+	 *            be registered as a help identifier with the help system.
+	 */
+	public AbstractMenuCustom(URI parentId, int positionHint, URI id) {
+		super(MenuType.custom, parentId, id);
+		this.positionHint = positionHint;
+	}
+
+	/**
+	 * Create the {@link Component} that is to be added to the parent.
+	 * <p>
+	 * The component must be compatible with the parent realisation from the
+	 * {@link MenuManager}, for instance you can't add {@link JMenuItem}s to a
+	 * toolbar.
+	 * </p>
+	 * <p>
+	 * Note that the component might get assigned new parents if the
+	 * menues/toolbars are rebuilt by the {@link MenuManager} is refreshed,
+	 * although the menu manager will try to avoid a second call to
+	 * {@link #createCustomComponent()}.
+	 * </p>
+	 * 
+	 * @return A custom {@link Component} such as {@link JMenu},
+	 *         {@link JMenuItem} or {@link JButton} to be added to the parent
+	 *         menu component.
+	 */
+	protected abstract Component createCustomComponent();
+
+	/**
+	 * Return the custom component created using
+	 * {@link #createCustomComponent()} on first call, return cached instance on
+	 * later calls.
+	 * 
+	 * {@inheritDoc}
+	 */
+	@Override
+	public final synchronized Component getCustomComponent() {
+		if (customComponent == null)
+			customComponent = createCustomComponent();
+		return customComponent;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuItem.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuItem.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuItem.java
new file mode 100644
index 0000000..63b6c78
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuItem.java
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+
+/**
+ * An abstract implementation of {@link MenuComponent} that can be used by
+ * convenience to create menu component SPIs for the {@link MenuManager}.
+ * <p>
+ * Abstract subclasses of this class are specialised by their
+ * {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType}. To create a menu,
+ * toolbar, section, action etc, create an SPI implementation by subclassing
+ * depending on the required type:
+ * </p>
+ * <dl>
+ * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#menu}</dt>
+ * <dd>Subclass {@link AbstractMenu}</dd>
+ * 
+ * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#toolBar}</dt>
+ * <dd>Subclass {@link AbstractToolBar}</dd>
+ * 
+ * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#section}</dt>
+ * <dd>Subclass {@link AbstractMenuSection}</dd>
+ * 
+ * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#action}</dt>
+ * <dd>Subclass {@link AbstractMenuAction}</dd>
+ * 
+ * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#toggle}</dt>
+ * <dd>Subclass {@link AbstractMenuToggle}</dd>
+ * 
+ * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#custom}</dt>
+ * <dd>Subclass {@link AbstractMenuCustom}</dd>
+ * 
+ * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#optionGroup}</dt>
+ * <dd>Subclass {@link AbstractMenuOptionGroup}</dd>
+ * 
+ * </dl>
+ * <p>
+ * Note that you are not required to subclass any of these as long as your SPI
+ * implementations implement the {@link MenuComponent} interface. In all cases
+ * you are still required to list all your implementations, including
+ * intermediate menus and sections, in the SPI description resource file
+ * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code>
+ * </p>
+ * 
+ * @author Stian Soiland-Reyes
+ * 
+ */
+public abstract class AbstractMenuItem implements MenuComponent {
+	/**
+	 * An {@link Action} that does not perform any action, but only contains a
+	 * name and icon. Used by {@link AbstractMenu} and others.
+	 * 
+	 * @author Stian Soiland-Reyes
+	 * 
+	 */
+	@SuppressWarnings("serial")
+	public static class DummyAction extends AbstractAction {
+		public DummyAction(String name) {
+			super(name);
+		}
+
+		public DummyAction(String name, Icon icon) {
+			super(name, icon);
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+		}
+	}
+
+	public AbstractMenuItem(MenuType type, URI parentId, URI id) {
+		this.type = type;
+		this.parentId = parentId;
+		this.id = id;
+	}
+
+	private final MenuType type;
+	private final URI parentId;
+	private final URI id;
+	protected int positionHint = 100;
+	protected Action action;
+	protected Component customComponent;
+
+	@Override
+	public Action getAction() {
+		return action;
+	}
+
+	@Override
+	public Component getCustomComponent() {
+		return customComponent;
+	}
+
+	@Override
+	public URI getId() {
+		return id;
+	}
+
+	@Override
+	public URI getParentId() {
+		return parentId;
+	}
+
+	@Override
+	public int getPositionHint() {
+		return positionHint;
+	}
+
+	@Override
+	public MenuType getType() {
+		return type;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return true;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuOptionGroup.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuOptionGroup.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuOptionGroup.java
new file mode 100644
index 0000000..091cc23
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuOptionGroup.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.net.URI;
+
+/**
+ * A {@link MenuComponent} of the type {@link MenuType#optionGroup}.
+ * <p>
+ * Subclass to create an SPI implementation for the {@link MenuManager} of an
+ * option group. An option group is similar to a
+ * {@linkplain AbstractMenuSection section}, but enforces that only one of the
+ * children are selected at any time.
+ * <p>
+ * Menu components are linked together using URIs, avoiding the need for compile
+ * time dependencies between SPI implementations. To add actions or toggles to
+ * an option group, use the {@link URI} identifying this section as their parent
+ * id.
+ * <p>
+ * <strong>Note:</strong> To avoid conflicts with other plugins, use a unique
+ * URI root that is related to the Java package name, for instance
+ * <code>http://cs.university.ac.uk/myplugin/2008/menu</code>, and use hash
+ * identifiers for each menu item, for instance
+ * <code>http://cs.university.ac.uk/myplugin/2008/menu#run</code> for a "Run"
+ * item. Use flat URI namespaces, don't base a child's URI on the parent's URI,
+ * as this might make it difficult to relocate the parent menu.
+ * <p>
+ * You need to list the {@linkplain Class#getName() fully qualified class name}
+ * (for example <code>com.example.t2plugin.menu.MyMenu</code>) of the option
+ * group implementation in the SPI description resource file
+ * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so
+ * that it can be discovered by the {@link MenuManager}. This requirement also
+ * applies to parent menu components (except {@link DefaultToolBar} and
+ * {@link DefaultMenuBar}, but ensure they are only listed once.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public abstract class AbstractMenuOptionGroup extends AbstractMenuItem {
+	/**
+	 * Construct an option group.
+	 * 
+	 * @param parentId
+	 *            The {@link URI} of the parent menu component. The parent
+	 *            should be of type {@link MenuType#menu} or
+	 *            {@link MenuType#toolBar}.
+	 * @param positionHint
+	 *            The position hint to determine the position of this option
+	 *            group among its siblings in the parent menu. For
+	 *            extensibility, use BASIC style numbering such as 10, 20, etc.
+	 *            (Note that position hints are local to each parent, so each
+	 *            option group have their own position hint scheme for their
+	 *            children.)
+	 * @param id
+	 *            The {@link URI} to identify this option group. Use this as the
+	 *            parent ID for menu components to appear in this option group.
+	 */
+	public AbstractMenuOptionGroup(URI parentId, int positionHint, URI id) {
+		super(MenuType.optionGroup, parentId, id);
+		this.positionHint = positionHint;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuSection.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuSection.java
new file mode 100644
index 0000000..2e649e0
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuSection.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.awt.Color;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+/**
+ * A {@link MenuComponent} of the type {@link MenuType#section}.
+ * <p>
+ * Subclass to create an SPI implementation for the {@link MenuManager} of a
+ * section. A section is a part of a {@linkplain AbstractMenu menu} or
+ * {@linkplain AbstractToolBar toolbar} that group together
+ * {@linkplain AbstractMenuAction actions} or {@linkplain AbstractMenuToggle
+ * toggles}, and separates them from siblings using separators if needed.
+ * <p>
+ * Menu components are linked together using URIs, avoiding the need for compile
+ * time dependencies between SPI implementations. To add actions to a section,
+ * use the {@link URI} identifying this section as their parent id.
+ * <p>
+ * <strong>Note:</strong> To avoid conflicts with other plugins, use a unique
+ * URI root that is related to the Java package name, for instance
+ * <code>http://cs.university.ac.uk/myplugin/2008/menu</code>, and use hash
+ * identifiers for each menu item, for instance
+ * <code>http://cs.university.ac.uk/myplugin/2008/menu#run</code> for a "Run"
+ * item. Use flat URI namespaces, don't base a child's URI on the parent's URI,
+ * as this might make it difficult to relocate the parent menu.
+ * <p>
+ * You need to list the {@linkplain Class#getName() fully qualified class name}
+ * (for example <code>com.example.t2plugin.menu.MyMenu</code>) of the section
+ * implementation in the SPI description resource file
+ * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so
+ * that it can be discovered by the {@link MenuManager}. This requirement also
+ * applies to parent menu components (except {@link DefaultToolBar} and
+ * {@link DefaultMenuBar}, but ensure they are only listed once.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public abstract class AbstractMenuSection extends AbstractMenuItem {
+	public static final String SECTION_COLOR = "sectionColor";
+
+	/**
+	 * Construct a menu section.
+	 * 
+	 * @param parentId
+	 *            The {@link URI} of the parent menu component. The parent
+	 *            should be of type {@link MenuType#menu} or
+	 *            {@link MenuType#toolBar}.
+	 * @param positionHint
+	 *            The position hint to determine the position of this section
+	 *            among its siblings in the parent menu. For extensibility, use
+	 *            BASIC style numbering such as 10, 20, etc. (Note that position
+	 *            hints are local to each parent, so each section have their own
+	 *            position hint scheme for their children.)
+	 * @param id
+	 *            The {@link URI} to identify this menu section. Use this as the
+	 *            parent ID for menu components to appear in this section.
+	 */
+	public AbstractMenuSection(URI parentId, int positionHint, URI id) {
+		super(MenuType.section, parentId, id);
+		this.positionHint = positionHint;
+	}
+
+	@Override
+	public synchronized Action getAction() {
+		if (action == null)
+			action = createAction();
+		return action;
+	}
+
+	/**
+	 * (Optionally) Create the {@link Action} that labels this section.
+	 * <p>
+	 * The actual action will be ignored, but the label and/or icon will be used
+	 * as a section header in the menu. If the property {@link #SECTION_COLOR}
+	 * has been defined in the action, that {@link Color} will be used to make
+	 * the section background.
+	 * <p>
+	 * The default implementation of this method returns <code>null</code>,
+	 * meaning that no section header will be created - instead a simple line
+	 * will separate this section from the items above (if needed).
+	 * <p>
+	 * Implementations might use {@link AbstractAction} as a superclass for menu
+	 * actions.
+	 * 
+	 * @return A configured {@link Action} that should at least have a label or
+	 *         icon.
+	 */
+	protected Action createAction() {
+		return null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuToggle.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuToggle.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuToggle.java
new file mode 100644
index 0000000..97e977d
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuToggle.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+/**
+ * A {@link MenuComponent} of the type {@link MenuType#toggle}.
+ * <p>
+ * Subclass to create an SPI implementation for the {@link MenuManager} of an
+ * toggle action. A toggle is a menu item that can be turned on/off and are
+ * typically represented with a check box when they are enabled.
+ * <p>
+ * This action can have as an parent a {@linkplain AbstractMenu menu} or
+ * {@linkplain AbstractToolBar toolbar}, or grouped within a
+ * {@linkplain AbstractMenuSection section} or
+ * {@linkplain AbstractMenuOptionGroup option group}.
+ * <p>
+ * To define the {@link Action}, implement {@link #createAction()}. The action
+ * should provide both the label/icon (representation) and
+ * {@link ActionListener#actionPerformed(ActionEvent)}.
+ * <p>
+ * You need to list the {@linkplain Class#getName() fully qualified class name}
+ * (for example <code>com.example.t2plugin.menu.MyMenuAction</code>) of the menu
+ * action implementation in the SPI description resource file
+ * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so
+ * that it can be discovered by the {@link MenuManager}. This requirement also
+ * applies to parent menu components (except {@link DefaultToolBar} and
+ * {@link DefaultMenuBar}, but ensure they are only listed once.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public abstract class AbstractMenuToggle extends AbstractMenuItem {
+	/**
+	 * Construct a toggle action to appear within the specified menu component.
+	 * 
+	 * @param parentId
+	 *            The {@link URI} of the parent menu component. The component
+	 *            should be a {@linkplain MenuType#isParentType() parent type}
+	 *            and must have been registered separately as an SPI.
+	 * @param positionHint
+	 *            The position hint to determine the position of this toggle
+	 *            action among its siblings in the parent menu, section or
+	 *            toolbar. For extensibility, use BASIC style numbering such as
+	 *            10, 20, etc. (Note that position hints are local to each
+	 *            parent, so each {@linkplain AbstractMenuSection section} have
+	 *            their own position hint scheme.)
+	 */
+	public AbstractMenuToggle(URI parentId, int positionHint) {
+		this(parentId, null, positionHint);
+	}
+
+	/**
+	 * Construct a toggle action to appear within the specified menu component.
+	 * 
+	 * @param parentId
+	 *            The {@link URI} of the parent menu component. The component
+	 *            should be a {@link MenuType#isParentType() parent type} and
+	 *            must have been registered separately as an SPI.
+	 * @param id
+	 *            The {@link URI} to identify this toggle action. Although no
+	 *            components can have an action as their parent, this URI can be
+	 *            used to retrieve the realisation of this component using
+	 *            {@link MenuManager#getComponentByURI(URI)}. This ID might also
+	 *            be registered as a help identifier with the help system.
+	 * @param positionHint
+	 *            The position hint to determine the position of this action
+	 *            among its siblings in the parent menu, section or toolbar. For
+	 *            extensibility, use BASIC style numbering such as 10, 20, etc.
+	 *            (Note that position hints are local to each parent, so each
+	 *            {@linkplain AbstractMenuSection section} have their own
+	 *            position hint scheme.)
+	 */
+	public AbstractMenuToggle(URI parentId, URI id, int positionHint) {
+		super(MenuType.toggle, parentId, id);
+		this.positionHint = positionHint;
+	}
+
+	/**
+	 * Call {@link #createAction()} on first call, after that return cached
+	 * action.
+	 * 
+	 * @see #createAction()
+	 * 
+	 *      {@inheritDoc}
+	 */
+	@Override
+	public synchronized Action getAction() {
+		if (action == null)
+			action = createAction();
+		return action;
+	}
+
+	/**
+	 * Create the {@link Action} that labels this toggle action, in addition to
+	 * performing the desired action on
+	 * {@link ActionListener#actionPerformed(ActionEvent)}.
+	 * <p>
+	 * Implementations might use {@link AbstractAction} as a superclass for menu
+	 * actions. It is recommended to make the action a top level class so that
+	 * it can be used both within an {@link AbstractMenuAction} of a menu bar
+	 * and within an {@link AbstractMenuAction} of a tool bar.
+	 * </p>
+	 * 
+	 * @return A configured {@link Action} that should at least have a label or
+	 *         icon.
+	 */
+	protected abstract Action createAction();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractToolBar.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractToolBar.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractToolBar.java
new file mode 100644
index 0000000..234ea75
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractToolBar.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.net.URI;
+
+/**
+ * A {@link MenuComponent} of the type {@link MenuType#toolBar}.
+ * <p>
+ * Subclass to create an SPI implementation for the {@link MenuManager} of a
+ * toolbar. A toolbar can contain {@linkplain AbstractMenuAction actions},
+ * {@linkplain AbstractMenuToggle toggles} or {@linkplain AbstractMenuCustom
+ * custom components}, or any of the above grouped in a
+ * {@linkplain AbstractMenuSection section} or an
+ * {@linkplain AbstractMenuOptionGroup option group}.
+ * <p>
+ * The {@link DefaultToolBar default toolbar} can be used as a parent for items
+ * that are to be returned in the toolbar {@link MenuManager#createToolBar()},
+ * while toolbars from other instances of AbstractToolBar can be created using
+ * {@link MenuManager#createToolBar(URI)} specifying the URI of the toolbar's
+ * identifier.
+ * <p>
+ * Menu components are linked together using URIs, avoiding the need for compile
+ * time dependencies between SPI implementations. To add components to a
+ * toolbar, use the {@link URI} identifying this toolbar as their parent id.
+ * <p>
+ * <strong>Note:</strong> To avoid conflicts with other plugins, use a unique
+ * URI root that is related to the Java package name, for instance
+ * <code>http://cs.university.ac.uk/myplugin/2008/menu</code>, and use hash
+ * identifiers for each menu item, for instance
+ * <code>http://cs.university.ac.uk/myplugin/2008/menu#run</code> for a "Run"
+ * item. Use flat URI namespaces, don't base a child's URI on the parent's URI,
+ * as this might make it difficult to relocate the parent menu.
+ * <p>
+ * You need to list the {@linkplain Class#getName() fully qualified class name}
+ * (for example <code>com.example.t2plugin.menu.MyMenu</code>) of the toolbar
+ * implementation in the SPI description resource file
+ * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so
+ * that it can be discovered by the {@link MenuManager}. This requirement also
+ * applies to parent menu components (except {@link DefaultToolBar} and
+ * {@link DefaultMenuBar}, but ensure they are only listed once.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public abstract class AbstractToolBar extends AbstractMenuItem {
+	/**
+	 * Construct a toolbar with the given {@link URI} as identifier.
+	 * 
+	 * @param id
+	 *            The {@link URI} to identify this toolbar. Use this as the
+	 *            parent ID for menu components to appear in this toolbar.
+	 */
+	public AbstractToolBar(URI id) {
+		super(MenuType.toolBar, null, id);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualMenuComponent.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualMenuComponent.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualMenuComponent.java
new file mode 100644
index 0000000..080beb1
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualMenuComponent.java
@@ -0,0 +1,35 @@
+package net.sf.taverna.t2.ui.menu;
+
+import java.awt.Component;
+
+/**
+ * A contextual menu component.
+ * <p>
+ * A {@link MenuComponent} that also implements ContextualMenuComponent, when
+ * included in a menu tree rooted in the {@link DefaultContextualMenu} and
+ * retrieved using
+ * {@link MenuManager#createContextMenu(Object, Object, Component)}, will be
+ * {@linkplain #setContextualSelection(ContextualSelection) informed} before
+ * calls to {@link #isEnabled()} or {@link #getAction()}.
+ * <p>
+ * In this way the contextual menu item can be visible for only certain
+ * selections, and its action can be bound to the current selection.
+ * <p>
+ * Contextual menu components can be grouped by {@linkplain AbstractMenuSection
+ * sections} and {@linkplain AbstractMenu sub-menus}, or directly have the
+ * {@link DefaultContextualMenu} as the parent.
+ * 
+ * @see ContextualSelection
+ * @see DefaultContextualMenu
+ * @author Stian Soiland-Reyes
+ */
+public interface ContextualMenuComponent extends MenuComponent {
+	/**
+	 * Set the contextual selection, or <code>null</code> if there is no current
+	 * selection (if the menu item was not included in a contextual menu).
+	 * 
+	 * @param contextualSelection
+	 *            The contextual selection
+	 */
+	void setContextualSelection(ContextualSelection contextualSelection);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualSelection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualSelection.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualSelection.java
new file mode 100644
index 0000000..e94307e
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualSelection.java
@@ -0,0 +1,48 @@
+package net.sf.taverna.t2.ui.menu;
+
+import java.awt.Component;
+
+import javax.swing.JPopupMenu;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * A contextual selection as passed to a {@link ContextualMenuComponent}.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class ContextualSelection {
+	private final Object parent;
+	private final Object selection;
+	private final Component relativeToComponent;
+
+	public ContextualSelection(Object parent, Object selection,
+			Component relativeToComponent) {
+		this.parent = parent;
+		this.selection = selection;
+		this.relativeToComponent = relativeToComponent;
+	}
+
+	/**
+	 * The parent object of the selected object, for instance a {@link Workflow}.
+	 */
+	public Object getParent() {
+		return parent;
+	}
+
+	/**
+	 * The selected object which actions in the contextual menu relate to, for
+	 * instance a Processor.
+	 */
+	public Object getSelection() {
+		return selection;
+	}
+
+	/**
+	 * A UI component which the returned {@link JPopupMenu} (and it's actions)
+	 * is to be relative to, for instance as a parent of pop-up dialogues.
+	 */
+	public Component getRelativeToComponent() {
+		return relativeToComponent;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultContextualMenu.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultContextualMenu.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultContextualMenu.java
new file mode 100644
index 0000000..0db13a7
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultContextualMenu.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.net.URI;
+
+/**
+ * The default contextual menu, created using
+ * {@link MenuManager#createContextMenu(Object, Object, java.awt.Component)()}.
+ * <p>
+ * Items that are part of a contextual menu should also implement
+ * {@link ContextualMenuComponent}, the menu manager will then be able to tell
+ * the items what is the current selection for the contextual menu, so that the
+ * items can update their {@link MenuComponent#isEnabled()} (only visible for
+ * some selections) and {@link MenuComponent#getAction()} (the action needs the
+ * selected object).
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class DefaultContextualMenu extends AbstractMenu {
+	/**
+	 * The URI of a menu item representing the default menu bar. Menu items who
+	 * has this URI as their {@link #getParentId()} will be shown in the top
+	 * menu of the main application window.
+	 */
+	public static final URI DEFAULT_CONTEXT_MENU = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#defaultContextMenu");
+
+	/**
+	 * Construct the default menu bar
+	 */
+	public DefaultContextualMenu() {
+		super(DEFAULT_CONTEXT_MENU);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultMenuBar.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultMenuBar.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultMenuBar.java
new file mode 100644
index 0000000..8c5eab6
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultMenuBar.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.net.URI;
+
+/**
+ * The default {@link AbstractMenu menu bar} that appears in the main
+ * application window, created using {@link MenuManager#createMenuBar()}.
+ * Alternative menu bars can be created using
+ * {@link MenuManager#createMenuBar(URI)} - referring to the URI of another
+ * instance of {@link AbstractMenu}.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class DefaultMenuBar extends AbstractMenu {
+	/**
+	 * The URI of a menu item representing the default menu bar. Menu items who
+	 * has this URI as their {@link #getParentId()} will be shown in the top
+	 * menu of the main application window.
+	 */
+	public static final URI DEFAULT_MENU_BAR = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#defaultMenuBar");
+
+	/**
+	 * Construct the default menu bar
+	 * 
+	 */
+	public DefaultMenuBar() {
+		super(DEFAULT_MENU_BAR);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultToolBar.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultToolBar.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultToolBar.java
new file mode 100644
index 0000000..302c37a
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultToolBar.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.net.URI;
+
+/**
+ * The default tool bar that will be shown by the main application window. Use
+ * {@link #DEFAULT_TOOL_BAR} as the {@link #getParentId()} for items that should
+ * appear in this toolbar. This toolbar can be created using
+ * {@link MenuManager#createToolBar()}
+ * <p>
+ * Separate toolbars can be made by subclassing {@link AbstractToolBar} and
+ * created by using {@link MenuManager#createToolBar(URI)}.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class DefaultToolBar extends AbstractToolBar {
+	/**
+	 * The URI of a tool bar item representing the default tool bar. Items who
+	 * has this URI as their {@link #getParentId()} will be shown in the default
+	 * toolbar of the main application window.
+	 */
+	public static final URI DEFAULT_TOOL_BAR = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#defaultToolBar");
+
+	/**
+	 * Construct the default toolbar.
+	 */
+	public DefaultToolBar() {
+		super(DEFAULT_TOOL_BAR);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOnlyAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOnlyAction.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOnlyAction.java
new file mode 100644
index 0000000..7260fdf
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOnlyAction.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+/**
+ * Marker interface for actions that are valid only when the design perspective
+ * is selected.
+ * 
+ * @author alanrw
+ * @author David Withers
+ */
+public interface DesignOnlyAction {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOrResultsAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOrResultsAction.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOrResultsAction.java
new file mode 100644
index 0000000..26e6b62
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOrResultsAction.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+/**
+ * Marker interface for actions that are valid the design or result perspectives
+ * are selected.
+ * 
+ * @author alanrw
+ * @author David Withers
+ */
+public interface DesignOrResultsAction {
+
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/resources/META-INF/spring/menu-items-context.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/resources/META-INF/spring/menu-items-context.xml b/taverna-menu-items/src/main/resources/META-INF/spring/menu-items-context.xml
new file mode 100644
index 0000000..6208a72
--- /dev/null
+++ b/taverna-menu-items/src/main/resources/META-INF/spring/menu-items-context.xml
@@ -0,0 +1,124 @@
+<?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="ActivityInputPortSection" class="net.sf.taverna.t2.ui.menu.items.activityport.ActivityInputPortSection" />
+	<bean id="ActivityOutputPortSection" class="net.sf.taverna.t2.ui.menu.items.activityport.ActivityOutputPortSection" />
+	<bean id="ConditionMenuActions" class="net.sf.taverna.t2.ui.menu.items.processor.ConditionMenuActions">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+			<property name="activityIconManager" ref="activityIconManager" />
+	</bean>
+	<bean id="AnnotatedConfigureMenuAction" class="net.sf.taverna.t2.ui.menu.items.annotated.AnnotatedConfigureMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+			<property name="annotationBeans" ref="annotationBeans" />
+	</bean>
+	<bean id="ConditionSection" class="net.sf.taverna.t2.ui.menu.items.controllink.ConditionSection" />
+	<bean id="ConfigureRunningContextualMenuSection" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureRunningContextualMenuSection" />
+	<bean id="ConfigureSection" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureSection" />
+	<bean id="ConnectDataflowInputPortMenuActions" class="net.sf.taverna.t2.ui.menu.items.ports.ConnectDataflowInputPortMenuActions">
+			<property name="editManager" ref="editManager" />
+			<property name="menuManager" ref="menuManager" />
+			<property name="workbenchConfiguration" ref="workbenchConfiguration" />
+			<property name="activityIconManager" ref="activityIconManager" />
+			<property name="colourManager" ref="colourManager" />
+	</bean>
+	<bean id="ConnectDataflowOutputPortMenuActions" class="net.sf.taverna.t2.ui.menu.items.ports.ConnectDataflowOutputPortMenuActions">
+			<property name="editManager" ref="editManager" />
+			<property name="menuManager" ref="menuManager" />
+			<property name="workbenchConfiguration" ref="workbenchConfiguration" />
+			<property name="activityIconManager" ref="activityIconManager" />
+			<property name="colourManager" ref="colourManager" />
+	</bean>
+	<bean id="ConnectInputPortMenuActions" class="net.sf.taverna.t2.ui.menu.items.activityport.ConnectInputPortMenuActions">
+			<property name="editManager" ref="editManager" />
+			<property name="menuManager" ref="menuManager" />
+			<property name="workbenchConfiguration" ref="workbenchConfiguration" />
+			<property name="activityIconManager" ref="activityIconManager" />
+			<property name="colourManager" ref="colourManager" />
+	</bean>
+	<bean id="ConnectOutputPortMenuActions" class="net.sf.taverna.t2.ui.menu.items.activityport.ConnectOutputPortMenuActions">
+			<property name="editManager" ref="editManager" />
+			<property name="menuManager" ref="menuManager" />
+			<property name="workbenchConfiguration" ref="workbenchConfiguration" />
+			<property name="activityIconManager" ref="activityIconManager" />
+			<property name="colourManager" ref="colourManager" />
+	</bean>
+	<bean id="CreateInputMenuAction" class="net.sf.taverna.t2.ui.menu.items.workflow.CreateInputMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="CreateOutputMenuAction" class="net.sf.taverna.t2.ui.menu.items.workflow.CreateOutputMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="EditDataflowInputPortMenuAction" class="net.sf.taverna.t2.ui.menu.items.ports.EditDataflowInputPortMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="EditDataflowOutputPortMenuAction" class="net.sf.taverna.t2.ui.menu.items.ports.EditDataflowOutputPortMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="EditSection" class="net.sf.taverna.t2.ui.menu.items.contextualviews.EditSection" />
+	<bean id="InsertSection" class="net.sf.taverna.t2.ui.menu.items.contextualviews.InsertSection" />
+	<bean id="LinkSection" class="net.sf.taverna.t2.ui.menu.items.datalink.LinkSection" />
+	<bean id="PasteMenuAction" class="net.sf.taverna.t2.ui.menu.items.contextualviews.PasteMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="menuManager" ref="menuManager" />
+			<property name="selectionManager" ref="selectionManager" />
+			<property name="serviceRegistry" ref="serviceRegistry" />
+	</bean>
+	<bean id="ProcessorSection" class="net.sf.taverna.t2.ui.menu.items.processor.ProcessorSection" />
+	<bean id="RemoveConditionMenuAction" class="net.sf.taverna.t2.ui.menu.items.controllink.RemoveConditionMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="RemoveDataflowInputPortMenuAction" class="net.sf.taverna.t2.ui.menu.items.ports.RemoveDataflowInputPortMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="RemoveDataflowOutputPortMenuAction" class="net.sf.taverna.t2.ui.menu.items.ports.RemoveDataflowOutputPortMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="RemoveLinkMenuAction" class="net.sf.taverna.t2.ui.menu.items.datalink.RemoveLinkMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="RemoveProcessorMenuAction" class="net.sf.taverna.t2.ui.menu.items.processor.RemoveProcessorMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="RenameProcessorMenuAction" class="net.sf.taverna.t2.ui.menu.items.processor.RenameProcessorMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="SetConstantInputPortValueMenuAction" class="net.sf.taverna.t2.ui.menu.items.activityport.SetConstantInputPortValueMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+			<property name="serviceRegistry" ref="serviceRegistry" />
+	</bean>
+	<bean id="ShowConfigureMenuAction" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ShowConfigureMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="menuManager" ref="menuManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="ShowDetailsContextualMenuAction" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ShowDetailsContextualMenuAction">
+			<property name="workbench" ref="workbench" />
+	</bean>
+	<bean id="ShowDetailsMenuAction" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ShowDetailsMenuAction">
+			<property name="workbench" ref="workbench" />
+	</bean>
+	<bean id="ShowReportsContextualMenuAction" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ShowReportsContextualMenuAction">
+			<property name="selectionManager" ref="selectionManager" />
+			<property name="reportManager" ref="reportManager" />
+			<property name="workbench" ref="workbench" />
+	</bean>
+	<bean id="WorkflowInputPortSection" class="net.sf.taverna.t2.ui.menu.items.ports.WorkflowInputPortSection" />
+	<bean id="WorkflowOutputPortSection" class="net.sf.taverna.t2.ui.menu.items.ports.WorkflowOutputPortSection" />
+	<bean id="WorkflowServiceTemplatesSection" class="net.sf.taverna.t2.ui.menu.items.workflow.WorkflowServiceTemplatesSection" />
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/pom.xml b/taverna-monitor-view/pom.xml
new file mode 100644
index 0000000..962367f
--- /dev/null
+++ b/taverna-monitor-view/pom.xml
@@ -0,0 +1,82 @@
+<?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>monitor-view</artifactId>
+	<packaging>bundle</packaging>
+	<name>Monitor View</name>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>graph-model</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+ 		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>results-view</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>graph-view</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.taverna.t2.lang</groupId>
+			<artifactId>ui</artifactId>
+			<version>${t2.lang.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-report-api</artifactId>
+			<version>${platform.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-run-api</artifactId>
+			<version>${platform.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>commons-beanutils</groupId>
+			<artifactId>commons-beanutils</artifactId>
+			<version>${commons.beanutils.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/MonitorViewComponent.java
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/MonitorViewComponent.java b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/MonitorViewComponent.java
new file mode 100644
index 0000000..7ebd21f
--- /dev/null
+++ b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/MonitorViewComponent.java
@@ -0,0 +1,168 @@
+
+/*******************************************************************************
+ * 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.views.monitor;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.SOUTH;
+import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
+import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED;
+import static org.apache.batik.ext.swing.GridBagConstants.EAST;
+import static org.apache.batik.ext.swing.GridBagConstants.NONE;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+
+import net.sf.taverna.t2.workbench.ui.Updatable;
+import net.sf.taverna.t2.workbench.views.monitor.graph.MonitorGraphComponent;
+import net.sf.taverna.t2.workbench.views.monitor.progressreport.TableMonitorComponent;
+
+/**
+ * Component that shows the progress of a workflow run, either through a graph or
+ * a table shown in separate tabs. For previous runs, it pulls processor and workflow
+ * statuses from provenance.
+ *
+ * Graph and table are interactive, where clicking on them triggers displaying of
+ * workflow results or intermediate results in a separate component.
+ *
+ * It also contains buttons to pause/resume and stop a workflow run.
+ *
+ */
+@SuppressWarnings({"serial","unused"})
+public class MonitorViewComponent extends JPanel implements Updatable {
+	private MonitorGraphComponent monitorGraph;
+	private TableMonitorComponent tableMonitorComponent;
+
+	private JTabbedPane tabbedPane;
+	private JPanel buttonsPanel;
+
+	public MonitorViewComponent() {
+		super(new BorderLayout());
+		tabbedPane = new JTabbedPane();
+		buttonsPanel = new JPanel(new GridBagLayout());
+
+//		buttonsPanel.add(new JLabel("Workflow status"));
+//
+//		buttonsPanel.add(new JButton("Pause"));
+//		buttonsPanel.add(new JButton("Cancel"));
+//		buttonsPanel.add(new JButton("Show results"));
+
+		add(tabbedPane, CENTER);
+		add(buttonsPanel, SOUTH);
+	}
+
+	public void setMonitorGraph(MonitorGraphComponent monitorGraph) {
+		this.monitorGraph = monitorGraph;
+		tabbedPane.add("Graph", monitorGraph);
+	}
+
+	public void setTableMonitorComponent(TableMonitorComponent tableMonitorComponent) {
+		this.tableMonitorComponent = tableMonitorComponent;
+
+		JScrollPane scrollPane = new JScrollPane(tableMonitorComponent,
+				VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
+
+		tabbedPane.add("Progress report", scrollPane);
+	}
+
+	public void addWorkflowRunStatusLabel(JLabel statusLabel){
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+
+		gbc.fill = NONE;
+		buttonsPanel.add(statusLabel, gbc);
+	}
+
+	public void addWorkflowPauseButton(JButton workflowRunPauseButton) {
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		gbc.gridx = 1;
+		gbc.gridy = 0;
+
+		gbc.fill = NONE;
+		gbc.weightx = 0.0;
+		buttonsPanel.add(workflowRunPauseButton, gbc);
+	}
+
+	public void addWorkflowCancelButton(JButton workflowRunCancelButton) {
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		gbc.gridx = 2;
+		gbc.gridy = 0;
+
+		gbc.fill = NONE;
+		gbc.weightx = 0.0;
+		buttonsPanel.add(workflowRunCancelButton, gbc);
+	}
+
+	public void addReloadWorkflowButton(JButton reloadWorkflowButton) {
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		gbc.gridx = 3;
+		gbc.gridy = 0;
+
+		gbc.fill = NONE;
+		gbc.weightx = 1.0;
+		gbc.anchor = EAST;
+		buttonsPanel.add(reloadWorkflowButton, gbc);
+	}
+
+	public void addIntermediateValuesButton(JButton intermediateValuesButton) {
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		gbc.gridx = 4;
+		gbc.gridy = 0;
+
+		gbc.fill = NONE;
+		gbc.weightx = 1.0;
+		gbc.anchor = EAST;
+		buttonsPanel.add(intermediateValuesButton, gbc);
+	}
+
+	public void addWorkflowResultsButton(JButton workflowResultsButton) {
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		gbc.gridx = 5;
+		gbc.gridy = 0;
+
+		gbc.fill = NONE;
+		gbc.weightx = 0.0;
+		gbc.anchor = EAST;
+		buttonsPanel.add(workflowResultsButton, gbc);
+	}
+
+	@Override
+	public void update() {
+		Component selectedComponent = tabbedPane.getSelectedComponent();
+		if (selectedComponent instanceof Updatable)
+			((Updatable) selectedComponent).update();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitor.java
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitor.java b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitor.java
new file mode 100644
index 0000000..ecaff3e
--- /dev/null
+++ b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitor.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * 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.views.monitor.graph;
+
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.closeIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.tickIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.workingIcon;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphController;
+import net.sf.taverna.t2.workbench.ui.Updatable;
+import uk.org.taverna.platform.report.ActivityReport;
+import uk.org.taverna.platform.report.ProcessorReport;
+import uk.org.taverna.platform.report.State;
+import uk.org.taverna.platform.report.WorkflowReport;
+
+/**
+ * An implementation of the Updatable interface that updates a Graph.
+ * 
+ * @author David Withers
+ */
+public class GraphMonitor implements Updatable {
+	private static final String STATUS_RUNNING = "Running";
+	private static final String STATUS_FINISHED = "Finished";
+	private static final String STATUS_CANCELLED = "Cancelled";
+
+	/**
+	 * Workflow run status label - we can only tell of workflow is running or is
+	 * finished from inside this monitor. If workfow run is stopped or paused -
+	 * this will be updated form the run-ui.
+	 */
+	private JLabel workflowRunStatusLabel;
+	/**
+	 * Similarly to {@link #workflowRunStatusLabel} - we disable the pause anc
+	 * cancel buttons when workflow runs is finished
+	 */
+	private JButton workflowRunPauseButton;
+	private JButton workflowRunCancelButton;
+	private GraphController graphController;
+	private Set<GraphMonitorNode> processors = new HashSet<>();
+	private final WorkflowReport workflowReport;
+
+	public GraphMonitor(GraphController graphController,
+			WorkflowReport workflowReport) {
+		this.graphController = graphController;
+		this.workflowReport = workflowReport;
+		createMonitorNodes(workflowReport.getSubject().getName(),
+				workflowReport);
+		redraw();
+	}
+
+	private void createMonitorNodes(String id, WorkflowReport workflowReport) {
+		for (ProcessorReport processorReport : workflowReport
+				.getProcessorReports()) {
+			String processorId = id + processorReport.getSubject().getName();
+			processors.add(new GraphMonitorNode(processorId, processorReport,
+					graphController));
+			for (ActivityReport activityReport : processorReport
+					.getActivityReports()) {
+				WorkflowReport nestedWorkflowReport = activityReport
+						.getNestedWorkflowReport();
+				if (nestedWorkflowReport != null)
+					createMonitorNodes(processorId, nestedWorkflowReport);
+			}
+		}
+	}
+
+	public void redraw() {
+		for (GraphMonitorNode node : processors)
+			node.redraw();
+	}
+
+	@Override
+	public void update() {
+		for (GraphMonitorNode node : processors)
+			node.update();
+		// updateState();
+	}
+
+	@SuppressWarnings("unused")
+	private void updateState() {
+		State state = workflowReport.getState();
+		switch (state) {
+		case COMPLETED:
+		case FAILED:
+			workflowRunStatusLabel.setText(STATUS_FINISHED);
+			workflowRunStatusLabel.setIcon(tickIcon);
+			workflowRunPauseButton.setEnabled(false);
+			workflowRunCancelButton.setEnabled(false);
+			break;
+		case CANCELLED:
+			workflowRunStatusLabel.setText(STATUS_CANCELLED);
+			workflowRunStatusLabel.setIcon(closeIcon);
+			workflowRunPauseButton.setEnabled(false);
+			workflowRunCancelButton.setEnabled(false);
+			break;
+		case RUNNING:
+			workflowRunStatusLabel.setText(STATUS_RUNNING);
+			workflowRunStatusLabel.setIcon(workingIcon);
+		default:
+			break;
+		}
+	}
+
+	// Set the status label that will be updated from this monitor
+	public void setWorkflowRunStatusLabel(JLabel workflowRunStatusLabel) {
+		this.workflowRunStatusLabel = workflowRunStatusLabel;
+	}
+
+	public void setWorkflowRunPauseButton(JButton workflowRunPauseButton) {
+		this.workflowRunPauseButton = workflowRunPauseButton;
+	}
+
+	public void setWorkflowRunCancelButton(JButton workflowRunCancelButton) {
+		this.workflowRunCancelButton = workflowRunCancelButton;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitorNode.java
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitorNode.java b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitorNode.java
new file mode 100644
index 0000000..8e5c441
--- /dev/null
+++ b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitorNode.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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.views.monitor.graph;
+
+import static java.lang.Math.max;
+import net.sf.taverna.t2.workbench.models.graph.GraphController;
+import uk.org.taverna.platform.report.ProcessorReport;
+
+/**
+ * A <code>MonitorNode</code> that updates a <code>Graph</code> when
+ * <code>ProcessorReport</code> property changes.
+ * 
+ * @author David Withers
+ */
+public class GraphMonitorNode {
+	private ProcessorReport processorReport;
+	private GraphController graphController;
+	private String processorId;
+	private int queueSize = 0;
+	private int sentJobs = 0;
+	private int completedJobs = 0;
+	private int errors = 0;
+
+	public GraphMonitorNode(String id, ProcessorReport processorReport,
+			GraphController graphController) {
+		this.processorReport = processorReport;
+		this.graphController = graphController;
+		processorId = id;
+	}
+
+	/**
+	 * Updates the <code>Graph</code> when changes to properties are detected.
+	 */
+	public void update() {
+		synchronized (graphController) {
+			boolean queueSizeChanged = false;
+			boolean sentJobsChanged = false;
+			boolean completedJobsChanged = false;
+			boolean errorsChanged = false;
+
+			int newQueueSize = processorReport.getJobsQueued();
+			newQueueSize = newQueueSize == -1 ? 0 : newQueueSize;
+			if (queueSize != newQueueSize) {
+				queueSize = newQueueSize;
+				queueSizeChanged = true;
+			}
+
+			int newSentJobs = processorReport.getJobsStarted();
+			if (sentJobs != newSentJobs) {
+				sentJobs = newSentJobs;
+				sentJobsChanged = true;
+			}
+
+			int newCompletedJobs = processorReport.getJobsCompleted();
+			if (completedJobs != newCompletedJobs) {
+				completedJobs = newCompletedJobs;
+				completedJobsChanged = true;
+			}
+
+			int newErrors = processorReport.getJobsCompletedWithErrors();
+			if (errors != newErrors) {
+				errors = newErrors;
+				errorsChanged = true;
+			}
+
+			if (queueSizeChanged || sentJobsChanged || completedJobsChanged
+					|| errorsChanged) {
+				if (completedJobsChanged)
+					graphController.setIteration(processorId, completedJobs);
+				if (completedJobs > 0)
+					graphController.setNodeCompleted(processorId,
+							(completedJobs / (float) (sentJobs + queueSize)));
+				if (sentJobsChanged) {
+					// graphController.setEdgeActive(processorId, true);
+				}
+				if (errorsChanged && errors > 0)
+					graphController.setErrors(processorId, errors);
+			}
+		}
+	}
+
+	public void redraw() {
+		synchronized (graphController) {
+			queueSize = max(processorReport.getJobsQueued(), 0);
+			sentJobs = processorReport.getJobsStarted();
+			completedJobs = processorReport.getJobsCompleted();
+			errors = processorReport.getJobsCompletedWithErrors();
+
+			graphController.setIteration(processorId, completedJobs);
+			if (completedJobs > 0)
+				graphController.setNodeCompleted(processorId,
+						(completedJobs / (float) (sentJobs + queueSize)));
+			if (errors > 0)
+				graphController.setErrors(processorId, errors);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/MonitorGraphComponent.java
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/MonitorGraphComponent.java b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/MonitorGraphComponent.java
new file mode 100644
index 0000000..0be66ff
--- /dev/null
+++ b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/MonitorGraphComponent.java
@@ -0,0 +1,378 @@
+/*******************************************************************************
+ * 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.views.monitor.graph;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static javax.swing.Action.SHORT_DESCRIPTION;
+import static javax.swing.Action.SMALL_ICON;
+import static javax.swing.BoxLayout.PAGE_AXIS;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.refreshIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomInIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomOutIcon;
+import static org.apache.batik.swing.svg.AbstractJSVGComponent.ALWAYS_DYNAMIC;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.Action;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JToolBar;
+import javax.swing.Timer;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
+import net.sf.taverna.t2.workbench.models.graph.GraphElement;
+import net.sf.taverna.t2.workbench.models.graph.GraphEventManager;
+import net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphController;
+import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.Updatable;
+import net.sf.taverna.t2.workbench.views.graph.AutoScrollInteractor;
+import net.sf.taverna.t2.workbench.views.graph.menu.ResetDiagramAction;
+import net.sf.taverna.t2.workbench.views.graph.menu.ZoomInAction;
+import net.sf.taverna.t2.workbench.views.graph.menu.ZoomOutAction;
+
+import org.apache.batik.swing.JSVGCanvas;
+import org.apache.batik.swing.JSVGScrollPane;
+import org.apache.batik.swing.gvt.GVTTreeRendererAdapter;
+import org.apache.batik.swing.gvt.GVTTreeRendererEvent;
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.platform.run.api.InvalidRunIdException;
+import uk.org.taverna.platform.run.api.RunService;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.WorkflowPort;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+/**
+ * Use to display the graph for fresh workflow runs and allow the user to click
+ * on processors to see the intermediate results for processors pulled from
+ * provenance.
+ */
+@SuppressWarnings("serial")
+public class MonitorGraphComponent extends JPanel implements Updatable {
+	private static Logger logger = Logger.getLogger(MonitorGraphComponent.class);
+
+	private SVGGraphController graphController;
+	private JPanel diagramPanel;
+	private GraphMonitor graphMonitor;
+
+	private Map<String, SVGGraphController> graphControllerMap = new HashMap<>();
+	private Map<String, GraphMonitor> graphMonitorMap = new HashMap<>();
+	private Map<String, JPanel> diagramPanelMap = new HashMap<>();
+	private Map<String, Action[]> diagramActionsMap = new HashMap<>();
+
+	@SuppressWarnings("unused")
+	private Timer timer;
+	private CardLayout cardLayout;
+	@SuppressWarnings("unused")
+	private JLabel statusLabel;
+
+	private final RunService runService;
+	private final ColourManager colourManager;
+	private final WorkbenchConfiguration workbenchConfiguration;
+	private final SelectionManager selectionManager;
+
+	public MonitorGraphComponent(RunService runService, ColourManager colourManager,
+			WorkbenchConfiguration workbenchConfiguration, SelectionManager selectionManager) {
+		this.runService = runService;
+		this.colourManager = colourManager;
+		this.workbenchConfiguration = workbenchConfiguration;
+		this.selectionManager = selectionManager;
+
+		cardLayout = new CardLayout();
+		setLayout(cardLayout);
+
+//		ActionListener taskPerformer = new ActionListener() {
+//			public void actionPerformed(ActionEvent evt) {
+//				if (graphController != null) {
+//					graphController.redraw();
+//					graphMonitor.redraw();
+//				}
+//				timer.stop();
+//			}
+//		};
+//		timer = new Timer(100, taskPerformer);
+//
+//		addComponentListener(new ComponentAdapter() {
+//			public void componentResized(ComponentEvent e) {
+//				if (timer.isRunning()) {
+//					timer.restart();
+//				} else {
+//					timer.start();
+//				}
+//			}
+//		});
+
+	}
+
+	@Override
+	protected void finalize() throws Throwable {
+		if (graphController != null)
+			graphController.shutdown();
+	}
+
+	@Override
+	public void update() {
+		if (graphMonitor != null)
+			graphMonitor.update();
+	}
+
+	private JPanel createDiagramPanel(String workflowRun) {
+		final JPanel diagramPanel = new JPanel(new BorderLayout());
+
+		try {
+			Workflow workflow = runService.getWorkflow(workflowRun);
+			Profile profile = runService.getProfile(workflowRun);
+
+			// get the default diagram settings
+			// Alignment alignment = Alignment.valueOf(graphViewConfiguration
+			// .getProperty(GraphViewConfiguration.ALIGNMENT));
+			// PortStyle portStyle = PortStyle.valueOf(graphViewConfiguration
+			// .getProperty(GraphViewConfiguration.PORT_STYLE));
+
+			// create an SVG canvas
+			final JSVGCanvas svgCanvas = new JSVGCanvas(null, true, false);
+			svgCanvas.setEnableZoomInteractor(false);
+			svgCanvas.setEnableRotateInteractor(false);
+			svgCanvas.setDocumentState(ALWAYS_DYNAMIC);
+
+			AutoScrollInteractor asi = new AutoScrollInteractor(svgCanvas);
+			svgCanvas.addMouseListener(asi);
+			svgCanvas.addMouseMotionListener(asi);
+
+			final JSVGScrollPane svgScrollPane = new MySvgScrollPane(svgCanvas);
+
+			GVTTreeRendererAdapter gvtTreeRendererAdapter = new GVTTreeRendererAdapter() {
+				@Override
+				public void gvtRenderingCompleted(GVTTreeRendererEvent e) {
+					logger.info("Rendered svg");
+//					svgScrollPane.reset();
+//					diagramPanel.revalidate();
+				}
+			};
+			svgCanvas.addGVTTreeRendererListener(gvtTreeRendererAdapter);
+
+			// create a graph controller
+			SVGGraphController svgGraphController = new SVGGraphController(
+					workflow, profile, true, svgCanvas, null, null,
+					colourManager, workbenchConfiguration);
+			DataflowSelectionModel selectionModel = selectionManager
+					.getWorkflowRunSelectionModel(workflowRun);
+			svgGraphController.setDataflowSelectionModel(selectionModel);
+			svgGraphController
+					.setGraphEventManager(new MonitorGraphEventManager(
+							selectionModel));
+
+			graphControllerMap.put(workflowRun, svgGraphController);
+
+			// Toolbar with actions related to graph
+			JToolBar graphActionsToolbar = graphActionsToolbar(workflowRun, svgCanvas);
+			graphActionsToolbar.setAlignmentX(LEFT_ALIGNMENT);
+			graphActionsToolbar.setFloatable(false);
+
+			// Panel to hold the toolbars
+			JPanel toolbarPanel = new JPanel();
+			toolbarPanel.setLayout(new BoxLayout(toolbarPanel, PAGE_AXIS));
+			toolbarPanel.add(graphActionsToolbar);
+
+			diagramPanel.add(toolbarPanel, NORTH);
+			diagramPanel.add(svgScrollPane, CENTER);
+
+			// JTextField workflowHierarchy = new JTextField(workflow.getName());
+			// diagramPanel.add(workflowHierarchy, BorderLayout.SOUTH);
+		} catch (InvalidRunIdException e) {
+			diagramPanel.add(new JLabel("Workflow run ID invalid", JLabel.CENTER),
+					CENTER);
+		}
+		return diagramPanel;
+	}
+
+	protected JToolBar graphActionsToolbar(String workflowRun, JSVGCanvas svgCanvas) {
+		JToolBar toolBar = new JToolBar();
+		toolBar.setAlignmentX(LEFT_ALIGNMENT);
+		toolBar.setFloatable(false);
+
+		JButton resetDiagramButton = new JButton();
+		resetDiagramButton.setBorder(new EmptyBorder(0, 2, 0, 2));
+		JButton zoomInButton = new JButton();
+		zoomInButton.setBorder(new EmptyBorder(0, 2, 0, 2));
+		JButton zoomOutButton = new JButton();
+		zoomOutButton.setBorder(new EmptyBorder(0, 2, 0, 2));
+
+		Action resetDiagramAction = svgCanvas.new ResetTransformAction();
+		ResetDiagramAction.setResultsAction(resetDiagramAction);
+		resetDiagramAction.putValue(SHORT_DESCRIPTION, "Reset Diagram");
+		resetDiagramAction.putValue(SMALL_ICON, refreshIcon);
+		resetDiagramButton.setAction(resetDiagramAction);
+
+		Action zoomInAction = svgCanvas.new ZoomAction(1.2);
+		ZoomInAction.setResultsAction(zoomInAction);
+		zoomInAction.putValue(SHORT_DESCRIPTION, "Zoom In");
+		zoomInAction.putValue(SMALL_ICON, zoomInIcon);
+		zoomInButton.setAction(zoomInAction);
+
+		Action zoomOutAction = svgCanvas.new ZoomAction(1 / 1.2);
+		ZoomOutAction.setResultsAction(zoomOutAction);
+		zoomOutAction.putValue(SHORT_DESCRIPTION, "Zoom Out");
+		zoomOutAction.putValue(SMALL_ICON, zoomOutIcon);
+		zoomOutButton.setAction(zoomOutAction);
+
+		// diagramActionsMap.put(workflowRun, new Action[] { resetDiagramAction, zoomInAction,
+		// zoomOutAction });
+
+		toolBar.add(resetDiagramButton);
+		toolBar.add(zoomInButton);
+		toolBar.add(zoomOutButton);
+
+		return toolBar;
+	}
+
+	// public void setStatus(Status status) {
+	// switch (status) {
+	// case RUNNING :
+	// statusLabel.setText("Workflow running");
+	// statusLabel.setIcon(WorkbenchIcons.workingIcon);
+	// if (workflow != null){ // should not be null really, workflow should be set before this
+	// method is called
+	// workflow.setIsRunning(true);
+	// }
+	// break;
+	// case FINISHED :
+	// statusLabel.setText("Workflow finished");
+	// statusLabel.setIcon(WorkbenchIcons.tickIcon);
+	// if (workflow != null){// should not be null really, workflow should be set before this method
+	// is called
+	// workflow.setIsRunning(false);
+	// }
+	// break;
+	// }
+	// }
+
+	public void setWorkflowRun(String workflowRun) throws InvalidRunIdException {
+		if (workflowRun != null) {
+			if (!diagramPanelMap.containsKey(workflowRun))
+				addWorkflowRun(workflowRun);
+			graphController = graphControllerMap.get(workflowRun);
+			diagramPanel = diagramPanelMap.get(workflowRun);
+			graphMonitor = graphMonitorMap.get(workflowRun);
+			Action[] actions = diagramActionsMap.get(workflowRun);
+			if (actions != null && actions.length == 3) {
+				ResetDiagramAction.setDesignAction(actions[0]);
+				ZoomInAction.setDesignAction(actions[1]);
+				ZoomOutAction.setDesignAction(actions[2]);
+			}
+			cardLayout.show(this, String.valueOf(diagramPanel.hashCode()));
+			// graphController.redraw();
+		}
+	}
+
+	public void addWorkflowRun(String workflowRun) throws InvalidRunIdException {
+		JPanel newDiagramPanel = createDiagramPanel(workflowRun);
+		add(newDiagramPanel, String.valueOf(newDiagramPanel.hashCode()));
+		diagramPanelMap.put(workflowRun, newDiagramPanel);
+		graphMonitorMap.put(workflowRun,
+				new GraphMonitor(graphControllerMap.get(workflowRun),
+						runService.getWorkflowReport(workflowRun)));
+	}
+
+	public void removeWorkflowRun(String workflowRun) {
+		JPanel removedDiagramPanel = diagramPanelMap.remove(workflowRun);
+		if (removedDiagramPanel != null)
+			remove(removedDiagramPanel);
+		SVGGraphController removedController = graphControllerMap
+				.remove(workflowRun);
+		if (removedController != null)
+			removedController.shutdown();
+		graphMonitorMap.remove(workflowRun);
+		diagramActionsMap.remove(workflowRun);
+	}
+
+	private class MonitorGraphEventManager implements GraphEventManager {
+		private final DataflowSelectionModel selectionModel;
+
+		public MonitorGraphEventManager(DataflowSelectionModel selectionModel) {
+			this.selectionModel = selectionModel;
+		}
+
+		@Override
+		public void mouseClicked(final GraphElement graphElement, short button,
+				boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y,
+				int screenX, int screenY) {
+			Object workflowObject = graphElement.getWorkflowBean();
+			if (workflowObject instanceof Processor
+					|| workflowObject instanceof WorkflowPort)
+				selectionModel.addSelection(workflowObject);
+		}
+
+		@Override
+		public void mouseDown(GraphElement graphElement, short button,
+				boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y,
+				int screenX, int screenY) {
+		}
+
+		@Override
+		public void mouseMoved(GraphElement graphElement, short button,
+				boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y,
+				int screenX, int screenY) {
+		}
+
+		@Override
+		public void mouseUp(GraphElement graphElement, short button,
+				boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y,
+				int screenX, int screenY) {
+		}
+
+		@Override
+		public void mouseOut(GraphElement graphElement, short button,
+				boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y,
+				int screenX, int screenY) {
+		}
+
+		@Override
+		public void mouseOver(GraphElement graphElement, short button,
+				boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y,
+				int screenX, int screenY) {
+		}
+	}
+
+	private class MySvgScrollPane extends JSVGScrollPane {
+		private static final long serialVersionUID = 6890422410714378543L;
+
+		public MySvgScrollPane(JSVGCanvas canvas) {
+			super(canvas);
+		}
+
+		@Override
+		public void reset() {
+			super.resizeScrollBars();
+			super.reset();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/TableMonitorComponent.java
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/TableMonitorComponent.java b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/TableMonitorComponent.java
new file mode 100644
index 0000000..bb82421
--- /dev/null
+++ b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/TableMonitorComponent.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (C) 2013 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.views.monitor.progressreport;
+
+import java.awt.CardLayout;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.Updatable;
+import uk.org.taverna.platform.report.WorkflowReport;
+import uk.org.taverna.platform.run.api.InvalidRunIdException;
+import uk.org.taverna.platform.run.api.RunService;
+
+/**
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class TableMonitorComponent extends JPanel implements Updatable {
+	private Map<String, WorkflowRunProgressTreeTable> tableMap = new HashMap<>();
+	private Map<String, WorkflowRunProgressTreeTableModel> tableModelMap = new HashMap<>();
+	private WorkflowRunProgressTreeTable table;
+	private WorkflowRunProgressTreeTableModel tableModel;
+	private CardLayout cardLayout;
+
+	private final RunService runService;
+	private final SelectionManager selectionManager;
+	private final ActivityIconManager activityIconManager;
+
+	public TableMonitorComponent(RunService runService,
+			SelectionManager selectionManager,
+			ActivityIconManager activityIconManager) {
+		this.runService = runService;
+		this.selectionManager = selectionManager;
+		this.activityIconManager = activityIconManager;
+
+		cardLayout = new CardLayout();
+		setLayout(cardLayout);
+	}
+
+	public void setWorkflowRun(String workflowRun) throws InvalidRunIdException {
+		if (workflowRun != null) {
+			if (!tableMap.containsKey(workflowRun))
+				addWorkflowRun(workflowRun);
+			table = tableMap.get(workflowRun);
+			tableModel = tableModelMap.get(workflowRun);
+			cardLayout.show(this, String.valueOf(table.hashCode()));
+		}
+	}
+
+	public void addWorkflowRun(String workflowRun) throws InvalidRunIdException {
+		WorkflowReport workflowReport = runService
+				.getWorkflowReport(workflowRun);
+		WorkflowRunProgressTreeTableModel newTableModel = new WorkflowRunProgressTreeTableModel(
+				workflowReport);
+		WorkflowRunProgressTreeTable newTable = new WorkflowRunProgressTreeTable(
+				newTableModel, activityIconManager,
+				selectionManager.getWorkflowRunSelectionModel(workflowRun));
+
+		add(new JScrollPane(newTable), String.valueOf(newTable.hashCode()));
+		tableMap.put(workflowRun, newTable);
+		tableModelMap.put(workflowRun, newTableModel);
+	}
+
+	public void removeWorkflowRun(String workflowRun) {
+		WorkflowRunProgressTreeTable removedTable = tableMap
+				.remove(workflowRun);
+		if (removedTable != null)
+			remove(removedTable);
+		tableModelMap.remove(workflowRun);
+	}
+
+	@Override
+	public void update() {
+		if (tableModel != null)
+			tableModel.update();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeCellRenderer.java b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeCellRenderer.java
new file mode 100644
index 0000000..4c4d6ad
--- /dev/null
+++ b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeCellRenderer.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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.views.monitor.progressreport;
+
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.workflowExplorerIcon;
+
+import java.awt.Component;
+import java.util.Set;
+
+import javax.swing.Icon;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+
+import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager;
+import uk.org.taverna.platform.report.ActivityReport;
+import uk.org.taverna.platform.report.ProcessorReport;
+import uk.org.taverna.platform.report.WorkflowReport;
+
+/**
+ * Cell renderer for Workflow Explorer tree.
+ *
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class WorkflowRunProgressTreeCellRenderer extends DefaultTreeCellRenderer {
+	private ActivityIconManager activityIconManager;
+
+	public WorkflowRunProgressTreeCellRenderer(ActivityIconManager activityIconManager) {
+		this.activityIconManager = activityIconManager;
+	}
+
+	@Override
+	public Component getTreeCellRendererComponent(JTree tree, Object value,
+			boolean sel, boolean expanded, boolean leaf, int row,
+			boolean hasFocus) {
+		Component result = super.getTreeCellRendererComponent(tree, value, sel,
+				expanded, leaf, row, hasFocus);
+
+		Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
+
+		WorkflowRunProgressTreeCellRenderer renderer = (WorkflowRunProgressTreeCellRenderer) result;
+
+		if (userObject instanceof WorkflowReport) // the root node
+			renderWorkflowReport(renderer, (WorkflowReport) userObject);
+		else if (userObject instanceof ProcessorReport)
+			renderProcessorReport(renderer, (ProcessorReport) userObject);
+
+		return result;
+	}
+
+	private void renderWorkflowReport(
+			WorkflowRunProgressTreeCellRenderer renderer,
+			WorkflowReport workflowReport) {
+		renderer.setIcon(workflowExplorerIcon);
+		renderer.setText(workflowReport.getSubject().getName());
+	}
+
+	private void renderProcessorReport(WorkflowRunProgressTreeCellRenderer renderer,
+			ProcessorReport processorReport) {
+		/*
+		 * Get the activity associated with the processor - currently only
+		 * one gets displayed
+		 */
+		Set<ActivityReport> activityReports = processorReport
+				.getActivityReports();
+		String text = processorReport.getSubject().getName();
+		if (!activityReports.isEmpty()) {
+			ActivityReport activityReport = activityReports.iterator()
+					.next();
+			Icon icon = activityIconManager.iconForActivity(activityReport
+					.getSubject());
+			if (icon != null)
+				renderer.setIcon(icon);
+		}
+		renderer.setText(text);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTable.java
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTable.java b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTable.java
new file mode 100644
index 0000000..f1f031c
--- /dev/null
+++ b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTable.java
@@ -0,0 +1,112 @@
+package net.sf.taverna.t2.workbench.views.monitor.progressreport;
+
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.lang.ui.treetable.JTreeTable;
+import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager;
+import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
+import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage;
+import uk.org.taverna.platform.report.StatusReport;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.WorkflowPort;
+
+@SuppressWarnings("serial")
+public class WorkflowRunProgressTreeTable extends JTreeTable {
+	private final WorkflowRunProgressTreeTableModel treeTableModel;
+	private final DataflowSelectionModel selectionModel;
+	private final DataflowSelectionObserver dataflowSelectionObserver;
+
+	public WorkflowRunProgressTreeTable(
+			WorkflowRunProgressTreeTableModel treeTableModel,
+			ActivityIconManager activityIconManager,
+			DataflowSelectionModel selectionModel) {
+		super(treeTableModel);
+
+		this.treeTableModel = treeTableModel;
+		this.selectionModel = selectionModel;
+
+		this.tree.setCellRenderer(new WorkflowRunProgressTreeCellRenderer(
+				activityIconManager));
+		this.tree.setEditable(false);
+		this.tree.setExpandsSelectedPaths(true);
+		this.tree.setDragEnabled(false);
+		this.tree.setScrollsOnExpand(false);
+
+		getTableHeader().setReorderingAllowed(false);
+		getSelectionModel().setSelectionMode(
+				ListSelectionModel.SINGLE_SELECTION);
+		getSelectionModel().addListSelectionListener(
+				new TableSelectionListener());
+
+		dataflowSelectionObserver = new DataflowSelectionObserver();
+		selectionModel.addObserver(dataflowSelectionObserver);
+	}
+
+	@Override
+	protected void finalize() throws Throwable {
+		selectionModel.removeObserver(dataflowSelectionObserver);
+	}
+
+	/**
+	 * Return object in the tree part of this JTreeTable that corresponds to
+	 * this row. It will either be a workflow (tree root) or a processor.
+	 */
+	public Object getTreeObjectForRow(int row) {
+		TreePath path = tree.getPathForRow(row);
+		if (path == null)
+			return null;
+		return ((DefaultMutableTreeNode) path.getLastPathComponent())
+				.getUserObject();
+	}
+
+	public void setSelectedRowForObject(Object workflowObject) {
+		// Find the row for the object in the tree
+		DefaultMutableTreeNode node = treeTableModel.getNodeForObject(workflowObject);
+		if (node != null) {
+			TreeNode[] path = node.getPath();
+			tree.scrollPathToVisible(new TreePath(path));
+			int row = tree.getRowForPath(new TreePath(path));
+			if (row >= 0)
+				// Set selected row on the table
+				setRowSelectionInterval(row, row);
+		}
+	}
+
+	private class DataflowSelectionObserver extends SwingAwareObserver<DataflowSelectionMessage> {
+		@Override
+		public void notifySwing(Observable<DataflowSelectionMessage> sender,
+				DataflowSelectionMessage message) {
+			for (Object selection : selectionModel.getSelection()) {
+				if (selection instanceof Processor
+						|| selection instanceof Workflow)
+					setSelectedRowForObject(selection);
+				else if (selection instanceof WorkflowPort)
+					setSelectedRowForObject(((WorkflowPort) selection)
+							.getParent());
+			}
+		}
+	}
+
+	private class TableSelectionListener implements ListSelectionListener {
+		@Override
+		public void valueChanged(ListSelectionEvent e) {
+			if (e.getValueIsAdjusting())
+				return;
+			int selectedRow = getSelectedRow();
+			if (selectedRow < 0)
+				return;
+			Object selection = getTreeObjectForRow(selectedRow);
+			if (selection instanceof StatusReport)
+				selectionModel.addSelection(((StatusReport<?, ?>) selection)
+						.getSubject());
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTableModel.java
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTableModel.java b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTableModel.java
new file mode 100644
index 0000000..1e2e6e9
--- /dev/null
+++ b/taverna-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTableModel.java
@@ -0,0 +1,279 @@
+/*******************************************************************************
+ * 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.views.monitor.progressreport;
+
+import static java.util.Collections.nCopies;
+import static net.sf.taverna.t2.workbench.views.monitor.progressreport.WorkflowRunProgressTreeTableModel.Column.values;
+import static net.sf.taverna.t2.workbench.views.results.processor.ProcessorResultsComponent.formatMilliseconds;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+
+import net.sf.taverna.t2.lang.ui.treetable.AbstractTreeTableModel;
+import net.sf.taverna.t2.lang.ui.treetable.TreeTableModel;
+import uk.org.taverna.platform.report.ActivityReport;
+import uk.org.taverna.platform.report.Invocation;
+import uk.org.taverna.platform.report.ProcessorReport;
+import uk.org.taverna.platform.report.State;
+import uk.org.taverna.platform.report.StatusReport;
+import uk.org.taverna.platform.report.WorkflowReport;
+
+/**
+ * A TreeTableModel used to display the progress of a workfow run. The workflow
+ * and its processors (some of which may be nested) are represented as a tree,
+ * where their properties, such as status, start and finish times, number of
+ * iterations, etc. are represented as table columns.
+ * 
+ * @author Alex Nenadic
+ * @author Stian Soiland-Reyes
+ * @author David Withers
+ */
+public class WorkflowRunProgressTreeTableModel extends AbstractTreeTableModel {
+	public static final String NAME = "Name";
+	public static final String STATUS = "Status";
+	public static final String AVERAGE_ITERATION_TIME = "Average time per iteration";
+	public static final String ITERATIONS = "Queued iterations";
+	public static final String ITERATIONS_DONE = "Completed iterations";
+	public static final String ITERATIONS_FAILED = "Iterations with errors";
+
+	public enum Column {
+		NAME("Name", TreeTableModel.class), STATUS("Status"), ITERATIONS_QUEUED(
+				"Queued iterations"), ITERATIONS_DONE("Iterations done"), ITERATIONS_FAILED(
+				"Iterations w/errors"), AVERAGE_ITERATION_TIME(
+				"Average time/iteration"), START_TIME(
+				"First iteration started", Date.class), FINISH_TIME(
+				"Last iteration ended", Date.class);
+
+		private final String label;
+		private final Class<?> columnClass;
+
+		Column(String label) {
+			this(label, String.class);
+		}
+
+		Column(String label, Class<?> columnClass) {
+			this.label = label;
+			this.columnClass = columnClass;
+		}
+
+		public Class<?> getColumnClass() {
+			return columnClass;
+		}
+
+		public String getLabel() {
+			return label;
+		}
+
+		@Override
+		public String toString() {
+			return label;
+		}
+	}
+
+	// Table data (maps workflow element nodes to column data associated with them)
+	private final Map<DefaultMutableTreeNode, List<Object>> data = new HashMap<>();
+	private final Map<Object, DefaultMutableTreeNode> nodeForObject = new HashMap<>();
+	private final DefaultMutableTreeNode rootNode;
+
+	public WorkflowRunProgressTreeTableModel(WorkflowReport workflowReport) {
+		super(new DefaultMutableTreeNode(workflowReport));
+		rootNode = (DefaultMutableTreeNode) this.getRoot();
+		createTree(workflowReport, rootNode);
+	}
+
+	private void createTree(WorkflowReport workflowReport, DefaultMutableTreeNode root) {
+		// If this is the root of the tree rather than a root of the nested sub-tree
+		if (root.equals(rootNode)) {
+			List<Object> columnData = new ArrayList<>(nCopies(values().length,
+					null));
+			setColumnValues(workflowReport, columnData);
+			nodeForObject.put(workflowReport, root);
+			nodeForObject.put(workflowReport.getSubject(), root);
+			data.put(root, columnData);
+		}
+		// One row for each processor
+		for (ProcessorReport processorReport : workflowReport.getProcessorReports()) {
+			List<Object> columnData = new ArrayList<>(nCopies(values().length,
+					null));
+			DefaultMutableTreeNode processorNode = new DefaultMutableTreeNode(
+					processorReport);
+			setColumnValues(processorReport, columnData);
+			nodeForObject.put(processorReport, processorNode);
+			nodeForObject.put(processorReport.getSubject(), processorNode);
+			data.put(processorNode, columnData);
+			root.add(processorNode);
+
+			Set<ActivityReport> activityReports = processorReport.getActivityReports();
+			if (activityReports.size() == 1) {
+				WorkflowReport nestedWorkflowReport = activityReports.iterator().next()
+						.getNestedWorkflowReport();
+				if (nestedWorkflowReport != null)
+					// create sub-tree
+					createTree(nestedWorkflowReport, processorNode);
+			}
+		}
+	}
+
+	public DefaultMutableTreeNode getNodeForObject(Object workflowObject) {
+		return nodeForObject.get(workflowObject);
+	}
+
+	public void setColumnValues(StatusReport<?, ?> report, List<Object> columns) {
+		if (report instanceof WorkflowReport) {
+			WorkflowReport workflowReport = (WorkflowReport) report;
+
+			State state = workflowReport.getState();
+			Date startTime = workflowReport.getStartedDate();
+			Date finishTime = workflowReport.getCompletedDate();
+
+			columns.set(Column.NAME.ordinal(), workflowReport.getSubject().getName());
+			columns.set(Column.STATUS.ordinal(), state);
+			columns.set(Column.ITERATIONS_DONE.ordinal(), "-");
+			columns.set(Column.ITERATIONS_FAILED.ordinal(), "-");
+			columns.set(Column.ITERATIONS_QUEUED.ordinal(), "-");
+			columns.set(Column.START_TIME.ordinal(), startTime);
+			columns.set(Column.FINISH_TIME.ordinal(), finishTime);
+			if (startTime != null && finishTime != null)
+				columns.set(Column.AVERAGE_ITERATION_TIME.ordinal(),
+						formatMilliseconds(finishTime.getTime() - finishTime.getTime()));
+			else
+				columns.set(Column.AVERAGE_ITERATION_TIME.ordinal(), "-");
+		} else if (report instanceof ProcessorReport) {
+			ProcessorReport processorReport = (ProcessorReport) report;
+
+			State state = processorReport.getState();
+			SortedSet<Invocation> invocations = processorReport
+					.getInvocations();
+
+			columns.set(Column.NAME.ordinal(), processorReport.getSubject()
+					.getName());
+			columns.set(Column.STATUS.ordinal(), state);
+			columns.set(Column.ITERATIONS_QUEUED.ordinal(),
+					processorReport.getJobsQueued());
+			columns.set(Column.ITERATIONS_DONE.ordinal(),
+					processorReport.getJobsCompleted());
+			columns.set(Column.ITERATIONS_FAILED.ordinal(),
+					processorReport.getJobsCompletedWithErrors());
+
+			if (invocations.isEmpty()) {
+				columns.set(Column.START_TIME.ordinal(), null);
+				columns.set(Column.FINISH_TIME.ordinal(), null);
+				columns.set(Column.AVERAGE_ITERATION_TIME.ordinal(), null); // iteration
+			} else {
+				Date earliestStartTime = invocations.first().getStartedDate();
+				Date latestFinishTime = invocations.first().getCompletedDate();
+				long totalInvocationTime = 0;
+				int finishedInvocations = 0;
+
+				for (Invocation invocation : invocations) {
+					// Get the earliest start time of all invocations
+					Date startTime = invocation.getStartedDate();
+					if (startTime != null) {
+						if (startTime.before(earliestStartTime))
+							earliestStartTime = startTime;
+						// Get the latest finish time of all invocations
+						Date finishTime = invocation.getCompletedDate();
+						if (finishTime != null) {
+							if (finishTime.after(latestFinishTime)) {
+								latestFinishTime = finishTime;
+								totalInvocationTime += finishTime.getTime() - startTime.getTime();
+							}
+							finishedInvocations++;
+						}
+					}
+				}
+
+				columns.set(Column.START_TIME.ordinal(), earliestStartTime);
+				columns.set(Column.FINISH_TIME.ordinal(), latestFinishTime);
+				if (finishedInvocations > 0) {
+					long averageTime = totalInvocationTime / finishedInvocations;
+					columns.set(Column.AVERAGE_ITERATION_TIME.ordinal(),
+							formatMilliseconds(averageTime));
+				} else
+					columns.set(Column.AVERAGE_ITERATION_TIME.ordinal(), "-");
+			}
+		}
+	}
+
+	public void update() {
+		update(rootNode);
+		fireTreeNodesChanged(rootNode, rootNode.getPath(), null, null);
+	}
+
+	private void update(DefaultMutableTreeNode node) {
+		setColumnValues((StatusReport<?, ?>) node.getUserObject(), data.get(node));
+		for (int i = 0; i < node.getChildCount(); i++)
+			update((DefaultMutableTreeNode) node.getChildAt(i));
+	}
+
+	//
+	// The TreeModel interface
+	//
+
+	@Override
+	public int getChildCount(Object node) {
+		return ((TreeNode) node).getChildCount();
+	}
+
+	@Override
+	public Object getChild(Object node, int i) {
+		return ((TreeNode) node).getChildAt(i);
+	}
+
+	//
+	// The TreeTableNode interface.
+	//
+
+	@Override
+	public int getColumnCount() {
+		return values().length;
+	}
+
+	@Override
+	public String getColumnName(int column) {
+		return values()[column].getLabel();
+	}
+
+	@Override
+	public Class<?> getColumnClass(int column) {
+		return values()[column].getColumnClass();
+	}
+
+	public Object getValueAt(Object node, Column column) {
+		return getValueAt(node, column.ordinal());
+	}
+
+	@Override
+	public Object getValueAt(Object node, int column) {
+		List<Object> columnValues = data.get(node);
+		if (columnValues == null)
+			return null;
+		return columnValues.get(column);
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI b/taverna-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
new file mode 100644
index 0000000..cf13a46
--- /dev/null
+++ b/taverna-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.views.monitor.graph.MonitorGraphComponentFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI b/taverna-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
new file mode 100644
index 0000000..30f1743
--- /dev/null
+++ b/taverna-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.views.monitor.graph.MonitorGraphComponent
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/resources/META-INF/spring/monitor-view-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/resources/META-INF/spring/monitor-view-context-osgi.xml b/taverna-monitor-view/src/main/resources/META-INF/spring/monitor-view-context-osgi.xml
new file mode 100644
index 0000000..ab22b97
--- /dev/null
+++ b/taverna-monitor-view/src/main/resources/META-INF/spring/monitor-view-context-osgi.xml
@@ -0,0 +1,9 @@
+<?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">
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-monitor-view/src/main/resources/META-INF/spring/monitor-view-context.xml
----------------------------------------------------------------------
diff --git a/taverna-monitor-view/src/main/resources/META-INF/spring/monitor-view-context.xml b/taverna-monitor-view/src/main/resources/META-INF/spring/monitor-view-context.xml
new file mode 100644
index 0000000..d662d87
--- /dev/null
+++ b/taverna-monitor-view/src/main/resources/META-INF/spring/monitor-view-context.xml
@@ -0,0 +1,6 @@
+<?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">
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-parallelize-ui/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-parallelize-ui/pom.xml b/taverna-parallelize-ui/pom.xml
new file mode 100644
index 0000000..e74c474
--- /dev/null
+++ b/taverna-parallelize-ui/pom.xml
@@ -0,0 +1,57 @@
+<?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>parallelize-ui</artifactId>
+	<packaging>bundle</packaging>
+	<name>Parallelize 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>menu-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>selection-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>${jackson-databind.version}</version>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigurationPanel.java b/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigurationPanel.java
new file mode 100644
index 0000000..f951da6
--- /dev/null
+++ b/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigurationPanel.java
@@ -0,0 +1,99 @@
+package net.sf.taverna.t2.workbench.parallelize;
+
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.border.EmptyBorder;
+
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+@SuppressWarnings("serial")
+public class ParallelizeConfigurationPanel extends JPanel {
+
+	private ObjectNode json;
+	private JTextField maxJobsField = new JTextField(10);
+	private final String processorName;
+
+	public ParallelizeConfigurationPanel(Configuration configuration, String processorName) {
+		if (configuration.getJson().has("parallelize")) {
+			json = (ObjectNode) configuration.getJson().get("parallelize").deepCopy();
+		} else {
+			json = configuration.getJsonAsObjectNode().objectNode();
+		}
+		this.processorName = processorName;
+		this.setLayout(new GridBagLayout());
+		this.setBorder(new EmptyBorder(10,10,10,10));
+		populate();
+	}
+
+	public void populate() {
+		this.removeAll();
+		GridBagConstraints gbc = new GridBagConstraints();
+		JLabel jobs = new JLabel("<html><body>Maximum numbers of items to process at the same time</body></html>");
+
+		jobs.setBorder(new EmptyBorder(0,0,0,10));
+		gbc.weightx = 0.8;
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+		this.add(jobs, gbc);
+		if (json.has("maximumJobs")) {
+			maxJobsField.setText(json.get("maximumJobs").asText());
+		} else {
+			maxJobsField.setText("1");
+		}
+		gbc.weightx = 0.2;
+		gbc.fill = GridBagConstraints.HORIZONTAL;
+		this.add(maxJobsField, gbc);
+		gbc.weightx = 0.1;
+		this.add(new JPanel(), gbc);
+
+		gbc.gridy=1;
+		gbc.gridx=0;
+		gbc.gridwidth=3;
+		gbc.weightx=0;
+		gbc.anchor = GridBagConstraints.SOUTH;
+		gbc.fill = GridBagConstraints.BOTH;
+		gbc.weighty = 1.0;
+		JLabel explanationLabel = new JLabel("<html><body><small>" +
+					"The service <b>" +  processorName + "</b> will be invoked as soon as the required inputs " +
+				    "for an iteration are available, but no more than the maximum number of items " +
+					"will be invoked at the same time."
+					+ "</small></body></html>");
+		this.add(explanationLabel, gbc);
+
+		this.setPreferredSize(new Dimension(350, 170));
+	}
+
+	public boolean validateConfig() {
+		String errorText = "";
+		int maxJobs = -1;
+		try {
+			maxJobs = Integer.parseInt(maxJobsField.getText());
+			if (maxJobs < 1) {
+				errorText += "The maximum number of items must be a positive integer.\n";
+			}
+		}
+		catch (NumberFormatException e) {
+			errorText += "The maximum number of items must be an integer.\n";
+		}
+
+		if (errorText.length() > 0) {
+			JOptionPane.showMessageDialog(this, errorText, "", JOptionPane.ERROR_MESSAGE);
+			return false;
+		}
+		return true;
+	}
+
+	public JsonNode getJson() {
+		json.put("maximumJobs", maxJobsField.getText());
+		return json;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureAction.java
----------------------------------------------------------------------
diff --git a/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureAction.java b/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureAction.java
new file mode 100644
index 0000000..6727c59
--- /dev/null
+++ b/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureAction.java
@@ -0,0 +1,185 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.parallelize;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.AddChildEdit;
+import net.sf.taverna.t2.workflow.edits.ChangeJsonEdit;
+
+import org.apache.log4j.Logger;
+
+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.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * @author alanrw
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class ParallelizeConfigureAction extends AbstractAction {
+
+	private Frame owner;
+	private final Processor processor;
+	private final ParallelizeContextualView parallelizeContextualView;
+
+	private EditManager editManager;
+
+	private static Logger logger = Logger.getLogger(ParallelizeConfigureAction.class);
+
+	private final Scufl2Tools scufl2Tools = new Scufl2Tools();
+	private final SelectionManager selectionManager;
+
+	public ParallelizeConfigureAction(Frame owner,
+			ParallelizeContextualView parallelizeContextualView,
+			Processor processor, EditManager editManager, SelectionManager selectionManager) {
+		super("Configure");
+		this.owner = owner;
+		this.parallelizeContextualView = parallelizeContextualView;
+		this.processor = processor;
+		this.editManager = editManager;
+		this.selectionManager = selectionManager;
+	}
+
+	public void actionPerformed(ActionEvent e) {
+		String processorName = processor.getName();
+		String title = "Parallel jobs for service " + processorName;
+		final JDialog dialog = new HelpEnabledDialog(owner, title, true);
+		Configuration configuration;
+		try {
+			configuration = scufl2Tools.configurationFor(processor, selectionManager.getSelectedProfile());
+		} catch (IndexOutOfBoundsException ex) {
+			configuration = new Configuration();
+		}
+		ParallelizeConfigurationPanel parallelizeConfigurationPanel = new ParallelizeConfigurationPanel(configuration, processorName);
+		dialog.add(parallelizeConfigurationPanel, BorderLayout.CENTER);
+
+		JPanel buttonPanel = new JPanel();
+		buttonPanel.setLayout(new FlowLayout());
+
+		JButton okButton = new JButton(new OKAction(dialog,
+				parallelizeConfigurationPanel));
+		buttonPanel.add(okButton);
+
+		JButton resetButton = new JButton(new ResetAction(
+				parallelizeConfigurationPanel));
+		buttonPanel.add(resetButton);
+
+		JButton cancelButton = new JButton(new CancelAction(dialog));
+		buttonPanel.add(cancelButton);
+
+		dialog.add(buttonPanel, BorderLayout.SOUTH);
+		dialog.pack();
+		dialog.setLocationRelativeTo(null);
+		dialog.setVisible(true);
+	}
+
+	public class ResetAction extends AbstractAction {
+
+		private final ParallelizeConfigurationPanel parallelizeConfigurationPanel;
+
+		public ResetAction(ParallelizeConfigurationPanel parallelizeConfigurationPanel) {
+			super("Reset");
+			this.parallelizeConfigurationPanel = parallelizeConfigurationPanel;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			parallelizeConfigurationPanel.populate();
+		}
+
+	}
+
+	public class OKAction extends AbstractAction {
+
+		private final ParallelizeConfigurationPanel parallelizeConfigurationPanel;
+		private final JDialog dialog;
+
+		public OKAction(JDialog dialog, ParallelizeConfigurationPanel parallelizeConfigurationPanel) {
+			super("OK");
+			this.dialog = dialog;
+			this.parallelizeConfigurationPanel = parallelizeConfigurationPanel;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			if (parallelizeConfigurationPanel.validateConfig()) {
+				try {
+					try {
+						Configuration configuration = scufl2Tools.configurationFor(processor, selectionManager.getSelectedProfile());
+						ObjectNode json = configuration.getJsonAsObjectNode().deepCopy();
+						ObjectNode parallelizeNode = null;
+						if (json.has("parallelize")) {
+							parallelizeNode = (ObjectNode) json.get("parallelize");
+						} else {
+							parallelizeNode = json.objectNode();
+							json.put("parallelize", parallelizeNode);
+						}
+						JsonNode newParallelizeNode = parallelizeConfigurationPanel.getJson();
+						Iterator<Entry<String, JsonNode>> fields = newParallelizeNode.fields();
+						while (fields.hasNext()) {
+							Entry<String, JsonNode> entry = fields.next();
+							parallelizeNode.set(entry.getKey(), entry.getValue());
+						}
+						Edit<Configuration> edit = new ChangeJsonEdit(configuration, json);
+						editManager.doDataflowEdit(selectionManager.getSelectedWorkflowBundle(), edit);
+					} catch (IndexOutOfBoundsException ex) {
+						Configuration configuration = new Configuration();
+						configuration.setConfigures(processor);
+						ObjectNode json = configuration.getJsonAsObjectNode();
+						json.put("parallelize", parallelizeConfigurationPanel.getJson());
+						Edit<Profile> edit = new AddChildEdit<Profile>(selectionManager.getSelectedProfile(), configuration);
+						editManager.doDataflowEdit(selectionManager.getSelectedWorkflowBundle(), edit);
+					}
+					dialog.setVisible(false);
+					if (parallelizeContextualView != null) {
+						parallelizeContextualView.refreshView();
+					}
+				} catch (EditException e1) {
+					logger.warn("Could not configure jobs", e1);
+					JOptionPane.showMessageDialog(owner, "Could not configure jobs",
+							"An error occured when configuring jobs: " + e1.getMessage(),
+							JOptionPane.ERROR_MESSAGE);
+				}
+			}
+		}
+
+	}
+
+	public class CancelAction extends AbstractAction {
+
+		private final JDialog dialog;
+
+		public CancelAction(JDialog dialog) {
+			super("Cancel");
+			this.dialog = dialog;
+
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			dialog.setVisible(false);
+		}
+
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureMenuAction.java b/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureMenuAction.java
new file mode 100644
index 0000000..194d81e
--- /dev/null
+++ b/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureMenuAction.java
@@ -0,0 +1,77 @@
+/**********************************************************************
+ * 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.parallelize;
+
+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.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.Processor;
+
+public class ParallelizeConfigureMenuAction extends AbstractContextualMenuAction {
+
+	public static final URI configureRunningSection = URI
+			.create("http://taverna.sf.net/2009/contextMenu/configureRunning");
+
+	private static final URI PARALLELIZE_CONFIGURE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/parallelizeConfigure");
+
+	public static URI TYPE = URI.create("http://ns.taverna.org.uk/2010/scufl2/taverna/dispatchlayer/Parallelize");
+
+	private EditManager editManager;
+
+	private SelectionManager selectionManager;
+
+	public ParallelizeConfigureMenuAction() {
+		super(configureRunningSection, 10, PARALLELIZE_CONFIGURE_URI);
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		return new AbstractAction("Parallel jobs...") {
+			public void actionPerformed(ActionEvent e) {
+				Processor processor = (Processor) getContextualSelection().getSelection();
+				ParallelizeConfigureAction parallelizeConfigureAction = new ParallelizeConfigureAction(
+						null, null, processor, editManager, selectionManager);
+				parallelizeConfigureAction.actionPerformed(e);
+			}
+		};
+	}
+
+	public boolean isEnabled() {
+		return super.isEnabled() && (getContextualSelection().getSelection() instanceof Processor);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchTabContentPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchTabContentPanel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchTabContentPanel.java
new file mode 100644
index 0000000..b583491
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchTabContentPanel.java
@@ -0,0 +1,450 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.SwingUtilities;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Base64;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.SearchEngine;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Util;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.SearchEngine.QuerySearchInstance;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class SearchTabContentPanel extends JPanel implements ActionListener {
+  // CONSTANTS
+  private final static int SEARCH_HISTORY_LENGTH = 50;
+  private final static int SEARCH_FAVOURITES_LENGTH = 30;
+  protected final static String SEARCH_FROM_FAVOURITES = "searchFromFavourites";
+  protected final static String SEARCH_FROM_HISTORY = "searchFromHistory";
+  protected final static String ADD_FAVOURITE_SEARCH_INSTANCE = "addFavouriteSearchInstance";
+  protected final static String REMOVE_FAVOURITE_SEARCH_INSTANCE = "removeFavouriteSearchInstance";
+
+  private final MainComponent pluginMainComponent;
+  private final MyExperimentClient myExperimentClient;
+  private final Logger logger;
+
+  // COMPONENTS
+  private JSplitPane spMainSplitPane;
+  private SearchOptionsPanel jpSearchOptions;
+  private JPanel jpFavouriteSearches;
+  private JPanel jpSearchHistory;
+  private SearchResultsPanel jpSearchResults;
+  private final ImageIcon iconFavourite = new ImageIcon(MyExperimentPerspective.getLocalResourceURL("favourite_icon"));
+  private final ImageIcon iconRemove = new ImageIcon(MyExperimentPerspective.getLocalResourceURL("destroy_icon"));
+
+  // Data storage
+  private QuerySearchInstance siPreviousSearch;
+  private LinkedList<QuerySearchInstance> llFavouriteSearches;
+  private LinkedList<QuerySearchInstance> llSearchHistory;
+
+  // Search components 
+  private final SearchEngine searchEngine; // The search engine for executing keyword query searches 
+  private final Vector<Long> vCurrentSearchThreadID; // This will keep ID of the current search thread (there will only be one such thread)
+
+  public SearchTabContentPanel(MainComponent component, MyExperimentClient client, Logger logger) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	// initialise the favourite searches
+	String strFavouriteSearches = (String) myExperimentClient.getSettings().get(MyExperimentClient.INI_FAVOURITE_SEARCHES);
+	if (strFavouriteSearches != null) {
+	  Object oFavouriteSearches = Base64.decodeToObject(strFavouriteSearches);
+	  this.llFavouriteSearches = (LinkedList<QuerySearchInstance>) oFavouriteSearches;
+	} else {
+	  this.llFavouriteSearches = new LinkedList<QuerySearchInstance>();
+	}
+
+	// initialise the search history
+	String strSearchHistory = (String) myExperimentClient.getSettings().get(MyExperimentClient.INI_SEARCH_HISTORY);
+	if (strSearchHistory != null) {
+	  Object oSearchHistory = Base64.decodeToObject(strSearchHistory);
+	  this.llSearchHistory = (LinkedList<QuerySearchInstance>) oSearchHistory;
+	} else {
+	  this.llSearchHistory = new LinkedList<QuerySearchInstance>();
+	}
+
+	this.initialiseUI();
+	this.updateFavouriteSearches();
+	this.updateSearchHistory();
+
+	// initialise the search engine
+	vCurrentSearchThreadID = new Vector<Long>(1);
+	vCurrentSearchThreadID.add(null); // this is just a placeholder, so that it's possible to update this value instead of adding new ones later
+	this.searchEngine = new SearchEngine(vCurrentSearchThreadID, false, jpSearchResults, pluginMainComponent, myExperimentClient, logger);
+
+	SwingUtilities.invokeLater(new Runnable() {
+	  public void run() {
+		// THIS MIGHT NOT BE NEEDED AS THE SEARCH OPTIONS BOX NOW
+		// SETS THE MINIMUM SIZE OF THE SIDEBAR PROPERLY
+		spMainSplitPane.setDividerLocation(390);
+		spMainSplitPane.setOneTouchExpandable(true);
+		spMainSplitPane.setDoubleBuffered(true);
+	  }
+	});
+  }
+
+  private void initialiseUI() {
+	// create search options panel
+	jpSearchOptions = new SearchOptionsPanel(this, this.pluginMainComponent, this.myExperimentClient, this.logger);
+	jpSearchOptions.setMaximumSize(new Dimension(1024, 0)); // HACK: this is to make sure that search options box won't be stretched
+
+	// create favourite searches panel
+	jpFavouriteSearches = new JPanel();
+	jpFavouriteSearches.setMaximumSize(new Dimension(1024, 0)); // HACK: this is to make sure that favourite searches box won't be stretched
+	jpFavouriteSearches.setLayout(new GridBagLayout());
+	jpFavouriteSearches.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " Favourite Searches "), BorderFactory.createEmptyBorder(0, 5, 5, 5)));
+
+	// create search history panel
+	jpSearchHistory = new JPanel();
+	jpSearchHistory.setMaximumSize(new Dimension(1024, 0)); // HACK: this is to make sure that search history box won't be stretched
+	jpSearchHistory.setLayout(new GridBagLayout());
+	jpSearchHistory.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " Search History "), BorderFactory.createEmptyBorder(0, 5, 5, 5)));
+
+	// create the search sidebar
+	JPanel jpSearchSidebar = new JPanel();
+	jpSearchSidebar.setLayout(new GridBagLayout());
+
+	GridBagConstraints gbConstraints = new GridBagConstraints();
+	gbConstraints.anchor = GridBagConstraints.NORTHWEST;
+	gbConstraints.fill = GridBagConstraints.BOTH;
+	gbConstraints.weightx = 1;
+	gbConstraints.gridx = 0;
+
+	gbConstraints.gridy = 0;
+	jpSearchSidebar.add(jpSearchOptions, gbConstraints);
+
+	gbConstraints.gridy = 1;
+	jpSearchSidebar.add(jpFavouriteSearches, gbConstraints);
+
+	gbConstraints.gridy = 2;
+	jpSearchSidebar.add(jpSearchHistory, gbConstraints);
+
+	JPanel jpSidebarContainer = new JPanel();
+	jpSidebarContainer.setLayout(new BorderLayout());
+	jpSidebarContainer.add(jpSearchSidebar, BorderLayout.NORTH);
+
+	// wrap sidebar in a scroll pane
+	JScrollPane spSearchSidebar = new JScrollPane(jpSidebarContainer);
+	spSearchSidebar.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+	spSearchSidebar.setMinimumSize(new Dimension(jpSidebarContainer.getMinimumSize().width + 20, 0));
+
+	// create panel for search results
+	this.jpSearchResults = new SearchResultsPanel(this, pluginMainComponent, myExperimentClient, logger);
+
+	spMainSplitPane = new JSplitPane();
+	spMainSplitPane.setLeftComponent(spSearchSidebar);
+	spMainSplitPane.setRightComponent(jpSearchResults);
+
+	// PUT EVERYTHING TOGETHER
+	this.setLayout(new BorderLayout());
+	this.add(spMainSplitPane);
+  }
+
+  private void addToSearchListQueue(LinkedList<QuerySearchInstance> searchInstanceList, QuerySearchInstance searchInstanceToAdd, int queueSize) {
+	// check if such entry is already in the list
+	int iDuplicateIdx = searchInstanceList.indexOf(searchInstanceToAdd);
+
+	// only do the following if the new search instance list OR current instance is not the same
+	// as the last one in the list
+	if (searchInstanceList.size() == 0
+		|| iDuplicateIdx != searchInstanceList.size() - 1) {
+	  // if the current item is already in the list, remove it (then re-add at the end of the list)
+	  if (iDuplicateIdx >= 0)
+		searchInstanceList.remove(iDuplicateIdx);
+
+	  // we want to keep the history size constant, therefore when it reaches a certain
+	  // size, oldest element needs to be removed
+	  if (searchInstanceList.size() >= queueSize)
+		searchInstanceList.remove();
+
+	  // in either case, add the new element to the tail of the search history
+	  searchInstanceList.offer(searchInstanceToAdd);
+	}
+  }
+
+  private void addToFavouriteSearches(QuerySearchInstance searchInstance) {
+	this.addToSearchListQueue(this.llFavouriteSearches, searchInstance, SEARCH_FAVOURITES_LENGTH);
+	Collections.sort(this.llFavouriteSearches);
+  }
+
+  // the method to update search history listing
+  protected void updateFavouriteSearches() {
+	this.jpFavouriteSearches.removeAll();
+
+	if (this.llFavouriteSearches.size() == 0) {
+	  GridBagConstraints c = new GridBagConstraints();
+	  c.weightx = 1.0;
+	  c.anchor = GridBagConstraints.WEST;
+	  this.jpFavouriteSearches.add(Util.generateNoneTextLabel("No favourite searches"), c);
+	} else {
+	  for (int i = this.llFavouriteSearches.size() - 1; i >= 0; i--) {
+		addEntryToSearchListingPanel(this.llFavouriteSearches, i, SEARCH_FROM_FAVOURITES, this.jpFavouriteSearches, this.iconRemove, REMOVE_FAVOURITE_SEARCH_INSTANCE, "<html>Click to remove from your local favourite searches.<br>"
+			+ "(This will not affect your myExperiment profile settings.)</html>");
+	  }
+	}
+
+	this.jpFavouriteSearches.repaint();
+	this.jpFavouriteSearches.revalidate();
+  }
+
+  private void addToSearchHistory(QuerySearchInstance searchInstance) {
+	this.addToSearchListQueue(this.llSearchHistory, searchInstance, SEARCH_HISTORY_LENGTH);
+  }
+
+  // the method to update search history listing
+  protected void updateSearchHistory() {
+	this.jpSearchHistory.removeAll();
+
+	if (this.llSearchHistory.size() == 0) {
+	  GridBagConstraints c = new GridBagConstraints();
+	  c.weightx = 1.0;
+	  c.anchor = GridBagConstraints.WEST;
+	  this.jpSearchHistory.add(Util.generateNoneTextLabel(SearchResultsPanel.NO_SEARCHES_STATUS), c);
+	} else {
+	  for (int i = this.llSearchHistory.size() - 1; i >= 0; i--) {
+		addEntryToSearchListingPanel(this.llSearchHistory, i, SEARCH_FROM_HISTORY, this.jpSearchHistory, this.iconFavourite, ADD_FAVOURITE_SEARCH_INSTANCE, "<html>Click to add to your local favourite"
+			+ " searches - these will be available every time you use Taverna.<br>(This will not affect your"
+			+ " myExperiment profile settings.)</html>");
+	  }
+	}
+
+	this.jpSearchHistory.repaint();
+	this.jpSearchHistory.revalidate();
+
+	// also update search history in History tab
+	if (this.pluginMainComponent.getHistoryBrowser() != null) {
+	  this.pluginMainComponent.getHistoryBrowser().refreshSearchHistory();
+	}
+  }
+
+  private void addEntryToSearchListingPanel(List<QuerySearchInstance> searchInstanceList, int iIndex, String searchAction, JPanel panelToPopulate, ImageIcon entryIcon, String iconAction, String iconActionTooltip) {
+	// labels with search query and search settings
+	JClickableLabel jclCurrentEntryLabel = new JClickableLabel(searchInstanceList.get(iIndex).getSearchQuery(), searchAction
+		+ ":" + iIndex, this, WorkbenchIcons.findIcon, SwingUtilities.LEFT, searchInstanceList.get(iIndex).toString());
+	JLabel jlCurrentEntrySettings = new JLabel(searchInstanceList.get(iIndex).detailsAsString());
+	jlCurrentEntrySettings.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
+
+	// grouping search details and search settings together
+	JPanel jpCurentEntryDetails = new JPanel();
+	jpCurentEntryDetails.setLayout(new GridBagLayout());
+	GridBagConstraints c = new GridBagConstraints();
+
+	c.anchor = GridBagConstraints.WEST;
+	jpCurentEntryDetails.add(jclCurrentEntryLabel, c);
+	c.weightx = 1.0;
+	jpCurentEntryDetails.add(jlCurrentEntrySettings, c);
+
+	// creating a "button" to add current item to favourites
+	JClickableLabel jclFavourite = new JClickableLabel("", iconAction + ":"
+		+ iIndex, this, entryIcon, SwingUtilities.LEFT, iconActionTooltip);
+	jclFavourite.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
+
+	// putting all pieces of current item together
+	JPanel jpCurrentEntry = new JPanel();
+	jpCurrentEntry.setLayout(new GridBagLayout());
+
+	c.anchor = GridBagConstraints.WEST;
+	c.weightx = 1.0;
+	jpCurrentEntry.add(jpCurentEntryDetails, c);
+
+	c.anchor = GridBagConstraints.WEST;
+	c.weightx = 0;
+	jpCurrentEntry.add(jclFavourite, c);
+
+	// adding current item to the history list 
+	c.fill = GridBagConstraints.HORIZONTAL;
+	c.weightx = 1.0;
+	c.gridx = 0;
+	c.gridy = GridBagConstraints.RELATIVE;
+	panelToPopulate.add(jpCurrentEntry, c);
+  }
+
+  public void actionPerformed(ActionEvent e) {
+	if (e.getSource().equals(this.jpSearchOptions.bSearch)) {
+	  // "Search" button was clicked
+
+	  // if no search query is specified, display error message
+	  if (jpSearchOptions.getSearchQuery().length() == 0) {
+		javax.swing.JOptionPane.showMessageDialog(null, "Search query is empty. Please specify your search query and try again.", "Error", JOptionPane.WARNING_MESSAGE);
+		jpSearchOptions.focusSearchQueryField();
+	  } else {
+		// will ensure that if the value in the search result limit editor
+		// is invalid, it is processed properly
+		try {
+		  this.jpSearchOptions.jsResultLimit.commitEdit();
+		} catch (ParseException ex) {
+		  JOptionPane.showMessageDialog(null, "Invalid search result limit value. This should be an\n"
+			  + "integer in the range of "
+			  + SearchOptionsPanel.SEARCH_RESULT_LIMIT_MIN
+			  + ".."
+			  + SearchOptionsPanel.SEARCH_RESULT_LIMIT_MAX, "MyExperiment Plugin - Error", JOptionPane.WARNING_MESSAGE);
+		  this.jpSearchOptions.tfResultLimitTextField.selectAll();
+		  this.jpSearchOptions.tfResultLimitTextField.requestFocusInWindow();
+		  return;
+		}
+
+		// all fine, settings present - store the settings..
+		siPreviousSearch = new SearchEngine.QuerySearchInstance(jpSearchOptions.getSearchQuery(), jpSearchOptions.getResultCountLimit(), jpSearchOptions.getSearchWorkflows(), jpSearchOptions.getSearchFiles(), jpSearchOptions.getSearchPacks(), jpSearchOptions.getSearchUsers(), jpSearchOptions.getSearchGroups());
+
+		// .. and execute the query
+		this.jpSearchOptions.focusSearchQueryField();
+		this.runSearch();
+	  }
+	} else if (e.getSource() instanceof JClickableLabel) {
+	  if (e.getActionCommand().startsWith(SEARCH_FROM_HISTORY)
+		  || e.getActionCommand().startsWith(SEARCH_FROM_FAVOURITES)) {
+		// the part of the action command that is following the prefix is the ID in the search history / favourites storage;
+		// this search instance is removed from history and will be re-added at the top of it when search is launched 
+		int iEntryID = Integer.parseInt(e.getActionCommand().substring(e.getActionCommand().indexOf(":") + 1));
+		final QuerySearchInstance si = (e.getActionCommand().startsWith(SEARCH_FROM_HISTORY) ? this.llSearchHistory.remove(iEntryID) : this.llFavouriteSearches.get(iEntryID)); // in case of favourites, no need to remove the entry
+
+		// re-set search options in the settings box and re-run the search
+		SwingUtilities.invokeLater(new Runnable() {
+		  public void run() {
+			jpSearchOptions.setSearchQuery(si.getSearchQuery());
+			jpSearchOptions.setSearchAllResourceTypes(false); // reset the 'all resource types'
+			jpSearchOptions.setSearchWorkflows(si.getSearchWorkflows());
+			jpSearchOptions.setSearchFiles(si.getSearchFiles());
+			jpSearchOptions.setSearchPacks(si.getSearchPacks());
+			jpSearchOptions.setSearchUsers(si.getSearchUsers());
+			jpSearchOptions.setSearchGroups(si.getSearchGroups());
+			jpSearchOptions.setResultCountLimit(si.getResultCountLimit());
+
+			// set this as the 'latest' search
+			siPreviousSearch = si;
+
+			// run the search (and update the search history)
+			runSearch();
+		  }
+		});
+	  } else if (e.getActionCommand().startsWith(ADD_FAVOURITE_SEARCH_INSTANCE)) {
+		// get the ID of the entry in the history listing first; then fetch the instance itself
+		int iHistID = Integer.parseInt(e.getActionCommand().substring(e.getActionCommand().indexOf(":") + 1));
+		final QuerySearchInstance si = this.llSearchHistory.get(iHistID);
+
+		// add item to favourites and re-draw the panel
+		SwingUtilities.invokeLater(new Runnable() {
+		  public void run() {
+			addToFavouriteSearches(si);
+			updateFavouriteSearches();
+		  }
+		});
+	  } else if (e.getActionCommand().startsWith(REMOVE_FAVOURITE_SEARCH_INSTANCE)) {
+		// get the ID of the entry in the favourite searches listing first; then remove the instance with that ID from the list
+		int iFavouriteID = Integer.parseInt(e.getActionCommand().substring(e.getActionCommand().indexOf(":") + 1));
+		this.llFavouriteSearches.remove(iFavouriteID);
+
+		// item removed from favourites - re-draw the panel now
+		SwingUtilities.invokeLater(new Runnable() {
+		  public void run() {
+			updateFavouriteSearches();
+		  }
+		});
+	  }
+	} else if (e.getSource().equals(this.jpSearchResults.bRefresh)) {
+	  // "Refresh" button clicked; disable clearing results and re-run previous search
+	  this.jpSearchResults.bClear.setEnabled(false);
+	  this.rerunLastSearch();
+	} else if (e.getSource().equals(this.jpSearchResults.bClear)) {
+	  // "Clear" button clicked - clear last search and disable re-running it
+	  siPreviousSearch = null;
+	  vCurrentSearchThreadID.set(0, null);
+	  this.jpSearchResults.clear();
+	  this.jpSearchResults.setStatus(SearchResultsPanel.NO_SEARCHES_STATUS);
+	  this.jpSearchResults.bClear.setEnabled(false);
+	  this.jpSearchResults.bRefresh.setEnabled(false);
+	}
+  }
+
+  // search history will get updated by default
+  private void runSearch() {
+	runSearch(true);
+  }
+
+  // makes preparations and runs the current search 
+  // (has an option not to update the search history)
+  private void runSearch(boolean bUpdateHistory) {
+	if (bUpdateHistory) {
+	  // add current search to search history ..
+	  this.addToSearchHistory(siPreviousSearch);
+
+	  // .. update the search history box ..
+	  SwingUtilities.invokeLater(new Runnable() {
+		public void run() {
+		  updateSearchHistory();
+		}
+	  });
+	}
+
+	// ..and run the query
+	this.searchEngine.searchAndPopulateResults(siPreviousSearch);
+  }
+
+  // re-executes the search for the most recent query
+  // (if searches have already been done before or were not cleared)
+  public void rerunLastSearch() {
+	if (this.siPreviousSearch != null) {
+	  runSearch();
+	}
+  }
+
+  public SearchResultsPanel getSearchResultPanel() {
+	return (this.jpSearchResults);
+  }
+
+  public LinkedList<QuerySearchInstance> getSearchFavouritesList() {
+	return (this.llFavouriteSearches);
+  }
+
+  public LinkedList<QuerySearchInstance> getSearchHistory() {
+	return (this.llSearchHistory);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/StyledHTMLEditorKit.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/StyledHTMLEditorKit.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/StyledHTMLEditorKit.java
new file mode 100644
index 0000000..2a2302a
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/StyledHTMLEditorKit.java
@@ -0,0 +1,19 @@
+package net.sf.taverna.t2.ui.perspectives.myexperiment;
+
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.StyleSheet;
+
+public class StyledHTMLEditorKit extends HTMLEditorKit {
+
+	private final StyleSheet styleSheet;
+
+	public StyledHTMLEditorKit(StyleSheet styleSheet) {
+		this.styleSheet = styleSheet;
+	}
+	
+	@Override
+	public StyleSheet getStyleSheet() {
+		return styleSheet;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TagBrowserTabContentPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TagBrowserTabContentPanel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TagBrowserTabContentPanel.java
new file mode 100644
index 0000000..eca9ab0
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TagBrowserTabContentPanel.java
@@ -0,0 +1,226 @@
+// Copyright (C) 2008 The University of Manchester, University of Southampton
+// and Cardiff University
+package net.sf.taverna.t2.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Vector;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JSplitPane;
+import javax.swing.SwingUtilities;
+
+import org.apache.log4j.Logger;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Base64;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.SearchEngine;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Tag;
+
+/*
+ * @author Jiten Bhagat
+ */
+public class TagBrowserTabContentPanel extends JPanel implements ActionListener {
+  // CONSTANTS
+  private static final double TAG_CLOUD_BALANCE = 0.35;
+  private static final double TAG_SIDEBAR_BALANCE = 0.4;
+  private static final int TAG_SEARCH_HISTORY_LENGTH = 50;
+
+  private MainComponent pluginMainComponent;
+  private MyExperimentClient myExperimentClient;
+  private Logger logger;
+
+  // COMPONENTS
+  private JSplitPane spMainSplitPane;
+  private JSplitPane spTagCloudSplitPane;
+  private TagCloudPanel jpMyTags;
+  private TagCloudPanel jpAllTags;
+  private SearchResultsPanel jpTagSearchResults;
+  private JButton bLoginToSeeMyTags;
+  private JPanel jpLoginToSeeMyTags;
+
+  // STORAGE
+  // last tag for which the search has been made
+  private String strCurrentTagCommand;
+  private ArrayList<Tag> lTagSearchHistory;
+
+  // Search components
+  private SearchEngine searchEngine; // The search engine for executing keyword
+  // query searches
+  private Vector<Long> vCurrentSearchThreadID; // This will keep ID of the
+
+  // current search thread (there
+  // will only be one such thread)
+
+  @SuppressWarnings("unchecked")
+  public TagBrowserTabContentPanel(MainComponent component, MyExperimentClient client, Logger logger) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the
+	// parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	// no tag searches have been done yet
+	this.strCurrentTagCommand = null;
+
+	// initialise the tag search history
+	String strTagSearchHistory = (String) myExperimentClient.getSettings().get(MyExperimentClient.INI_TAG_SEARCH_HISTORY);
+	if (strTagSearchHistory != null) {
+	  Object oTagSearchHistory = Base64.decodeToObject(strTagSearchHistory);
+	  this.lTagSearchHistory = (ArrayList<Tag>) oTagSearchHistory;
+	} else {
+	  this.lTagSearchHistory = new ArrayList<Tag>();
+	}
+
+	this.initialiseUI();
+
+	// initialise the search engine
+	vCurrentSearchThreadID = new Vector<Long>(1);
+	vCurrentSearchThreadID.add(null); // this is just a placeholder, so that
+	// it's possible to update this value
+	// instead of adding new ones later
+	this.searchEngine = new SearchEngine(vCurrentSearchThreadID, true, jpTagSearchResults, pluginMainComponent, myExperimentClient, logger);
+
+	SwingUtilities.invokeLater(new Runnable() {
+	  public void run() {
+		spTagCloudSplitPane.setDividerLocation(TAG_CLOUD_BALANCE);
+		spMainSplitPane.setDividerLocation(TAG_SIDEBAR_BALANCE);
+		spMainSplitPane.setOneTouchExpandable(true);
+		spMainSplitPane.setDoubleBuffered(true);
+	  }
+	});
+  }
+
+  private void initialiseUI() {
+	// This panel will be used when the user is not logged in - i.e. when can't
+	// show "My Tags";
+	// log-in button will be shown instead
+	this.bLoginToSeeMyTags = new JButton("Login to see your tags", new ImageIcon(MyExperimentPerspective.getLocalResourceURL("login_icon")));
+	this.bLoginToSeeMyTags.addActionListener(this);
+
+	this.jpLoginToSeeMyTags = new JPanel();
+	this.jpLoginToSeeMyTags.setLayout(new GridBagLayout());
+	this.jpLoginToSeeMyTags.add(this.bLoginToSeeMyTags);
+
+	// Create the tag clouds
+	this.jpMyTags = new TagCloudPanel("My Tags", TagCloudPanel.TAGCLOUD_TYPE_USER, this, pluginMainComponent, myExperimentClient, logger);
+	this.jpAllTags = new TagCloudPanel("All Tags", TagCloudPanel.TAGCLOUD_TYPE_GENERAL, this, pluginMainComponent, myExperimentClient, logger);
+
+	// add the two tag clouds to the left-hand side sidebar
+	this.spTagCloudSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+	this.spTagCloudSplitPane.setTopComponent(this.myExperimentClient.isLoggedIn() ? jpMyTags : jpLoginToSeeMyTags);
+	this.spTagCloudSplitPane.setBottomComponent(jpAllTags);
+
+	// create panel for tag search results
+	this.jpTagSearchResults = new SearchResultsPanel(this, pluginMainComponent, myExperimentClient, logger);
+
+	this.spMainSplitPane = new JSplitPane();
+	this.spMainSplitPane.setLeftComponent(spTagCloudSplitPane);
+	this.spMainSplitPane.setRightComponent(this.jpTagSearchResults);
+
+	this.setLayout(new BorderLayout());
+	this.add(this.spMainSplitPane, BorderLayout.CENTER);
+  }
+
+  // this helper is called when the user logs in / out to swap the
+  // view accordingly; 'refresh' on "My Tags" panel should be called
+  // immediately after changing the view
+  public void setMyTagsShown(boolean bShow) {
+	if (bShow) {
+	  this.spTagCloudSplitPane.setTopComponent(this.jpMyTags);
+	} else {
+	  this.spTagCloudSplitPane.setTopComponent(this.jpLoginToSeeMyTags);
+	}
+
+	// in either case apply element balance again
+	this.spTagCloudSplitPane.setDividerLocation(TAG_CLOUD_BALANCE);
+  }
+
+  public void refresh() {
+	if (this.myExperimentClient.isLoggedIn()) {
+	  // "My Tags" are only accessible when the user has logged in
+	  this.jpMyTags.refresh();
+	}
+	this.jpAllTags.refresh();
+  }
+
+  // re-executes the search for the most recent tag
+  // (if tag searches have already been done before)
+  public void rerunLastTagSearch() {
+	if (this.strCurrentTagCommand != null) {
+	  this.actionPerformed(new ActionEvent(this.jpAllTags, 0, this.strCurrentTagCommand));
+	}
+  }
+
+  public TagCloudPanel getMyTagPanel() {
+	return (this.jpMyTags);
+  }
+
+  public TagCloudPanel getAllTagPanel() {
+	return (this.jpAllTags);
+  }
+
+  public SearchResultsPanel getTagSearchResultPanel() {
+	return (this.jpTagSearchResults);
+  }
+
+  public ArrayList<Tag> getTagSearchHistory() {
+	return (this.lTagSearchHistory);
+  }
+
+  public void actionPerformed(ActionEvent e) {
+	if (e.getSource().equals(this.jpTagSearchResults.bRefresh)) {
+	  // disable clearing results and re-run last tag search
+	  this.getTagSearchResultPanel().bClear.setEnabled(false);
+	  this.rerunLastTagSearch();
+	} else if (e.getSource().equals(this.jpTagSearchResults.bClear)) {
+	  // clear last search and disable re-running it
+	  this.strCurrentTagCommand = null;
+	  vCurrentSearchThreadID.set(0, null);
+	  this.getTagSearchResultPanel().clear();
+	  this.getTagSearchResultPanel().setStatus(SearchResultsPanel.NO_SEARCHES_STATUS);
+	  this.getTagSearchResultPanel().bClear.setEnabled(false);
+	  this.getTagSearchResultPanel().bRefresh.setEnabled(false);
+	} else if (e.getSource() instanceof JClickableLabel
+		|| e.getSource() instanceof TagCloudPanel) {
+	  // one of the tags was clicked as a hyperlink in any TagCloudPanel
+	  // (in Resource Preview Browser or in Tag Browser tab) or as a
+	  // JClickableLabel
+	  if (e.getActionCommand().startsWith("tag:")) {
+		// record the tag search command and run the search
+		this.strCurrentTagCommand = e.getActionCommand();
+		this.searchEngine.searchAndPopulateResults(strCurrentTagCommand);
+
+		// update tag search history making sure that:
+		// - there's only one occurrence of this tag in the history;
+		// - if this tag was in the history before, it is moved to the 'top'
+		// now;
+		// - predefined history size is not exceeded
+		Tag t = Tag.instantiateTagFromActionCommand(strCurrentTagCommand);
+		this.lTagSearchHistory.remove(t);
+		this.lTagSearchHistory.add(t);
+		if (this.lTagSearchHistory.size() > TAG_SEARCH_HISTORY_LENGTH)
+		  this.lTagSearchHistory.remove(0);
+
+		// now update the tag search history panel in 'History' tab
+		if (this.pluginMainComponent.getHistoryBrowser() != null) {
+		  this.pluginMainComponent.getHistoryBrowser().refreshTagSearchHistory();
+		}
+	  }
+	} else if (e.getSource().equals(this.bLoginToSeeMyTags)) {
+	  // set the return "link"
+	  this.pluginMainComponent.getMyStuffTab().cTabContentComponentToSwitchToAfterLogin = this;
+
+	  // switch to login tab
+	  this.pluginMainComponent.getMainTabs().setSelectedComponent(this.pluginMainComponent.getMyStuffTab());
+	}
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TagCloudPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TagCloudPanel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TagCloudPanel.java
new file mode 100644
index 0000000..b03fd8f
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TagCloudPanel.java
@@ -0,0 +1,342 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSlider;
+import javax.swing.JTextPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.HTMLEditorKit;
+
+import org.apache.log4j.Logger;
+
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Tag;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.TagCloud;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+/**
+ * @author Sergejs Aleksejevs, Jiten Bhagat
+ */
+public class TagCloudPanel extends JPanel implements ChangeListener, ItemListener, ActionListener, HyperlinkListener {
+  // CONSTANTS
+  private static final int TAGCLOUD_MAX_FONTSIZE = 36;
+  private static final int TAGCLOUD_MIN_FONTSIZE = 12;
+  private static final int TAGCLOUD_DEFAULT_MAX_SIZE = 350;
+  private static final int TAGCLOUD_DEFAULT_DISPLAY_SIZE = 50;
+
+  public static final int TAGCLOUD_TYPE_GENERAL = 0;
+  public static final int TAGCLOUD_TYPE_USER = 1;
+  public static final int TAGCLOUD_TYPE_RESOURCE_PREVIEW = 2;
+
+  private MainComponent pluginMainComponent;
+  private MyExperimentClient myExperimentClient;
+  private Logger logger;
+
+  // COMPONENTS
+  private String strTitle;
+  private int iType;
+  private ShadedLabel lCloudTitle;
+  private JSlider jsCloudSizeSlider;
+  private JCheckBox cbShowAllTags;
+  private JButton bRefresh;
+  private JTextPane tpTagCloudBody;
+
+  private ActionListener clickHandler;
+  private TagCloud tcData = new TagCloud();
+  private boolean bUserTagCloudSliderValueNeverSet = true;
+
+  public TagCloudPanel(String title, int iTagCloudType, ActionListener clickHandler, MainComponent component, MyExperimentClient client, Logger logger) {
+	super();
+
+	// set parameters and the main variables to ensure access to myExperiment,
+	// logger and the parent component
+	this.strTitle = title;
+	this.iType = iTagCloudType;
+	this.clickHandler = clickHandler;
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	initialiseUI();
+  }
+
+  private void initialiseUI() {
+	// set the title of the tag cloud
+	lCloudTitle = new ShadedLabel(this.strTitle, ShadedLabel.BLUE);
+
+	// create the tag cloud controls panel
+	// (all controls will be created anyway, but if that's a resource
+	// preview tag cloud, make sure that these controls are not displayed)
+	JPanel jpCloudControlsPanel = new JPanel();
+	jpCloudControlsPanel.setLayout(new BoxLayout(jpCloudControlsPanel, BoxLayout.LINE_AXIS));
+	this.jsCloudSizeSlider = new JSlider(1, TAGCLOUD_DEFAULT_MAX_SIZE, TAGCLOUD_DEFAULT_DISPLAY_SIZE);
+	this.cbShowAllTags = new JCheckBox("All tags", false);
+	this.bRefresh = new JButton("Refresh", WorkbenchIcons.refreshIcon);
+
+	if (this.iType != TagCloudPanel.TAGCLOUD_TYPE_RESOURCE_PREVIEW) {
+	  this.jsCloudSizeSlider.addChangeListener(this);
+	  this.jsCloudSizeSlider.setToolTipText("Drag the slider to select how big the tag cloud should be, or check the \"All tags\" box to get the full tag cloud.");
+	  jpCloudControlsPanel.add(this.jsCloudSizeSlider);
+
+	  this.cbShowAllTags.addItemListener(this);
+	  jpCloudControlsPanel.add(this.cbShowAllTags);
+
+	  this.bRefresh.addActionListener(this);
+	  this.bRefresh.setToolTipText("Click this button to refresh the Tag Cloud");
+	  jpCloudControlsPanel.add(this.bRefresh);
+	}
+
+	// tag cloud header panel which which contains controls
+	JPanel jpCloudHeader = new JPanel(new BorderLayout());
+	jpCloudHeader.add(jpCloudControlsPanel, BorderLayout.CENTER);
+	jpCloudHeader.setBorder(BorderFactory.createEtchedBorder());
+
+	// body of the tag cloud with the actual tags
+	this.tpTagCloudBody = new JTextPane();
+	this.tpTagCloudBody.setBorder(BorderFactory.createEmptyBorder());
+	this.tpTagCloudBody.setEditable(false);
+	this.tpTagCloudBody.setContentType("text/html");
+	this.tpTagCloudBody.addHyperlinkListener(this);
+
+	JScrollPane spTagCloudBody = new JScrollPane(this.tpTagCloudBody, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+	spTagCloudBody.setBorder(BorderFactory.createEmptyBorder());
+	spTagCloudBody.setOpaque(true);
+
+	// PUT EVERYTHING TOGETHER
+	JPanel jpTagCloudContentWithControls = new JPanel();
+	jpTagCloudContentWithControls.setLayout(new BorderLayout());
+	jpTagCloudContentWithControls.add(spTagCloudBody, BorderLayout.CENTER);
+	if (this.iType != TagCloudPanel.TAGCLOUD_TYPE_RESOURCE_PREVIEW) {
+	  jpTagCloudContentWithControls.add(jpCloudHeader, BorderLayout.NORTH);
+	}
+
+	this.setLayout(new BorderLayout());
+	if (this.iType != TagCloudPanel.TAGCLOUD_TYPE_RESOURCE_PREVIEW) {
+	  this.add(lCloudTitle, BorderLayout.NORTH);
+	}
+	this.add(jpTagCloudContentWithControls, BorderLayout.CENTER);
+	this.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2), BorderFactory.createEtchedBorder()));
+  }
+
+  public void refresh() {
+	this.lCloudTitle.setText(strTitle
+		+ " <span style='color: gray;'>(Loading...)</span>");
+
+	// Make call to myExperiment API in a different thread
+	// (then use SwingUtilities.invokeLater to update the UI when ready).
+	new Thread("Get '" + this.strTitle + "' tag cloud data") {
+	  public void run() {
+		logger.debug("Getting '" + strTitle + "' tag cloud data");
+
+		try {
+		  int size = -1;
+		  if (!cbShowAllTags.isSelected()) {
+			size = jsCloudSizeSlider.getValue();
+		  }
+
+		  // based on the type of the tag cloud, different data needs to be
+		  // fetched
+		  switch (iType) {
+			case TagCloudPanel.TAGCLOUD_TYPE_GENERAL:
+			  tcData = myExperimentClient.getGeneralTagCloud(size);
+			  break;
+
+			case TagCloudPanel.TAGCLOUD_TYPE_USER:
+			  tcData = myExperimentClient.getUserTagCloud(myExperimentClient.getCurrentUser(), size);
+			  break;
+
+			case TagCloudPanel.TAGCLOUD_TYPE_RESOURCE_PREVIEW:
+			  // fetch tag counts for tags that are already pre-set
+			  myExperimentClient.convertTagListIntoTagCloudData(tcData.getTags());
+			  break;
+
+			default:
+			  // unknown type of tag cloud; show no data
+			  tcData = new TagCloud();
+			  break;
+		  }
+
+		  SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+			  repopulate();
+			}
+		  });
+		} catch (Exception ex) {
+		  logger.error("Failed to get tag cloud data from myExperiment", ex);
+		}
+	  }
+	}.start();
+  }
+
+  public void repopulate() {
+	logger.debug("Building '" + this.strTitle + "' tag cloud...");
+
+	try {
+	  this.jsCloudSizeSlider.removeChangeListener(this);
+	  if (this.iType == TAGCLOUD_TYPE_USER) {
+		jsCloudSizeSlider.setMinimum(1);
+		jsCloudSizeSlider.setMaximum(myExperimentClient.getCurrentUser().getTags().size());
+		if (bUserTagCloudSliderValueNeverSet) {
+		  // this is the first load of the cloud, show all user tags
+		  jsCloudSizeSlider.setValue(jsCloudSizeSlider.getMaximum());
+		  bUserTagCloudSliderValueNeverSet = false;
+		} else {
+		  // not the first load, test if the position of the slider is still
+		  // within the scope
+		  // (put that back to maximum if exceeds)
+		  if (jsCloudSizeSlider.getValue() > jsCloudSizeSlider.getMaximum()
+			  || this.cbShowAllTags.isSelected())
+			jsCloudSizeSlider.setValue(jsCloudSizeSlider.getMaximum());
+		}
+	  } else {
+		// if "All tags" check box is ticked, set the slider max to the max no
+		// of tags
+		// (this will be the total number of tags available from myExperiment);
+		// if "All tags" was never checked before, max position of the slider
+		// will
+		// stay at predefined default value (because the total number of tags is
+		// not known yet)
+		if (this.cbShowAllTags.isSelected()) {
+		  int size = this.tcData.getTags().size();
+		  this.jsCloudSizeSlider.setMaximum(size);
+		  this.jsCloudSizeSlider.setValue(size);
+		}
+	  }
+	  this.jsCloudSizeSlider.addChangeListener(this);
+
+	  // For tag cloud font size calculations
+	  int iMaxCount = this.tcData.getMaxTagCount();
+
+	  StringBuffer content = new StringBuffer();
+
+	  if (this.tcData.getTags().size() > 0) {
+		content.append("<div class='outer'>");
+		content.append("<br>");
+		content.append("<div class='tag_cloud'>");
+
+		for (Tag t : this.tcData.getTags()) {
+		  // Normalise count and use it to obtain a font size value.
+		  // Also chops off based on min and max.
+		  int fontSize = (int) (((double) t.getCount() / ((double) iMaxCount / 3)) * TAGCLOUD_MAX_FONTSIZE);
+		  if (fontSize < TAGCLOUD_MIN_FONTSIZE) {
+			fontSize = TAGCLOUD_MIN_FONTSIZE;
+		  }
+		  if (fontSize > TAGCLOUD_MAX_FONTSIZE) {
+			fontSize = TAGCLOUD_MAX_FONTSIZE;
+		  }
+
+		  content.append("<a style='font-size: " + fontSize + "pt;' href='tag:"
+			  + t.getTagName() + "'>" + t.getTagName() + "</a>");
+		  content.append("&nbsp;&nbsp;&nbsp;");
+		}
+
+		content.append("<br>");
+		content.append("</div>");
+		content.append("</div>");
+	  } else {
+		content.append("<br>");
+		content.append("<span style='color: gray; font-weight: italic;'>&nbsp;&nbsp;No tags to display</span>");
+	  }
+
+	  HTMLEditorKit kit = new StyledHTMLEditorKit(pluginMainComponent.getStyleSheet());
+	  HTMLDocument doc = (HTMLDocument) (kit.createDefaultDocument());
+	  doc.insertAfterStart(doc.getRootElements()[0].getElement(0), content.toString());
+
+	  this.tpTagCloudBody.setEditorKit(kit);
+	  this.tpTagCloudBody.setDocument(doc);
+	} catch (Exception e) {
+	  logger.error("Failed to populate tag cloud", e);
+	}
+
+	SwingUtilities.invokeLater(new Runnable() {
+	  public void run() {
+		lCloudTitle.setText(strTitle
+			+ " <span style='color: gray;'>(currently showing "
+			+ tcData.getTags().size() + ")</span>");
+		revalidate();
+	  }
+	});
+  }
+
+  /**
+   * Helper method to get hold of the tag cloud data. Needed to be able to set
+   * tag cloud data when using this for resource preview (when tag fetching is
+   * not required).
+   */
+  public TagCloud getTagCloudData() {
+	return this.tcData;
+  }
+
+  public void stateChanged(ChangeEvent e) {
+	if (e.getSource().equals(this.jsCloudSizeSlider)) {
+	  // cloud size slider was dragged to a new place and the drag event
+	  // has finished; refresh the tag cloud with the newly selected tag count
+	  if (!this.jsCloudSizeSlider.getValueIsAdjusting()) {
+		this.cbShowAllTags.removeItemListener(this);
+		this.cbShowAllTags.setSelected(false);
+		this.cbShowAllTags.addItemListener(this);
+
+		this.refresh();
+	  }
+	}
+  }
+
+  public void itemStateChanged(ItemEvent e) {
+	if (e.getItemSelectable().equals(this.cbShowAllTags)) {
+	  // "Show all" clicked - need to refresh with all tags being displayed
+	  this.refresh();
+	}
+  }
+
+  public void actionPerformed(ActionEvent e) {
+	if (e.getSource().equals(this.bRefresh)) {
+	  // refresh button clicked on the cloud controls panel -
+	  // simply refresh the cloud with the same parameters
+	  this.refresh();
+	}
+  }
+
+  public void hyperlinkUpdate(HyperlinkEvent e) {
+	if (e.getSource().equals(this.tpTagCloudBody)
+		&& e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+	  // one of the tags was clicked, but click processing is off-loaded to
+	  // 'clickHandler'
+	  this.clickHandler.actionPerformed(new ActionEvent(this, (this.getClass().getName() + e.getDescription()).hashCode(), e.getDescription()));
+	}
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TestJFrameForLocalLaunch.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TestJFrameForLocalLaunch.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TestJFrameForLocalLaunch.java
new file mode 100644
index 0000000..15dd3a4
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/TestJFrameForLocalLaunch.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.Dimension;
+import javax.swing.JFrame;
+
+public class TestJFrameForLocalLaunch {
+
+	/**
+	 * This is a simple test class for launching myExperiment perspective
+	 * from outside Taverna. At some point it will be not usable anymore,
+	 * when proper integration of myExperiment plugin is made.
+	 *
+	 * @author Sergejs Aleksejevs
+	 */
+	public static void main(String[] args)
+	{
+	  JFrame frame = new JFrame("myExperiment Perspective Test");
+	  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+	  frame.setMinimumSize(new Dimension(1000, 700));
+	  frame.setLocation(300, 150);
+	  frame.getContentPane().add(new net.sf.taverna.t2.ui.perspectives.myexperiment.MainComponent(null, null));
+
+	  frame.pack();
+	  frame.setVisible(true);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/UploadWorkflowDialog.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/UploadWorkflowDialog.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/UploadWorkflowDialog.java
new file mode 100644
index 0000000..ed47183
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/UploadWorkflowDialog.java
@@ -0,0 +1,849 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JRootPane;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.License;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.ServerResponse;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Util;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Workflow;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.SaveWorkflowAsAction;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workflowmodel.Dataflow;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Emmanuel Tagarira, Sergejs Aleksejevs
+ */
+public class UploadWorkflowDialog extends HelpEnabledDialog implements ActionListener,
+		CaretListener, ComponentListener, KeyListener, FocusListener {
+	// components for accessing application's main elements
+	private final MainComponent pluginMainComponent = MainComponent.MAIN_COMPONENT;
+	private final MyExperimentClient myExperimentClient = MainComponent.MY_EXPERIMENT_CLIENT;
+	private final Logger logger = MainComponent.LOGGER;
+
+	// COMPONENTS
+	private JTextArea taDescription;
+	private JTextField tfTitle;
+	private JButton bUpload;
+	private JButton bCancel;
+	private JLabel lStatusMessage;
+	private JComboBox jcbLicences;
+	private JComboBox jcbSharingPermissions;
+	private String licence;
+	private String sharing;
+
+	// STORAGE
+	private File localWorkflowFile; // the local workflow file to be uploaded
+	private Resource updateResource; // the workflow resource that is to be
+	// updated
+	private File uploadFile; // THE UPLOAD FILE
+
+	private String strDescription = null;
+	private String strTitle = null;
+	private boolean bUploadingSuccessful = false;
+
+	// misc.
+	private int gridYPositionForStatusLabel;
+	private JRadioButton rbSelectLocalFile;
+	private JRadioButton rbSelectOpenWorkflow;
+	private JButton bSelectFile;
+	private JComboBox jcbOpenWorkflows;
+	private final JLabel selectedFileLabel = new JLabel("no wokflow file selected");
+	private boolean uploadWorkflowFromLocalFile;
+	JFileChooser jfsSelectFile = new JFileChooser();
+
+	private boolean userRequestedWorkflowUpload;
+	private final FileManager fileManager;
+
+	public UploadWorkflowDialog(JFrame parent, boolean doUpload, FileManager fileManager) {
+		super(parent, "Upload workflow to myExperiment", true);
+		this.fileManager = fileManager;
+		initVarsAndUI(doUpload, null);
+	}
+
+	public UploadWorkflowDialog(JFrame parent, boolean doUpload, Resource resource,
+			FileManager fileManager) {
+		super(parent, (doUpload ? "Upload new workflow version" : "Update workflow information"),
+				true);
+		this.fileManager = fileManager;
+		initVarsAndUI(doUpload, resource);
+	}
+
+	private void initVarsAndUI(boolean doUpload, Resource resource) {
+		// set the resource for which the comment is being added
+		this.updateResource = resource;
+		this.userRequestedWorkflowUpload = doUpload;
+
+		// set options of the 'add comment' dialog box
+		this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+
+		initialiseUI();
+		this.setMinimumSize(new Dimension(525, 375));
+	}
+
+	private JPanel createSelectSource() {
+		// create radio buttons
+		ButtonGroup radioButtons = new ButtonGroup();
+		rbSelectOpenWorkflow = new JRadioButton("Already Opened Workflow");
+		rbSelectOpenWorkflow.addFocusListener(this);
+		rbSelectLocalFile = new JRadioButton("Select Local File");
+		rbSelectLocalFile.addFocusListener(this);
+		radioButtons.add(rbSelectOpenWorkflow);
+		rbSelectOpenWorkflow.setSelected(true);
+		radioButtons.add(rbSelectLocalFile);
+
+		// create the source panel and add items
+		JPanel source = new JPanel(new GridBagLayout());
+		source.setBorder(BorderFactory.createTitledBorder("Workflow source"));
+
+		GridBagConstraints c = new GridBagConstraints();
+		c.anchor = GridBagConstraints.NORTHWEST;
+		c.gridy = 0;
+		c.gridx = 0;
+		c.gridwidth = 1;
+		c.weightx = 1;
+		c.insets = new Insets(3, 0, 3, 0);
+		c.fill = GridBagConstraints.BOTH;
+
+		// add info label
+		// JLabel info = new
+		// JLabel("Upload a workflow you would like to upload:");
+		// source.add(info, c);
+
+		// add open workflow radio button
+		c.gridy++;
+		source.add(rbSelectOpenWorkflow, c);
+		c.gridx = 1;
+		c.gridwidth = 2;
+		createDropdown();
+		source.add(jcbOpenWorkflows, c);
+
+		// add local file radio button
+		c.gridwidth = 1;
+		c.gridy++;
+		c.gridx = 0;
+		source.add(rbSelectLocalFile, c);
+		c.gridx = 1;
+		source.add(selectedFileLabel, c);
+		bSelectFile = new JButton(WorkbenchIcons.openIcon);
+		bSelectFile.addActionListener(this);
+		bSelectFile.setToolTipText("Select the file you would like to upload to myExperiment");
+		c.gridx = 2;
+		source.add(bSelectFile, c);
+
+		return source;
+	}
+
+	private void createDropdown() {
+		List<DataflowSelection> openDataflows = new ArrayList<DataflowSelection>();
+
+		int currentlyOpenedIndex = 0;
+		boolean foundIndex = false;
+
+		for (Dataflow df : fileManager.getOpenDataflows()) {
+			Object source = fileManager.getDataflowSource(df);
+
+			String name = "";
+			boolean getLocalName = source instanceof InputStream;
+			if (source != null)
+				name = (getLocalName ? df.getLocalName() : source.toString());
+
+			if (df.equals(fileManager.getCurrentDataflow())) {
+				name = "<html><body>" + name + " - " + " <i>(current)</i></body></html>";
+				foundIndex = true;
+			}
+
+			openDataflows.add(new DataflowSelection(df, name));
+			if (!foundIndex)
+				currentlyOpenedIndex++;
+		}
+
+		jcbOpenWorkflows = new JComboBox(openDataflows.toArray());
+		jcbOpenWorkflows.setSelectedIndex(currentlyOpenedIndex);
+	}
+
+	private JPanel createMetadataPanel() {
+		Insets fieldInset = new Insets(0, 5, 4, 5);
+		Insets labelInset = new Insets(3, 5, 4, 5);
+
+		GridBagConstraints c = new GridBagConstraints();
+		c.gridx = 0;
+		c.weightx = 1;
+		c.gridy = 0;
+		c.anchor = GridBagConstraints.WEST;
+		c.gridwidth = 2;
+		c.fill = GridBagConstraints.HORIZONTAL;
+
+		JPanel metaPanel = new JPanel(new GridBagLayout());
+		metaPanel.setBorder(BorderFactory.createTitledBorder("Workflow information"));
+
+		// title
+		JLabel lTitle = new JLabel("Workflow title:");
+		c.insets = labelInset;
+		c.gridy++;
+		metaPanel.add(lTitle, c);
+
+		this.tfTitle = new JTextField();
+		if (this.updateResource != null)
+			this.tfTitle.setText(this.updateResource.getTitle());
+		c.gridy++;
+		c.insets = fieldInset;
+		metaPanel.add(this.tfTitle, c);
+
+		// description
+		JLabel lDescription = new JLabel("Workflow description:");
+		c.gridy++;
+		c.insets = labelInset;
+		metaPanel.add(lDescription, c);
+
+		this.taDescription = new JTextArea(5, 35);
+		this.taDescription.setLineWrap(true);
+		this.taDescription.setWrapStyleWord(true);
+		if (this.updateResource != null)
+			this.taDescription.setText(this.updateResource.getDescription());
+
+		JScrollPane spDescription = new JScrollPane(this.taDescription);
+		c.gridy++;
+		c.insets = fieldInset;
+		metaPanel.add(spDescription, c);
+
+		// licences
+		String[] licenseText = new String[License.SUPPORTED_TYPES.length];
+		for (int x = 0; x < License.SUPPORTED_TYPES.length; x++)
+			licenseText[x] = License.getInstance(License.SUPPORTED_TYPES[x]).getText();
+
+		jcbLicences = new JComboBox(licenseText);
+    String defaultLicenseText = License.getInstance(License.DEFAULT_LICENSE)
+    .getText();
+    jcbLicences.setSelectedItem(defaultLicenseText);
+
+		if (this.updateResource != null) { // adding a new workflow
+			Workflow wf = (Workflow) this.updateResource;
+			String wfText = wf.getLicense().getText();
+			for (int x = 0; x < licenseText.length; x++)
+				if (wfText.equals(licenseText[x])) {
+					jcbLicences.setSelectedIndex(x);
+					break;
+				}
+		}
+
+		jcbLicences.addActionListener(this);
+		jcbLicences.setEditable(false);
+
+		JLabel lLicense = new JLabel("Please select the licence to apply:");
+		c.gridy++;
+		c.insets = labelInset;
+		metaPanel.add(lLicense, c);
+
+		c.gridy++;
+		c.insets = fieldInset;
+		metaPanel.add(jcbLicences, c);
+
+		// sharing - options: private / view / download
+		String[] permissions = { "This workflow is private.",
+				"Anyone can view, but noone can download.",
+				"Anyone can view, and anyone can download" };
+
+		this.jcbSharingPermissions = new JComboBox(permissions);
+
+		jcbSharingPermissions.addActionListener(this);
+		jcbSharingPermissions.setEditable(false);
+
+		JLabel jSharing = new JLabel("Please select your sharing permissions:");
+		c.gridy++;
+		c.insets = labelInset;
+		metaPanel.add(jSharing, c);
+
+		c.gridy++;
+		c.insets = fieldInset;
+		metaPanel.add(jcbSharingPermissions, c);
+
+		return metaPanel;
+	}
+
+	private void initialiseUI() {
+		// get content pane
+		Container contentPane = this.getContentPane();
+
+		Insets fieldInset = new Insets(3, 5, 3, 5);
+
+		// set up layout
+		contentPane.setLayout(new GridBagLayout());
+		GridBagConstraints c = new GridBagConstraints();
+		c.gridx = 0;
+		c.gridy = 0;
+		c.anchor = GridBagConstraints.NORTHWEST;
+		c.gridwidth = 2;
+		c.fill = GridBagConstraints.HORIZONTAL;
+
+		// ADD ALL COMPONENTS
+		// source for workflow to upload
+		if (userRequestedWorkflowUpload) {
+			c.insets = fieldInset;
+			contentPane.add(createSelectSource(), c);
+			c.gridy++;
+		}
+
+		// create metadata panel
+		contentPane.add(createMetadataPanel(), c);
+
+		// buttons
+		this.bUpload = new JButton(userRequestedWorkflowUpload ? "Upload Workflow"
+				: "Update Workflow");
+		this.bUpload.setDefaultCapable(true);
+		this.getRootPane().setDefaultButton(this.bUpload);
+		this.bUpload.addActionListener(this);
+		this.bUpload.addKeyListener(this);
+
+		c.gridy++;
+		c.anchor = GridBagConstraints.EAST;
+		c.gridwidth = 1;
+		c.fill = GridBagConstraints.NONE;
+		c.weightx = 0.5;
+		c.insets = new Insets(10, 5, 10, 5);
+		contentPane.add(bUpload, c);
+
+		this.bCancel = new JButton("Cancel");
+		this.bCancel.setPreferredSize(this.bUpload.getPreferredSize());
+		this.bCancel.addActionListener(this);
+		c.gridx = 1;
+		c.anchor = GridBagConstraints.WEST;
+		c.weightx = 0.5;
+		contentPane.add(bCancel, c);
+
+		this.pack();
+		this.addComponentListener(this);
+
+		gridYPositionForStatusLabel = c.gridy;
+	}
+
+	/**
+	 * Opens up a modal dialog where the user can enter the comment text. Window
+	 * is simply closed if 'Cancel' button is pressed; on pressing 'Post
+	 * Comment' button the window will turn into 'waiting' state, post the
+	 * comment and return the resulting XML document (which would contain the
+	 * newly added comment) back to the caller.
+	 *
+	 * @return String value of the non-empty comment text to be sent to
+	 *         myExperiment or null if action was cancelled.
+	 */
+	public boolean launchUploadDialogAndPostIfRequired() {
+		// makes the 'add comment' dialog visible, then waits until it is
+		// closed;
+		// control returns to this method when the dialog window is disposed
+		this.setVisible(true);
+		return (bUploadingSuccessful);
+	}
+
+	private File performSourceCheck() {
+		if (!rbSelectLocalFile.isSelected() && !rbSelectOpenWorkflow.isSelected()) { // it
+			// is
+			// logicall
+			// impossible
+			// to
+			// get
+			// this
+			// message,
+			// have
+			// it
+			// JUST
+			// IN
+			// CASE
+			JOptionPane.showConfirmDialog(this,
+					"You have not selected a source for you workflow.\n"
+							+ "Please select a source and try again.", "Select Workflow Source",
+					JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE);
+			return null;
+		}
+
+		if (rbSelectOpenWorkflow.isSelected()) { // user requested to use a flow
+			// currently open in t2
+			Dataflow dataflowToUpload = ((DataflowSelection) jcbOpenWorkflows.getSelectedItem())
+					.getDataflow();
+			SaveWorkflowAsAction saveAction = new SaveWorkflowAsAction(fileManager);
+
+			boolean skipPrompt = false;
+			if (fileManager.isDataflowChanged(dataflowToUpload)) { // if flow
+																	// has
+				// changed, prompt
+				// user to save
+				JOptionPane.showConfirmDialog(this, "The workflow you are trying to upload has\n"
+						+ "changed since the last time it was saved.\n\n"
+						+ "Please save your file to proceed...", "Save Workflow",
+						JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE);
+				saveAction.saveDataflow(this, dataflowToUpload);
+				skipPrompt = true;
+			}
+
+			File dataflowFile = (File) fileManager.getDataflowSource(dataflowToUpload);
+			if (dataflowFile == null && !skipPrompt) {
+				JOptionPane.showConfirmDialog(this, "You cannot upload an empty workflow.\n"
+						+ "Please select a different workflow before\n"
+						+ "you attempt the upload again.", "Upload Error",
+						JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE);
+				return null;
+			} else
+				return dataflowFile;
+
+		} else { // user wants to use local file
+			if (localWorkflowFile == null) {
+				JOptionPane.showConfirmDialog(this, "You have not selected a file to upload.\n"
+						+ "Please select a file and try again.", "Select Workflow Source",
+						JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE);
+				return null;
+			}
+
+			return localWorkflowFile;
+		}
+	}
+
+	private void getMetadata() {
+		// get sharing permission
+		switch (this.jcbSharingPermissions.getSelectedIndex()) {
+		case 0:
+			this.sharing = "private";
+			break;
+		case 1:
+			this.sharing = "view";
+			break;
+		case 2:
+			this.sharing = "download";
+			break;
+		}
+
+		// get licence
+		this.licence = License.SUPPORTED_TYPES[this.jcbLicences.getSelectedIndex()];
+
+		// get title
+		this.strTitle = this.tfTitle.getText();
+		this.strTitle = this.strTitle.trim();
+
+		// get description
+		this.strDescription = this.taDescription.getText();
+		this.strDescription = this.strDescription.trim();
+	}
+
+	public void actionPerformed(ActionEvent e) {
+		if (e.getSource().equals(this.bUpload)) { // * *** *** *** * UPLOAD
+													// BUTTON *
+			// *** *** *** *
+			// perform source check returns a file if attaining the source was
+			// successful
+			if (userRequestedWorkflowUpload) {
+				uploadFile = performSourceCheck();
+				if (uploadFile == null)
+					return;
+			}
+
+			// collect and put the metadata values in their respectable vars
+			getMetadata();
+
+			// if the description or the title are empty, prompt the user to
+			// confirm
+			// the upload
+			boolean proceedWithUpload = false;
+			if ((this.strDescription.length() == 0) && (this.strTitle.length() == 0)) {
+				String strInfo = "The workflow 'title' field and the 'description' field\n"
+						+ "(or both) are empty.  Any metadata found within the\n"
+						+ "workflow will be used instead.  Do you wish to proceed?";
+				int confirm = JOptionPane.showConfirmDialog(this, strInfo, "Empty fields",
+						JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE);
+				if (confirm == JOptionPane.YES_OPTION)
+					proceedWithUpload = true;
+			} else
+				proceedWithUpload = true;
+
+			if (proceedWithUpload) {
+				// the window will stay visible, but should turn into 'waiting'
+				// state
+				final JRootPane rootPane = this.getRootPane();
+				final Container contentPane = this.getContentPane();
+				contentPane.remove(this.bUpload);
+				contentPane.remove(this.bCancel);
+				if (this.lStatusMessage != null)
+					contentPane.remove(this.lStatusMessage);
+				this.taDescription.setEditable(false);
+
+				final GridBagConstraints c = new GridBagConstraints();
+				c.gridx = 0;
+				c.gridy = gridYPositionForStatusLabel;
+				c.gridwidth = 2;
+				c.anchor = GridBagConstraints.CENTER;
+				c.fill = GridBagConstraints.NONE;
+				c.insets = new Insets(10, 5, 10, 5);
+				lStatusMessage = new JLabel((updateResource == null ? "Uploading" : "Updating")
+						+ " your workflow...", new ImageIcon(
+						MyExperimentPerspective.getLocalResourceURL("spinner")),
+						SwingConstants.CENTER);
+				contentPane.add(lStatusMessage, c);
+
+				// disable the (X) button (ideally, would need to remove it, but
+				// there's
+				// no way to do this)
+				this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
+
+				// revalidate the window
+				this.pack();
+				this.validate();
+				this.repaint();
+
+				new Thread("Posting workflow") {
+					boolean formatRecognized = false;
+
+					@Override
+					public void run() {
+						String workflowFileContent = "";
+						if (userRequestedWorkflowUpload) {
+							if (uploadFile != null) {
+								try {
+									BufferedReader reader = new BufferedReader(new FileReader(
+											uploadFile));
+									String line;
+
+									String scuflSchemaDef = "xmlns:s=\"http://org.embl.ebi.escience/xscufl/0.1alpha\"";
+									String t2flowSchemaDef = "xmlns=\"http://taverna.sf.net/2008/xml/t2flow\"";
+
+									while ((line = reader.readLine()) != null) {
+										if (!formatRecognized
+												&& (line.contains(scuflSchemaDef) || line
+														.contains(t2flowSchemaDef)))
+											formatRecognized = true;
+
+										workflowFileContent += line + "\n";
+									}
+								} catch (Exception e) {
+									lStatusMessage = new JLabel("Error occurred:" + e.getMessage(),
+											new ImageIcon(MyExperimentPerspective
+													.getLocalResourceURL("failure_icon")),
+											SwingConstants.LEFT);
+									logger.error(e.getCause() + "\n" + e.getMessage());
+								}
+							}
+						}
+
+						// *** POST THE WORKFLOW ***
+						final ServerResponse response;
+
+						if ((userRequestedWorkflowUpload && formatRecognized)
+								|| !userRequestedWorkflowUpload) {
+							if (updateResource == null) // upload a new workflow
+								response = myExperimentClient.postWorkflow(workflowFileContent,
+										Util.stripAllHTML(strTitle),
+										Util.stripAllHTML(strDescription), licence, sharing);
+							else
+								// edit existing workflow
+								response = myExperimentClient.updateWorkflowVersionOrMetadata(
+										updateResource, workflowFileContent,
+										Util.stripAllHTML(strTitle),
+										Util.stripAllHTML(strDescription), licence, sharing);
+
+							bUploadingSuccessful = (response.getResponseCode() == HttpURLConnection.HTTP_OK);
+						} else {
+							bUploadingSuccessful = false;
+							response = null;
+						}
+
+						SwingUtilities.invokeLater(new Runnable() {
+							public void run() {
+								// *** REACT TO POSTING RESULT ***
+								if (bUploadingSuccessful) {
+									// workflow uploaded successfully
+									setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+
+									// disable all fields in dialog
+									tfTitle.setEnabled(false);
+									taDescription.setEnabled(false);
+									jcbLicences.setEnabled(false);
+									jcbSharingPermissions.setEnabled(false);
+									if (userRequestedWorkflowUpload) {
+										rbSelectOpenWorkflow.setEnabled(false);
+										rbSelectLocalFile.setEnabled(false);
+										selectedFileLabel.setEnabled(false);
+										bSelectFile.setEnabled(false);
+										jcbOpenWorkflows.setEnabled(false);
+									}
+									contentPane.remove(lStatusMessage);
+
+									c.insets = new Insets(10, 5, 5, 5);
+									lStatusMessage = new JLabel("Your workflow was successfully "
+											+ (updateResource == null ? "uploaded." : "updated."),
+											new ImageIcon(MyExperimentPerspective
+													.getLocalResourceURL("success_icon")),
+											SwingConstants.LEFT);
+									contentPane.add(lStatusMessage, c);
+
+									bCancel.setText("OK");
+									bCancel.setDefaultCapable(true);
+									rootPane.setDefaultButton(bCancel);
+									c.insets = new Insets(5, 5, 10, 5);
+									c.gridy++;
+									contentPane.add(bCancel, c);
+
+									pack();
+									bCancel.requestFocusInWindow();
+
+									// update uploaded items history making sure
+									// that:
+									// - there's only one occurrence of this
+									// item in the history;
+									// - if this item was in the history before,
+									// it is moved to
+									// the 'top' now;
+									// - predefined history size is not exceeded
+									MainComponent.MAIN_COMPONENT.getHistoryBrowser()
+											.getUploadedItemsHistoryList().remove(updateResource);
+									MainComponent.MAIN_COMPONENT.getHistoryBrowser()
+											.getUploadedItemsHistoryList().add(updateResource);
+									if (MainComponent.MAIN_COMPONENT.getHistoryBrowser()
+											.getUploadedItemsHistoryList().size() > HistoryBrowserTabContentPanel.UPLOADED_ITEMS_HISTORY_LENGTH) {
+										MainComponent.MAIN_COMPONENT.getHistoryBrowser()
+												.getUploadedItemsHistoryList().remove(0);
+									}
+
+									// now update the uploaded items history
+									// panel in 'History'
+									// tab
+									if (MainComponent.MAIN_COMPONENT.getHistoryBrowser() != null) {
+										MainComponent.MAIN_COMPONENT
+												.getHistoryBrowser()
+												.refreshHistoryBox(
+														HistoryBrowserTabContentPanel.UPLOADED_ITEMS_HISTORY);
+									}
+
+								} else {
+									// posting wasn't successful, notify the
+									// user
+									// and provide an option to close window or
+									// start again
+									setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+									taDescription.setEditable(true);
+									contentPane.remove(lStatusMessage);
+
+									c.insets = new Insets(10, 5, 5, 5);
+
+									String msg;
+									if (!formatRecognized)
+										msg = "Error occured: Invalid Taverna workflow.";
+									else
+										msg = "An error occured while processing your request.";
+
+									lStatusMessage = new JLabel(msg, new ImageIcon(
+											MyExperimentPerspective
+													.getLocalResourceURL("failure_icon")),
+											SwingConstants.LEFT);
+									contentPane.add(lStatusMessage, c);
+
+									bUpload.setText("Try again");
+									bUpload.setToolTipText("Please review your workflow or myExperiment base URL");
+									c.anchor = GridBagConstraints.EAST;
+									c.insets = new Insets(5, 5, 10, 5);
+									c.gridwidth = 1;
+									c.weightx = 0.5;
+									c.gridx = 0;
+									c.gridy++;
+									contentPane.add(bUpload, c);
+									rootPane.setDefaultButton(bUpload);
+
+									c.anchor = GridBagConstraints.WEST;
+									c.gridx = 1;
+									bCancel.setPreferredSize(bUpload.getPreferredSize());
+									contentPane.add(bCancel, c);
+
+									pack();
+									validate();
+									repaint();
+								}
+							}
+						});
+					}
+				}.start();
+			} // if proceedWithUpload
+		} else if (e.getSource().equals(this.bCancel)) { // * CANCEL BUTTON *
+			// cleanup the input fields if it wasn't posted successfully +
+			// simply
+			// close and destroy the window
+			if (!this.bUploadingSuccessful) {
+				this.strDescription = null;
+				this.tfTitle = null;
+			}
+			this.dispose();
+		} else if (e.getSource().equals(bSelectFile)) {// * SELECT FILE BUTTON *
+			// *
+			if (jfsSelectFile.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
+				localWorkflowFile = jfsSelectFile.getSelectedFile();
+
+				if (localWorkflowFile != null) {
+					selectedFileLabel.setText(localWorkflowFile.getAbsolutePath());
+					selectedFileLabel.setEnabled(true);
+				}
+				pack();
+			}
+		}
+	}
+
+	public void keyPressed(KeyEvent e) {
+		// if TAB was pressed in the text field (title), need to move keyboard
+		// focus
+		if (e.getSource().equals(this.tfTitle) || e.getSource().equals(this.taDescription)) {
+			if (e.getKeyCode() == KeyEvent.VK_TAB) {
+				if ((e.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) == KeyEvent.SHIFT_DOWN_MASK) {
+					// SHIFT + TAB move focus backwards
+					((Component) e.getSource()).transferFocusBackward();
+				} else {
+					// TAB moves focus forward
+					((Component) e.getSource()).transferFocus();
+				}
+				e.consume();
+			}
+		}
+	}
+
+	public void focusGained(FocusEvent e) {
+		if (e.getSource().equals(rbSelectLocalFile)) {
+			uploadWorkflowFromLocalFile = true;
+			bSelectFile.setEnabled(uploadWorkflowFromLocalFile);
+			jcbOpenWorkflows.setEnabled(!uploadWorkflowFromLocalFile);
+
+			if (localWorkflowFile != null) {
+				selectedFileLabel.setEnabled(uploadWorkflowFromLocalFile);
+				selectedFileLabel.setText(localWorkflowFile.getAbsolutePath());
+				pack();
+			} else
+				selectedFileLabel.setEnabled(!uploadWorkflowFromLocalFile);
+
+		} else if (e.getSource().equals(rbSelectOpenWorkflow)) {
+			uploadWorkflowFromLocalFile = false;
+			selectedFileLabel.setEnabled(uploadWorkflowFromLocalFile);
+			bSelectFile.setEnabled(uploadWorkflowFromLocalFile);
+			jcbOpenWorkflows.setEnabled(!uploadWorkflowFromLocalFile);
+		}
+	}
+
+	public void componentShown(ComponentEvent e) {
+		// center this dialog box within the preview browser window
+		if (updateResource == null) // upload has been pressed from the MAIN
+			// perspective window
+			Util.centerComponentWithinAnother(this.pluginMainComponent, this);
+		else
+			// upload pressed from resource preview window
+			Util.centerComponentWithinAnother(this.pluginMainComponent.getPreviewBrowser(), this);
+	}
+
+	public void focusLost(FocusEvent e) {
+		// not in use
+	}
+
+	public void caretUpdate(CaretEvent e) {
+		// not in use
+	}
+
+	public void componentHidden(ComponentEvent e) {
+		// not in use
+	}
+
+	public void componentMoved(ComponentEvent e) {
+		// not in use
+	}
+
+	public void keyReleased(KeyEvent e) {
+		// not in use
+	}
+
+	public void keyTyped(KeyEvent e) {
+		// not in use
+	}
+
+	public void componentResized(ComponentEvent e) {
+		// not in use
+	}
+
+	private class DataflowSelection {
+		private final Dataflow dataflow;
+		private final String name;
+
+		public DataflowSelection(Dataflow dataflow, String name) {
+			this.dataflow = dataflow;
+			this.name = name;
+		}
+
+		public Dataflow getDataflow() {
+			return dataflow;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		@Override
+		public String toString() {
+			return name;
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64$InputStream.class
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64$InputStream.class b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64$InputStream.class
new file mode 100644
index 0000000..a1a5f82
Binary files /dev/null and b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64$InputStream.class differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64$OutputStream.class
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64$OutputStream.class b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64$OutputStream.class
new file mode 100644
index 0000000..e754656
Binary files /dev/null and b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64$OutputStream.class differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64.class
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64.class b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64.class
new file mode 100644
index 0000000..84fcee9
Binary files /dev/null and b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64.class differ


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationPanel.java b/taverna-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationPanel.java
new file mode 100644
index 0000000..1229d57
--- /dev/null
+++ b/taverna-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationPanel.java
@@ -0,0 +1,582 @@
+/*******************************************************************************
+ * 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.httpproxy.config;
+
+import static java.awt.GridBagConstraints.BOTH;
+import static java.awt.GridBagConstraints.CENTER;
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.WEST;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
+import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED;
+import static net.sf.taverna.t2.workbench.helper.Helper.showHelp;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.PROXY_USE_OPTION;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.SYSTEM_NON_PROXY_HOSTS;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.SYSTEM_PROXY_HOST;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.SYSTEM_PROXY_PASSWORD;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.SYSTEM_PROXY_PORT;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.SYSTEM_PROXY_USER;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.TAVERNA_NON_PROXY_HOSTS;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.TAVERNA_PROXY_HOST;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.TAVERNA_PROXY_PASSWORD;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.TAVERNA_PROXY_PORT;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.TAVERNA_PROXY_USER;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.USE_NO_PROXY_OPTION;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.USE_SPECIFIED_VALUES_OPTION;
+import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.USE_SYSTEM_PROPERTIES_OPTION;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.lang.ui.DialogTextArea;
+import uk.org.taverna.configuration.proxy.HttpProxyConfiguration;
+
+/**
+ * The HttpProxyConfigurationPanel provides the user interface to a
+ * {@link HttpProxyConfiguration} to determine how HTTP Connections are made by
+ * Taverna.
+ * 
+ * @author alanrw
+ * @author David Withers
+ */
+public class HttpProxyConfigurationPanel extends JPanel {
+	static final long serialVersionUID = 3668473431971125038L;
+	/**
+	 * The size of the field for the JTextFields.
+	 */
+	private static int TEXTFIELD_SIZE = 25;
+
+	private final HttpProxyConfiguration httpProxyConfiguration;
+	/**
+	 * RadioButtons that are in a common ButtonGroup. Selecting one of them
+	 * indicates whether the system http proxy settings, the ad hoc specified
+	 * values or no proxy settings at all should be used.
+	 */
+	private JRadioButton useSystemProperties;
+	private JRadioButton useSpecifiedValues;
+	private JRadioButton useNoProxy;
+	/**
+	 * JTextFields and one DialogTextArea to hold the settings for the HTTP
+	 * proxy properties. The values are only editable if the user picks
+	 * useSpecifiedValues.
+	 */
+	private JTextField proxyHostField;
+	private JTextField proxyPortField;
+	private JTextField proxyUserField;
+	private JTextField proxyPasswordField;
+	private DialogTextArea nonProxyHostsArea;
+	private JScrollPane nonProxyScrollPane;
+	/**
+	 * A string that indicates which HTTP setting option the user has currently
+	 * picked. This does not necesarily match that which has been applied.
+	 */
+	private String shownOption = USE_SYSTEM_PROPERTIES_OPTION;
+
+	/**
+	 * The HttpProxyConfigurationPanel consists of a set of properties where the
+	 * configuration values for HTTP can be specified and a set of buttons where
+	 * the more general apply, help etc. appear.
+	 */
+	public HttpProxyConfigurationPanel(
+			HttpProxyConfiguration httpProxyConfiguration) {
+		this.httpProxyConfiguration = httpProxyConfiguration;
+		initComponents();
+	}
+
+	/**
+	 * Populates the panel with a representation of the current HTTP proxy
+	 * settings for the specified {@link HttpProxyConfiguration} and also the
+	 * capability to alter them.
+	 */
+	private void initComponents() {
+		shownOption = httpProxyConfiguration.getProperty(PROXY_USE_OPTION);
+
+		this.setLayout(new GridBagLayout());
+
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		// Title describing what kind of settings we are configuring here
+		JTextArea descriptionText = new JTextArea("HTTP proxy configuration");
+		descriptionText.setLineWrap(true);
+		descriptionText.setWrapStyleWord(true);
+		descriptionText.setEditable(false);
+		descriptionText.setFocusable(false);
+		descriptionText.setBorder(new EmptyBorder(10, 10, 10, 10));
+		gbc.anchor = WEST;
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.gridwidth = 2;
+		gbc.weightx = 1.0;
+		gbc.weighty = 0.0;
+		gbc.fill = HORIZONTAL;
+		this.add(descriptionText, gbc);
+
+		/**
+		 * Generate the three radio buttons and put them in a group. Each button
+		 * is bound to an action that alters the shownOption and re-populates
+		 * the shown HTTP property fields.
+		 */
+		useNoProxy = new JRadioButton("Do not use a proxy");
+		useNoProxy.setAlignmentX(LEFT_ALIGNMENT);
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.gridwidth = 2;
+		gbc.weightx = 0.0;
+		gbc.weighty = 0.0;
+		gbc.fill = NONE;
+		gbc.insets = new Insets(10, 0, 0, 0);
+		this.add(useNoProxy, gbc);
+		ActionListener useNoProxyListener = new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				shownOption = USE_NO_PROXY_OPTION;
+				populateFields();
+			}
+		};
+		useNoProxy.addActionListener(useNoProxyListener);
+
+		useSystemProperties = new JRadioButton("Use system properties");
+		useSystemProperties.setAlignmentX(LEFT_ALIGNMENT);
+		gbc.gridx = 0;
+		gbc.gridy = 2;
+		gbc.insets = new Insets(0, 0, 0, 0);
+		this.add(useSystemProperties, gbc);
+		ActionListener systemPropertiesListener = new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				shownOption = USE_SYSTEM_PROPERTIES_OPTION;
+				populateFields();
+			}
+		};
+		useSystemProperties.addActionListener(systemPropertiesListener);
+
+		useSpecifiedValues = new JRadioButton("Use specified values");
+		useSpecifiedValues.setAlignmentX(LEFT_ALIGNMENT);
+		gbc.gridx = 0;
+		gbc.gridy = 3;
+		this.add(useSpecifiedValues, gbc);
+		ActionListener specifiedValuesListener = new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				shownOption = USE_SPECIFIED_VALUES_OPTION;
+				populateFields();
+			}
+		};
+		useSpecifiedValues.addActionListener(specifiedValuesListener);
+
+		ButtonGroup bg = new ButtonGroup();
+		bg.add(useSystemProperties);
+		bg.add(useSpecifiedValues);
+		bg.add(useNoProxy);
+
+		/**
+		 * Create the fields to show the HTTP proxy property values. These
+		 * become editable if the shown option is to use specified values.
+		 */
+		proxyHostField = new JTextField(TEXTFIELD_SIZE);
+		gbc.gridx = 0;
+		gbc.gridy = 4;
+		gbc.gridwidth = 1;
+		gbc.fill = NONE;
+		gbc.insets = new Insets(10, 0, 0, 0);
+		this.add(new JLabel("Proxy host"), gbc);
+		gbc.gridx = 1;
+		gbc.gridy = 4;
+		gbc.gridwidth = 1;
+		gbc.fill = HORIZONTAL;
+		this.add(proxyHostField, gbc);
+
+		proxyPortField = new JTextField(TEXTFIELD_SIZE);
+		gbc.gridx = 0;
+		gbc.gridy = 5;
+		gbc.gridwidth = 1;
+		gbc.fill = NONE;
+		gbc.insets = new Insets(0, 0, 0, 0);
+		this.add(new JLabel("Proxy port"), gbc);
+		gbc.gridx = 1;
+		gbc.gridy = 5;
+		gbc.gridwidth = 1;
+		gbc.fill = HORIZONTAL;
+		this.add(proxyPortField, gbc);
+
+		proxyUserField = new JTextField(TEXTFIELD_SIZE);
+		gbc.gridx = 0;
+		gbc.gridy = 6;
+		gbc.gridwidth = 1;
+		gbc.fill = NONE;
+		this.add(new JLabel("Proxy user"), gbc);
+		gbc.gridx = 1;
+		gbc.gridy = 6;
+		gbc.gridwidth = 1;
+		gbc.fill = HORIZONTAL;
+		this.add(proxyUserField, gbc);
+
+		proxyPasswordField = new JTextField(TEXTFIELD_SIZE);
+		gbc.gridx = 0;
+		gbc.gridy = 7;
+		gbc.gridwidth = 1;
+		gbc.fill = NONE;
+		this.add(new JLabel("Proxy password"), gbc);
+		gbc.gridx = 1;
+		gbc.gridy = 7;
+		gbc.gridwidth = 1;
+		gbc.fill = HORIZONTAL;
+		this.add(proxyPasswordField, gbc);
+
+		nonProxyHostsArea = new DialogTextArea(10, 40);
+		nonProxyScrollPane = new JScrollPane(nonProxyHostsArea);
+		nonProxyScrollPane
+				.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		nonProxyScrollPane
+				.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
+		// nonProxyScrollPane.setPreferredSize(new Dimension(300, 500));
+		gbc.gridx = 0;
+		gbc.gridy = 8;
+		gbc.gridwidth = 2;
+		gbc.fill = NONE;
+		gbc.insets = new Insets(10, 0, 0, 0);
+		this.add(new JLabel("Non-proxy hosts"), gbc);
+		gbc.gridx = 0;
+		gbc.gridy = 9;
+		gbc.weightx = 1.0;
+		gbc.weighty = 1.0;
+		gbc.gridwidth = 2;
+		gbc.insets = new Insets(0, 0, 0, 0);
+		gbc.fill = BOTH;
+		this.add(nonProxyScrollPane, gbc);
+
+		// Add buttons panel
+		gbc.gridx = 0;
+		gbc.gridy = 10;
+		gbc.weightx = 0.0;
+		gbc.weighty = 0.0;
+		gbc.gridwidth = 2;
+		gbc.fill = HORIZONTAL;
+		gbc.anchor = CENTER;
+		gbc.insets = new Insets(10, 0, 0, 0);
+		this.add(createButtonPanel(), gbc);
+
+		setFields();
+	}
+
+	/**
+	 * Populate the fields in the property panel according to which option is
+	 * being shown and the stored values within the
+	 * {@link HttpProxyConfiguration}.
+	 */
+	private void populateFields() {
+		/**
+		 * Editing of the property fields is only available when the option is
+		 * to use the specified values.
+		 */
+		boolean editingEnabled = shownOption
+				.equals(USE_SPECIFIED_VALUES_OPTION);
+
+		if (shownOption.equals(USE_SYSTEM_PROPERTIES_OPTION)) {
+			proxyHostField.setText(httpProxyConfiguration
+					.getProperty(SYSTEM_PROXY_HOST));
+			proxyPortField.setText(httpProxyConfiguration
+					.getProperty(SYSTEM_PROXY_PORT));
+			proxyUserField.setText(httpProxyConfiguration
+					.getProperty(SYSTEM_PROXY_USER));
+			proxyPasswordField.setText(httpProxyConfiguration
+					.getProperty(SYSTEM_PROXY_PASSWORD));
+			nonProxyHostsArea.setText(httpProxyConfiguration
+					.getProperty(SYSTEM_NON_PROXY_HOSTS));
+		} else if (shownOption.equals(USE_SPECIFIED_VALUES_OPTION)) {
+			proxyHostField.setText(httpProxyConfiguration
+					.getProperty(TAVERNA_PROXY_HOST));
+			proxyPortField.setText(httpProxyConfiguration
+					.getProperty(TAVERNA_PROXY_PORT));
+			proxyUserField.setText(httpProxyConfiguration
+					.getProperty(TAVERNA_PROXY_USER));
+			proxyPasswordField.setText(httpProxyConfiguration
+					.getProperty(TAVERNA_PROXY_PASSWORD));
+			nonProxyHostsArea.setText(httpProxyConfiguration
+					.getProperty(TAVERNA_NON_PROXY_HOSTS));
+		} else {
+			proxyHostField.setText(null);
+			proxyPortField.setText(null);
+			proxyUserField.setText(null);
+			proxyPasswordField.setText(null);
+			nonProxyHostsArea.setText(null);
+		}
+
+		proxyHostField.setEnabled(editingEnabled);
+		proxyPortField.setEnabled(editingEnabled);
+		proxyUserField.setEnabled(editingEnabled);
+		proxyPasswordField.setEnabled(editingEnabled);
+		nonProxyHostsArea.setEnabled(editingEnabled);
+		nonProxyHostsArea.setEditable(editingEnabled);
+		nonProxyScrollPane.setEnabled(editingEnabled);
+	}
+
+	/**
+	 * Create the panel to contain the buttons
+	 * 
+	 * @return
+	 */
+	@SuppressWarnings("serial")
+	private JPanel createButtonPanel() {
+		final JPanel panel = new JPanel();
+
+		/**
+		 * The helpButton shows help about the current component
+		 */
+		JButton helpButton = new JButton(new AbstractAction("Help") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				showHelp(panel);
+			}
+		});
+		panel.add(helpButton);
+
+		/**
+		 * The resetButton changes the property values shown to those
+		 * corresponding to the configuration currently applied.
+		 */
+		JButton resetButton = new JButton(new AbstractAction("Reset") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				setFields();
+			}
+		});
+		panel.add(resetButton);
+
+		/**
+		 * The applyButton applies the shown field values to the
+		 * {@link HttpProxyConfiguration} and saves them for future.
+		 */
+		JButton applyButton = new JButton(new AbstractAction("Apply") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				applySettings();
+				setFields();
+			}
+		});
+		panel.add(applyButton);
+
+		return panel;
+	}
+
+	/**
+	 * Checks that the specified values for the HTTP properties are a valid
+	 * combination and, if so, saves them for future use. It does not apply them
+	 * to the currently executing Taverna.
+	 */
+	private void saveSettings() {
+		if (useSystemProperties.isSelected()) {
+			httpProxyConfiguration.setProperty(PROXY_USE_OPTION,
+					USE_SYSTEM_PROPERTIES_OPTION);
+		} else if (useNoProxy.isSelected()) {
+			httpProxyConfiguration.setProperty(PROXY_USE_OPTION,
+					USE_NO_PROXY_OPTION);
+		} else {
+			if (validateFields()) {
+				httpProxyConfiguration.setProperty(PROXY_USE_OPTION,
+						USE_SPECIFIED_VALUES_OPTION);
+				httpProxyConfiguration.setProperty(TAVERNA_PROXY_HOST,
+						proxyHostField.getText());
+				httpProxyConfiguration.setProperty(TAVERNA_PROXY_PORT,
+						proxyPortField.getText());
+				httpProxyConfiguration.setProperty(TAVERNA_PROXY_USER,
+						proxyUserField.getText());
+				httpProxyConfiguration.setProperty(TAVERNA_PROXY_PASSWORD,
+						proxyPasswordField.getText());
+				httpProxyConfiguration.setProperty(TAVERNA_NON_PROXY_HOSTS,
+						nonProxyHostsArea.getText());
+			}
+		}
+	}
+
+	/**
+	 * Validates and, where appropriate formats, the properties values specified
+	 * for HTTP Proxy configuration.
+	 * 
+	 * @return
+	 */
+	private boolean validateFields() {
+		boolean result = true;
+		result = result && validateHostField();
+		result = result && validatePortField();
+		result = result && validateUserField();
+		result = result && validatePasswordField();
+		result = result && validateNonProxyHostsArea();
+		return result;
+	}
+
+	/**
+	 * Checks that, if a value is specified for non-proxy hosts then a proxy
+	 * host has also been specified. Formats the non-proxy hosts string so that
+	 * if the user has entered the hosts on separate lines, then the stored
+	 * values are separated by bars.
+	 * 
+	 * @return
+	 */
+	private boolean validateNonProxyHostsArea() {
+		boolean result = true;
+		String value = nonProxyHostsArea.getText();
+		if ((value != null) && (!value.equals(""))) {
+			value = value.replaceAll("\\n", "|");
+			nonProxyHostsArea.setText(value);
+			result = result
+					&& dependsUpon("non-proxy host", "host",
+							proxyHostField.getText());
+		}
+		return result;
+	}
+
+	/**
+	 * Checks that, if a password has been specified, then a user has also been
+	 * specified.
+	 * 
+	 * @return
+	 */
+	private boolean validatePasswordField() {
+		boolean result = true;
+		String value = proxyPasswordField.getText();
+		if ((value != null) && !value.isEmpty())
+			result = result
+					&& dependsUpon("password", "user", proxyHostField.getText());
+		return result;
+	}
+
+	/**
+	 * Checks that if a user has been specified, then a host has also been
+	 * specified.
+	 * 
+	 * @return
+	 */
+	private boolean validateUserField() {
+		boolean result = true;
+		String value = proxyUserField.getText();
+		if ((value != null) && !value.isEmpty())
+			result = result
+					&& dependsUpon("user", "host", proxyHostField.getText());
+		return result;
+	}
+
+	/**
+	 * Checks that if a port has been specified then a host has also been
+	 * specified. Checks that the port number is a non-negative integer. If the
+	 * port has not been specified, then if a host has been specified, the
+	 * default value 80 is used.
+	 * 
+	 * @return
+	 */
+	private boolean validatePortField() {
+		boolean result = true;
+		String value = proxyPortField.getText();
+		if ((value != null) && (!value.equals(""))) {
+			result = result
+					&& dependsUpon("port", "host", proxyHostField.getText());
+			try {
+				int parsedNumber = Integer.parseInt(value);
+				if (parsedNumber <= 0) {
+					showMessageDialog(this, "The port must be non-negative");
+					result = false;
+				}
+			} catch (NumberFormatException e) {
+				showMessageDialog(this, "The port must be an integer");
+				result = false;
+			}
+		} else {
+			String hostField = proxyHostField.getText();
+			if ((hostField != null) && !hostField.isEmpty())
+				proxyPortField.setText("80");
+		}
+		return result;
+	}
+
+	/**
+	 * Checks if the targetValue has been specified. If not then a message is
+	 * displayed indicating that the dependent cannot be specified with the
+	 * target.
+	 * 
+	 * @param dependent
+	 * @param target
+	 * @param targetValue
+	 * @return
+	 */
+	private boolean dependsUpon(String dependent, String target,
+			String targetValue) {
+		boolean result = true;
+		if ((targetValue == null) || target.equals("")) {
+			showMessageDialog(this, "A " + dependent
+					+ " cannot be specified without a " + target);
+			result = false;
+		}
+		return result;
+	}
+
+	/**
+	 * Could validate the host field e.g. by establishing a connection.
+	 * Currently no validation is done.
+	 * 
+	 * @return
+	 */
+	private boolean validateHostField() {
+		boolean result = true;
+		// String value = proxyHostField.getText();
+		return result;
+	}
+
+	/**
+	 * Save the currently set field values (if valid) to the
+	 * {@link HttpProxyConfiguration}. Also applies those values to the
+	 * currently running Taverna.
+	 */
+	private void applySettings() {
+		if (validateFields()) {
+			saveSettings();
+			httpProxyConfiguration.changeProxySettings();
+		}
+	}
+
+	/**
+	 * Set the shown field values to those currently in use (i.e. last saved
+	 * configuration).
+	 */
+	private void setFields() {
+		shownOption = httpProxyConfiguration.getProperty(PROXY_USE_OPTION);
+		useSystemProperties.setSelected(shownOption
+				.equals(USE_SYSTEM_PROPERTIES_OPTION));
+		useSpecifiedValues.setSelected(shownOption
+				.equals(USE_SPECIFIED_VALUES_OPTION));
+		useNoProxy.setSelected(shownOption.equals(USE_NO_PROXY_OPTION));
+		populateFields();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationUIFactory.java
----------------------------------------------------------------------
diff --git a/taverna-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationUIFactory.java b/taverna-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationUIFactory.java
new file mode 100644
index 0000000..9f6ac8c
--- /dev/null
+++ b/taverna-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationUIFactory.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.httpproxy.config;
+
+import javax.swing.JPanel;
+
+import uk.org.taverna.configuration.Configurable;
+import uk.org.taverna.configuration.ConfigurationUIFactory;
+import uk.org.taverna.configuration.proxy.HttpProxyConfiguration;
+
+/**
+ * A Factory to create a HttpProxyConfiguration
+ *
+ * @author alanrw
+ * @author David Withers
+ */
+public class HttpProxyConfigurationUIFactory implements ConfigurationUIFactory {
+	private HttpProxyConfiguration httpProxyConfiguration;
+
+	@Override
+	public boolean canHandle(String uuid) {
+		return uuid.equals(getConfigurable().getUUID());
+	}
+
+	@Override
+	public JPanel getConfigurationPanel() {
+		return new HttpProxyConfigurationPanel(httpProxyConfiguration);
+	}
+
+	@Override
+	public Configurable getConfigurable() {
+		return httpProxyConfiguration;
+	}
+
+	public void setHttpProxyConfiguration(HttpProxyConfiguration httpProxyConfiguration) {
+		this.httpProxyConfiguration = httpProxyConfiguration;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-httpproxy-config/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
----------------------------------------------------------------------
diff --git a/taverna-httpproxy-config/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-httpproxy-config/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
new file mode 100644
index 0000000..d87772b
--- /dev/null
+++ b/taverna-httpproxy-config/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.httpproxy.config.HttpProxyConfigurationUIFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context-osgi.xml b/taverna-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context-osgi.xml
new file mode 100644
index 0000000..631bdb4
--- /dev/null
+++ b/taverna-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context-osgi.xml
@@ -0,0 +1,13 @@
+<?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="HttpProxyConfigurationUIFactory" interface="uk.org.taverna.configuration.ConfigurationUIFactory" />
+
+	<reference id="httpProxyConfiguration" interface="uk.org.taverna.configuration.proxy.HttpProxyConfiguration" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context.xml
----------------------------------------------------------------------
diff --git a/taverna-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context.xml b/taverna-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context.xml
new file mode 100644
index 0000000..6d6060f
--- /dev/null
+++ b/taverna-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context.xml
@@ -0,0 +1,10 @@
+<?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="HttpProxyConfigurationUIFactory" class="net.sf.taverna.t2.workbench.httpproxy.config.HttpProxyConfigurationUIFactory">
+		<property name="httpProxyConfiguration" ref="httpProxyConfiguration" />
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/pom.xml b/taverna-iteration-strategy-ui/pom.xml
new file mode 100644
index 0000000..ce47f16
--- /dev/null
+++ b/taverna-iteration-strategy-ui/pom.xml
@@ -0,0 +1,79 @@
+<?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>iteration-strategy-ui</artifactId>
+	<packaging>bundle</packaging>
+	<name>Menu generation API</name>
+	<description>An SPI system for building UI menues</description>
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>workflowmodel-api</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.taverna.t2.lang</groupId>
+			<artifactId>ui</artifactId>
+			<version>${t2.lang.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>contextual-views-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>file-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>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>workflowmodel-impl</artifactId>
+			<version>${taverna.engine.version}</version>
+			<!-- <scope>test</scope> -->
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/IterationStrategyIcons.java
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/IterationStrategyIcons.java b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/IterationStrategyIcons.java
new file mode 100644
index 0000000..350c0cc
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/IterationStrategyIcons.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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;
+
+import javax.swing.ImageIcon;
+
+import org.apache.log4j.Logger;
+
+public class IterationStrategyIcons {
+
+	private static Logger logger = Logger
+			.getLogger(IterationStrategyIcons.class);
+
+	public static ImageIcon joinIteratorIcon, lockStepIteratorIcon,
+			leafnodeicon;
+
+	static {
+		try {
+			Class<?> c = IterationStrategyIcons.class;
+			joinIteratorIcon = new ImageIcon(c
+					.getResource("icons/crossproducticon.png"));
+			lockStepIteratorIcon = new ImageIcon(c
+					.getResource("icons/dotproducticon.png"));
+			leafnodeicon = new ImageIcon(c
+					.getResource("icons/leafnodeicon.png"));
+		} catch (Exception ex) {
+			logger.warn("Could not find icon", ex);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyConfigurationDialog.java
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyConfigurationDialog.java b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyConfigurationDialog.java
new file mode 100644
index 0000000..1af83cb
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyConfigurationDialog.java
@@ -0,0 +1,148 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.iterationstrategy.contextview;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+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.helper.HelpEnabledDialog;
+import net.sf.taverna.t2.workbench.iterationstrategy.editor.IterationStrategyEditorControl;
+import net.sf.taverna.t2.workflowmodel.Edit;
+import net.sf.taverna.t2.workflowmodel.EditException;
+import net.sf.taverna.t2.workflowmodel.Edits;
+import net.sf.taverna.t2.workflowmodel.Processor;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategy;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategyStack;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author alanrw
+ *
+ */
+@SuppressWarnings("serial")
+public class IterationStrategyConfigurationDialog extends HelpEnabledDialog {
+
+	private static Logger logger = Logger
+	.getLogger(IterationStrategyConfigurationDialog.class);
+
+	private final EditManager editManager;
+	private final FileManager fileManager;
+
+
+	private final Frame owner;
+	private final Processor processor;
+	private final IterationStrategyStack originalStack;
+
+	private IterationStrategyStack workingStack;
+
+	public IterationStrategyConfigurationDialog(Frame owner, Processor processor, IterationStrategyStack iStack, EditManager editManager, FileManager fileManager) {
+		super (owner, "List handling for " + processor.getLocalName(), true, null);
+		this.owner = owner;
+		this.processor = processor;
+		this.originalStack = iStack;
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+		this.workingStack = IterationStrategyContextualView.copyIterationStrategyStack(originalStack);
+		IterationStrategy iterationStrategy = IterationStrategyContextualView.getIterationStrategy(workingStack);
+		IterationStrategyEditorControl iterationStrategyEditorControl = new IterationStrategyEditorControl(
+				iterationStrategy);
+		this.add(iterationStrategyEditorControl, BorderLayout.CENTER);
+
+		JPanel buttonPanel = new JPanel();
+		buttonPanel.setLayout(new FlowLayout());
+
+		JButton okButton = new JButton(new OKAction(this));
+		buttonPanel.add(okButton);
+
+		JButton resetButton = new JButton(new ResetAction(
+				iterationStrategyEditorControl));
+		buttonPanel.add(resetButton);
+
+		JButton cancelButton = new JButton(new CancelAction(this));
+		buttonPanel.add(cancelButton);
+
+		this.add(buttonPanel, BorderLayout.SOUTH);
+		this.pack();
+		this.setSize(new Dimension(getPreferredSize().width, getPreferredSize().height > 400 ? 400 : getPreferredSize().height));
+	}
+
+	private final class OKAction extends AbstractAction {
+		private final JDialog dialog;
+
+		private OKAction(JDialog dialog) {
+			super("OK");
+			this.dialog = dialog;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			Edits edits = editManager.getEdits();
+			try {
+				Edit<?> edit = edits.getSetIterationStrategyStackEdit(
+						processor,
+						IterationStrategyContextualView.copyIterationStrategyStack(workingStack));
+				editManager.doDataflowEdit(
+						fileManager.getCurrentDataflow(), edit);
+				dialog.setVisible(false);
+			} catch (RuntimeException ex) {
+				logger.warn("Could not set list handling", ex);
+				JOptionPane.showMessageDialog(owner,
+						"Can't set list handling",
+						"An error occured when setting list handling: "
+								+ ex.getMessage(),
+						JOptionPane.ERROR_MESSAGE);
+			} catch (EditException ex) {
+				logger.warn("Could not set list handling", ex);
+				JOptionPane.showMessageDialog(owner,
+						"Can't set list handling",
+						"An error occured when setting list handling: "
+								+ ex.getMessage(),
+						JOptionPane.ERROR_MESSAGE);
+			}
+		}
+	}
+
+	private final class ResetAction extends AbstractAction {
+		private final IterationStrategyEditorControl strategyEditorControl;
+
+		private ResetAction(
+				IterationStrategyEditorControl strategyEditorControl) {
+			super("Reset");
+			this.strategyEditorControl = strategyEditorControl;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			workingStack = IterationStrategyContextualView.copyIterationStrategyStack(originalStack);
+			strategyEditorControl
+					.setIterationStrategy(IterationStrategyContextualView.getIterationStrategy(workingStack));
+		}
+
+	}
+
+	private final class CancelAction extends AbstractAction {
+		private final JDialog dialog;
+
+		private CancelAction(JDialog dialog) {
+			super("Cancel");
+			this.dialog = dialog;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			dialog.setVisible(false);
+		}
+
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualView.java b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualView.java
new file mode 100644
index 0000000..369bea4
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualView.java
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * 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.contextview;
+
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JComponent;
+
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+import net.sf.taverna.t2.workbench.iterationstrategy.editor.IterationStrategyTree;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workflowmodel.Processor;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategy;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategyStack;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.impl.IterationStrategyImpl;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.impl.IterationStrategyStackImpl;
+
+import org.apache.log4j.Logger;
+import org.jdom.Content;
+import org.jdom.Element;
+import org.jdom.filter.ElementFilter;
+
+/**
+ * Contextual view of an {@link IterationStrategyStack}.
+ *
+ * @author Stian Soiland-Reyes
+ *
+ */
+public class IterationStrategyContextualView extends ContextualView {
+
+	private static Logger logger = Logger
+			.getLogger(IterationStrategyContextualView.class);
+
+	private EditManager editManager;
+
+	private FileManager fileManager;
+
+	private IterationStrategyStack iterationStack;
+
+	private final Processor processor;
+
+	private IterationStrategyTree strategyTree = new IterationStrategyTree();
+
+	static {
+
+// This should be enabled and modified for T2-822
+/*		editManager.addObserver(new Observer<EditManagerEvent> () {
+
+			private void examineEdit(Edit edit) {
+				if (edit instanceof ConnectDatalinkEdit) {
+					processConnectDatalinkEdit((ConnectDatalinkEdit) edit);
+				}
+				if (edit instanceof CompoundEdit) {
+					processCompoundEdit((CompoundEdit) edit);
+				}
+			}
+
+			private void processConnectDatalinkEdit(ConnectDatalinkEdit edit) {
+				Datalink d = ((ConnectDatalinkEdit) edit).getSubject();
+				EventHandlingInputPort sink = d.getSink();
+				if (sink instanceof ProcessorInputPort) {
+					ProcessorInputPort pip = (ProcessorInputPort) sink;
+					Processor p = pip.getProcessor();
+					final HelpEnabledDialog dialog = new IterationStrategyConfigurationDialog(null, p, copyIterationStrategyStack(p.getIterationStrategy()));
+					dialog.setVisible(true);
+				}
+			}
+
+			private void processCompoundEdit(CompoundEdit edit) {
+				for (Edit e : edit.getChildEdits()) {
+					examineEdit(e);
+				}
+			}
+
+			@Override
+			public void notify(Observable<EditManagerEvent> sender,
+					EditManagerEvent message) throws Exception {
+				if (!(message instanceof DataflowEditEvent)) {
+					return;
+				}
+				examineEdit(message.getEdit());
+			}});*/
+	}
+
+	public IterationStrategyContextualView(Processor processor, EditManager editManager, FileManager fileManager) {
+		if (processor == null || processor.getIterationStrategy() == null) {
+			throw new NullPointerException(
+					"Iteration strategy stack can't be null");
+		}
+		this.processor = processor;
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+		refreshIterationStrategyStack();
+		initView();
+	}
+
+	@Override
+	public Action getConfigureAction(final Frame owner) {
+		return new ConfigureIterationStrategyAction(owner);
+	}
+
+	public Processor getProcessor() {
+		return processor;
+	}
+
+	@Override
+	public void refreshView() {
+		refreshIterationStrategyStack();
+		strategyTree.setIterationStrategy(getIterationStrategy(iterationStack));
+	}
+
+	public static IterationStrategyStack copyIterationStrategyStack(
+			IterationStrategyStack stack) {
+		Element asXML = ((IterationStrategyStackImpl)stack).asXML();
+		stripEmptyElements(asXML);
+		IterationStrategyStackImpl copyStack = new IterationStrategyStackImpl();
+		copyStack.configureFromElement(asXML);
+		if (copyStack.getStrategies().isEmpty()) {
+			copyStack.addStrategy(new IterationStrategyImpl());
+		}
+		return copyStack;
+	}
+
+	private static void stripEmptyElements(Element asXML) {
+		int childCount = asXML.getContent().size();
+		int index = 0;
+		while (index < childCount) {
+			Content child = asXML.getContent(index);
+			if (child instanceof Element) {
+				Element childElement = (Element) child;
+				if (childElement.getName().equals("port")) {
+					index++;
+				}
+				else if (childElement.getDescendants(new ElementFilter("port")).hasNext()) {
+					stripEmptyElements(childElement);
+					index++;
+				} else {
+					asXML.removeContent(childElement);
+					childCount--;
+				}
+			}
+		}
+	}
+
+	public static IterationStrategy getIterationStrategy(IterationStrategyStack iStack) {
+		List<? extends IterationStrategy> strategies = iStack
+				.getStrategies();
+		if (strategies.isEmpty()) {
+			throw new IllegalStateException("Empty iteration stack");
+		}
+		IterationStrategy strategy = strategies.get(0);
+		if (!(strategy instanceof IterationStrategyImpl)) {
+			throw new IllegalStateException(
+					"Can't edit unknown iteration strategy implementation "
+							+ strategy);
+		}
+		return (IterationStrategyImpl) strategy;
+	}
+
+	private void refreshIterationStrategyStack() {
+		IterationStrategyStack originalIterationStrategy = processor
+				.getIterationStrategy();
+		if (!(originalIterationStrategy instanceof IterationStrategyStackImpl)) {
+			throw new IllegalStateException(
+					"Unknown iteration strategy implementation "
+							+ originalIterationStrategy);
+		}
+		this.iterationStack = copyIterationStrategyStack((IterationStrategyStackImpl) originalIterationStrategy);
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		refreshView();
+		return strategyTree;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "List handling";
+	}
+
+	private final class ConfigureIterationStrategyAction extends AbstractAction {
+		private final Frame owner;
+
+		private ConfigureIterationStrategyAction(Frame owner) {
+			super("Configure");
+			this.owner = owner;
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			final HelpEnabledDialog dialog = new IterationStrategyConfigurationDialog(owner, processor, iterationStack, editManager, fileManager);
+			dialog.setVisible(true);
+			refreshView();
+		}
+
+
+
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 200;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualViewFactory.java b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualViewFactory.java
new file mode 100644
index 0000000..a970239
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualViewFactory.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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.contextview;
+
+import java.util.Arrays;
+import java.util.List;
+
+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;
+import net.sf.taverna.t2.workflowmodel.Processor;
+
+public class IterationStrategyContextualViewFactory implements
+		ContextualViewFactory<Processor> {
+
+	private EditManager editManager;
+	private FileManager fileManager;
+
+	public boolean canHandle(Object selection) {
+		return selection instanceof Processor;
+	}
+
+	public List<ContextualView> getViews(Processor p) {
+		return Arrays.asList(new ContextualView[] {new IterationStrategyContextualView(p, 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-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyCellRenderer.java b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyCellRenderer.java
new file mode 100644
index 0000000..4c31574
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyCellRenderer.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * 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 java.awt.Component;
+
+import javax.swing.JTree;
+import javax.swing.tree.DefaultTreeCellRenderer;
+
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.iterationstrategy.IterationStrategyIcons;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.CrossProduct;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.DotProduct;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.NamedInputPortNode;
+
+import org.apache.log4j.Logger;
+
+@SuppressWarnings("serial")
+final class IterationStrategyCellRenderer extends DefaultTreeCellRenderer {
+
+	@SuppressWarnings("unused")
+	private static Logger logger = Logger
+			.getLogger(IterationStrategyCellRenderer.class);
+
+	@Override
+	public Component getTreeCellRendererComponent(JTree tree, Object value,
+			boolean selected, boolean expanded, boolean leaf, int row,
+			boolean hasFocus) {
+		super.getTreeCellRendererComponent(tree, value, selected, expanded,
+				leaf, row, hasFocus);
+		if (value instanceof CrossProduct) {
+			setIcon(IterationStrategyIcons.joinIteratorIcon);
+			setText("Cross product");
+		} else if (value instanceof DotProduct) {
+			setIcon(IterationStrategyIcons.lockStepIteratorIcon);
+			setText("Dot product");
+		} else if (value instanceof NamedInputPortNode) {
+			setIcon(IterationStrategyIcons.leafnodeicon);
+			NamedInputPortNode namedInput = (NamedInputPortNode) value;
+			setText(namedInput.getPortName());
+		} else {
+			setText("List handling");
+			if (!leaf){
+				if (expanded) {
+					setIcon(WorkbenchIcons.folderOpenIcon);
+				} else {
+					setIcon(WorkbenchIcons.folderClosedIcon);
+				}
+			}
+		}
+		return this;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditor.java
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditor.java b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditor.java
new file mode 100644
index 0000000..add5201
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditor.java
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * 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 java.awt.GraphicsEnvironment;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+//import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+import javax.swing.DropMode;
+import javax.swing.JComponent;
+import javax.swing.JTree;
+import javax.swing.TransferHandler;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.AbstractIterationStrategyNode;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategy;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.NamedInputPortNode;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.TerminalNode;
+
+import org.apache.log4j.Logger;
+
+@SuppressWarnings("serial")
+public class IterationStrategyEditor extends IterationStrategyTree implements
+		UIComponentSPI {
+
+	private static Logger logger = Logger
+			.getLogger(IterationStrategyEditor.class);
+
+	//private BufferedImage imgGhost; // The 'drag image'
+
+	// mouse was clicked
+
+	public IterationStrategyEditor() {
+		super();
+		// Make this a drag source
+		if (!GraphicsEnvironment.isHeadless()) {
+			this.setDragEnabled(true);  
+	        this.setDropMode(DropMode.ON_OR_INSERT);  
+	        this.setTransferHandler(new TreeTransferHandler());  
+	        this.getSelectionModel().setSelectionMode(  
+	                TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);  
+	        expandTree();
+		}
+
+		//
+	}
+
+	public IterationStrategyEditor(IterationStrategy theStrategy) {
+		this();
+		setIterationStrategy(theStrategy);
+	}
+
+    /**
+     * 
+     * This code is freely adapted from code derived 
+     *
+     */
+    class TreeTransferHandler extends TransferHandler {  
+        DataFlavor nodesFlavor;
+        DataFlavor[] flavors = new DataFlavor[1];  
+       
+        public TreeTransferHandler() {
+        	getNodesFlavor();
+          }
+        
+        private DataFlavor getNodesFlavor() {
+        	if (nodesFlavor == null) {
+                try {  
+                     nodesFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType +
+                             ";class=" + AbstractIterationStrategyNode.class.getName(),
+                             "AbstractIterationStrategyNode",
+                             this.getClass().getClassLoader());  
+                    flavors[0] = nodesFlavor;  
+                } catch(Exception e) {  
+                    logger.error("Problem creating nodesFlavor:" + e);
+                }         		
+        	}
+        	return nodesFlavor;
+        }
+       
+        public boolean canImport(TransferHandler.TransferSupport support) {  
+            if(!support.isDrop()) {
+            	logger.error("isDrop not supported");
+                return false;  
+            }  
+
+            if(!support.isDataFlavorSupported(getNodesFlavor())) {
+            	logger.info("Not correct flavor");
+                return false;  
+            }  
+            // Do not allow a drop on the drag source selections.  
+            JTree.DropLocation dl =  
+                    (JTree.DropLocation)support.getDropLocation();  
+            TreePath dest = dl.getPath();  
+            AbstractIterationStrategyNode destination =  
+                (AbstractIterationStrategyNode)dest.getLastPathComponent();
+            Transferable t = support.getTransferable();
+            if (destination instanceof TerminalNode) {
+            	return false;
+            }
+            try {
+				AbstractIterationStrategyNode node = (AbstractIterationStrategyNode) t.getTransferData(getNodesFlavor());
+				if (node.isNodeDescendant(destination)) {
+					return false;
+				}
+			} catch (UnsupportedFlavorException e) {
+				return false;
+			} catch (IOException e) {
+				return false;
+			}  
+//			JTree tree = (JTree) support.getComponent();
+//			int dropRow = tree.getRowForPath(dl.getPath());
+//			int selRow = tree.getLeadSelectionRow();
+//			if (selRow == dropRow) {
+//				logger.info("Dragging to source");
+//				return false;
+//
+//			}
+			support.setShowDropLocation(true);  
+            return true;  
+        }  
+       
+         protected Transferable createTransferable(JComponent c) {  
+            JTree tree = (JTree)c;  
+            TreePath[] paths = tree.getSelectionPaths();  
+            if(paths != null) {  
+                AbstractIterationStrategyNode node =  
+                    (AbstractIterationStrategyNode)paths[0].getLastPathComponent();  
+                 return new NodeTransferable(node);  
+            }  
+            return null;  
+        }  
+              
+        protected void exportDone(JComponent source, Transferable data, int action) {  
+        }  
+       
+        public int getSourceActions(JComponent c) {  
+            return MOVE;  
+        }  
+       
+        public boolean importData(TransferHandler.TransferSupport support) { 
+            if(!canImport(support)) {
+            	logger.info("Cannot import");
+                return false;  
+            }  
+            // Extract transfer data.  
+            AbstractIterationStrategyNode node = null;  
+            try {  
+                Transferable t = support.getTransferable();  
+                node = (AbstractIterationStrategyNode) t.getTransferData(getNodesFlavor());  
+            } catch(UnsupportedFlavorException ufe) {  
+                logger.error("UnsupportedFlavor", ufe);  
+            } catch (Exception e) {
+            	logger.error("Problem getting transfer data", e);
+            }
+
+           // Get drop location info.  
+            JTree.DropLocation dl =  
+                    (JTree.DropLocation)support.getDropLocation();  
+            int childIndex = dl.getChildIndex();  
+            TreePath dest = dl.getPath();  
+            AbstractIterationStrategyNode parent =  
+                (AbstractIterationStrategyNode)dest.getLastPathComponent();
+            int index = childIndex;
+            logger.info ("parent is a " + parent.getClass().getName());
+            if (parent instanceof NamedInputPortNode) {
+            	AbstractIterationStrategyNode sibling = parent;
+            	parent = (AbstractIterationStrategyNode) sibling.getParent();
+            	index = parent.getIndex(sibling);
+            } else if (index == -1) {
+            	index = parent.getChildCount();
+            }
+            if (parent instanceof TerminalNode) {
+            	if (parent.getChildCount() > 0) {
+            		parent = (AbstractIterationStrategyNode) parent.getChildAt(0);
+            		index = parent.getChildCount();
+            	}
+            	
+            }
+            logger.info("parent is a " + parent.getClass().getName());
+
+			try {
+				// The parent insert removes from the oldParent
+				parent.insert(node, index++);
+				DefaultTreeModel model = IterationStrategyEditor.this
+						.getModel();
+				refreshModel();
+			} catch (IllegalStateException e) {
+				logger.error(e);
+			} catch (IllegalArgumentException e) {
+				logger.error(e);
+			}
+          return true;  
+        }  
+       
+        public String toString() {  
+            return getClass().getName();  
+        }  
+       
+        public class NodeTransferable implements Transferable {  
+            AbstractIterationStrategyNode node;  
+       
+            public NodeTransferable(AbstractIterationStrategyNode node) {  
+                this.node = node;  
+             }  
+       
+            public AbstractIterationStrategyNode getTransferData(DataFlavor flavor)  
+                                     throws UnsupportedFlavorException {  
+                if(!isDataFlavorSupported(flavor))  
+                    throw new UnsupportedFlavorException(flavor);  
+                return node;  
+            }  
+       
+            public DataFlavor[] getTransferDataFlavors() {  
+                return flavors;  
+            }  
+       
+            public boolean isDataFlavorSupported(DataFlavor flavor) {
+            	 return getNodesFlavor().equals(flavor);  
+            }  
+        }  
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditorControl.java
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditorControl.java b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditorControl.java
new file mode 100644
index 0000000..c745283
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditorControl.java
@@ -0,0 +1,439 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+/**
+ * This file is a component of the Taverna project,
+ * and is licensed under the GNU LGPL.
+ * Copyright Tom Oinn, EMBL-EBI
+ */
+package net.sf.taverna.t2.workbench.iterationstrategy.editor;
+
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JToolBar;
+import javax.swing.SwingConstants;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.iterationstrategy.IterationStrategyIcons;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.CrossProduct;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.DotProduct;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategy;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategyNode;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.TerminalNode;
+
+import org.apache.log4j.Logger;
+
+/**
+ * A control panel for the iteration tree editor allowing the user to manipulate
+ * the tree, removing and adding nodes into the tree based on the context.
+ * 
+ * @author Tom Oinn
+ * @author Stian Soiland-Reyes
+ * 
+ */
+@SuppressWarnings("serial")
+public class IterationStrategyEditorControl extends JPanel {
+
+	protected static Set<IterationStrategyNode> descendentsOfNode(
+			IterationStrategyNode node) {
+		Set<IterationStrategyNode> descendants = new HashSet<IterationStrategyNode>();
+		Set<IterationStrategyNode> nodesToVisit = new HashSet<IterationStrategyNode>();
+		Set<IterationStrategyNode> visitedNodes = new HashSet<IterationStrategyNode>();
+
+		// Note: Not added to descendants
+		nodesToVisit.add(node);
+		while (!nodesToVisit.isEmpty()) {
+			// pick the first one
+			IterationStrategyNode visiting = nodesToVisit.iterator().next();
+			visitedNodes.add(visiting);
+			nodesToVisit.remove(visiting);
+
+			// Find new and interesting children
+			List<IterationStrategyNode> children = visiting.getChildren();
+			Set<IterationStrategyNode> newNodes = new HashSet<IterationStrategyNode>(
+					children);
+			newNodes.removeAll(visitedNodes);
+
+			descendants.addAll(newNodes);
+			nodesToVisit.addAll(newNodes);
+		}
+		return descendants;
+	}
+
+	private static Logger logger = Logger
+			.getLogger(IterationStrategyEditorControl.class);
+
+	private IterationStrategyNode selectedNode = null;
+
+	private IterationStrategyTree tree;
+
+	protected AddCrossAction addCross = new AddCrossAction();
+	protected AddDotAction addDot = new AddDotAction();
+	protected ChangeAction change = new ChangeAction();
+	protected NormalizeAction normalize = new NormalizeAction();
+	protected RemoveAction remove = new RemoveAction();
+	protected MoveUpAction moveUp = new MoveUpAction();
+
+	//private static final int ICON_SIZE = 15;
+
+	protected ImageIcon arrowUpIcon = WorkbenchIcons.upArrowIcon;
+	protected ImageIcon arrowDownIcon = WorkbenchIcons.downArrowIcon;
+	//protected ImageIcon arrowLeft = WorkbenchIcons.leftArrowIcon;
+	//protected ImageIcon arrowRight = WorkbenchIcons.rightArrowIcon;
+	protected ImageIcon normalizeIcon = WorkbenchIcons.normalizeIcon;
+
+	private final IterationStrategy strategy;
+
+	/**
+	 * Create a new panel from the supplied iteration strategy
+	 */
+	public IterationStrategyEditorControl(IterationStrategy strategy) {
+
+		this.strategy = strategy;
+		setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
+
+		// Create the components
+		tree = new IterationStrategyEditor(strategy);
+
+		JButton addCrossButton = new JButton(addCross);
+		addCrossButton.setHorizontalAlignment(SwingConstants.LEFT);
+		JButton addDotButton = new JButton(addDot);
+		addDotButton.setHorizontalAlignment(SwingConstants.LEFT);
+		JButton normalizeButton = new JButton(normalize);
+		normalizeButton.setHorizontalAlignment(SwingConstants.LEFT);
+		normalizeButton.setIcon(normalizeIcon);
+		JButton removeButton = new JButton(remove);
+		removeButton.setHorizontalAlignment(SwingConstants.LEFT);
+		JButton changeButton = new JButton(change);
+		changeButton.setHorizontalAlignment(SwingConstants.LEFT);
+
+		JButton moveUpButton = new JButton(moveUp);
+		moveUpButton.setIcon(arrowUpIcon);
+		moveUpButton.setHorizontalAlignment(SwingConstants.LEFT);
+
+		// Set the default enabled state to off on all buttons other than the
+		// normalizeButton
+		// one.
+		disableButtons();
+
+		// Create a layout with the tree on the right and the buttons in a grid
+		// layout on the left
+		JToolBar toolbar = new JToolBar();
+		toolbar.setFloatable(false);
+		toolbar.setRollover(true);
+		// toolbar.setLayout(new GridLayout(2,2));
+		toolbar.add(normalizeButton);
+		toolbar.add(addCrossButton);
+		toolbar.add(addDotButton);
+		toolbar.add(removeButton);
+		toolbar.add(changeButton);
+		toolbar.add(moveUpButton);
+
+		toolbar.setAlignmentX(LEFT_ALIGNMENT);
+
+		// Listen to tree selection events and enable buttons appropriately
+		tree.addTreeSelectionListener(new ButtonEnabler());
+
+		// Add components to the control panel
+		add(toolbar);
+		JScrollPane treePane = new JScrollPane(tree);
+		//treePane.setPreferredSize(new Dimension(0, 0));
+		add(treePane);
+	}
+
+	public void setIterationStrategy(IterationStrategy iterationStrategy) {
+		tree.setIterationStrategy(iterationStrategy);
+		disableButtons();
+		selectNode(null);
+	}
+
+	private void disableButtons() {
+		remove.setEnabled(false);
+		addCross.setEnabled(false);
+		addDot.setEnabled(false);
+		change.setEnabled(false);
+	}
+
+	private IterationStrategyNode findRoot() {
+		IterationStrategyNode root = (IterationStrategyNode) tree.getModel()
+				.getRoot();
+		if (root.getChildCount() > 0) {
+			return root.getChildAt(0);
+		}
+		return root;
+	}
+
+	protected void selectNode(TreeNode newNode) {
+		DefaultTreeModel model = tree.getModel();
+		if (newNode == null) {
+			newNode = (TreeNode) model.getRoot();
+		}
+		TreeNode[] pathToRoot = model.getPathToRoot(newNode);
+		tree.setSelectionPath(new TreePath(pathToRoot));
+	}
+
+	/**
+	 * Add a cross product node as a child of the selected node
+	 */
+	protected class AddCrossAction extends AbstractAction {
+
+		public AddCrossAction() {
+			super("Add Cross", IterationStrategyIcons.joinIteratorIcon);
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			CrossProduct newNode = new CrossProduct();
+			newNode.setParent(selectedNode);
+			tree.refreshModel();
+		}
+	}
+
+	/**
+	 * Add a dot product node as a child of the selected node
+	 * 
+	 * @author Stian Soiland-Reyes
+	 * 
+	 */
+	protected class AddDotAction extends AbstractAction {
+
+		public AddDotAction() {
+			super("Add Dot", IterationStrategyIcons.lockStepIteratorIcon);
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			DotProduct newNode = new DotProduct();
+			newNode.setParent(selectedNode);
+			tree.refreshModel();
+		}
+	}
+
+	protected class ButtonEnabler implements TreeSelectionListener {
+		public void valueChanged(TreeSelectionEvent e) {
+			TreePath selectedPath = e.getPath();
+			IterationStrategyNode selectedObject = (IterationStrategyNode) selectedPath
+					.getLastPathComponent();
+			selectedNode = selectedObject;
+			if (selectedObject instanceof CrossProduct
+					|| selectedObject instanceof DotProduct) {
+				if ((selectedObject.getParent() == null) || (selectedObject.getParent() instanceof TerminalNode)) {
+					remove.setEnabled(false);
+				} else {
+					remove.setEnabled(true);
+				}
+				if (selectedObject instanceof CrossProduct) {
+					change.putValue(Action.NAME, "Change to Dot Product");
+					change.putValue(Action.SMALL_ICON,
+							IterationStrategyIcons.lockStepIteratorIcon);
+				} else {
+					change.putValue(Action.NAME, "Change to Cross Product");
+					change.putValue(Action.SMALL_ICON,
+							IterationStrategyIcons.joinIteratorIcon);
+				}
+				addCross.setEnabled(true);
+				addDot.setEnabled(true);
+				change.setEnabled(true);
+			} else {
+				// Top- or leaf node
+				remove.setEnabled(false);
+				addCross.setEnabled(false);
+				addDot.setEnabled(false);
+				change.setEnabled(false);
+			}
+		}
+	}
+
+	/**
+	 * Add a cross product node as a child of the selected node
+	 */
+	protected class ChangeAction extends AbstractAction {
+
+		public ChangeAction() {
+			super("Switch to...", IterationStrategyIcons.joinIteratorIcon);
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			IterationStrategyNode newNode;
+			if (selectedNode instanceof CrossProduct) {
+				newNode = new DotProduct();
+			} else {
+				newNode = new CrossProduct();
+			}
+
+			List<IterationStrategyNode> children = new ArrayList<IterationStrategyNode>(
+					selectedNode.getChildren());
+			for (IterationStrategyNode child : children) {
+				child.setParent(newNode);
+			}
+
+			DefaultTreeModel model = tree.getModel();
+			if (selectedNode.getParent() == null) {
+				model.setRoot(newNode);
+				tree.refreshModel();
+				newNode.setParent(null);
+			} else {
+				IterationStrategyNode parent = selectedNode.getParent();
+				int index = parent.getIndex(selectedNode);
+				selectedNode.setParent(null);
+				parent.insert(newNode, index);
+				tree.refreshModel();
+			}
+
+			selectNode(newNode);
+		}
+
+	}
+
+	/**
+	 * Normalize the tree when the button is pressed
+	 * 
+	 */
+	protected class NormalizeAction extends AbstractAction {
+		public NormalizeAction() {
+			super("Normalize", normalizeIcon);
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			strategy.normalize();
+			// Expand all the nodes in the tree
+			//DefaultTreeModel model = tree.getModel();
+			tree.refreshModel();
+		}
+	}
+
+	/**
+	 * Remove the selected node, moving any descendant leaf nodes to the parent
+	 * to prevent them getting lost
+	 */
+	protected class RemoveAction extends AbstractAction {
+		public RemoveAction() {
+			super("Remove node", WorkbenchIcons.deleteIcon);
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			IterationStrategyNode nodeToBeRemoved = selectedNode;
+
+			//DefaultTreeModel model = tree.getModel();
+
+			// Now removeButton the candidate nodes from their parents and
+			// put them back into the root node
+			IterationStrategyNode root = findRoot();
+			if (root == selectedNode) {
+				return;
+			}
+			IterationStrategyNode oldParent = nodeToBeRemoved.getParent();
+
+			for (IterationStrategyNode nodeToMove : descendentsOfNode(nodeToBeRemoved)) {
+				nodeToMove.setParent(oldParent);
+			}
+			nodeToBeRemoved.setParent(null);
+			tree.refreshModel();
+			// Disable the various buttons, as the current selection
+			// is now invalid.
+			remove.setEnabled(false);
+			addCross.setEnabled(false);
+			addDot.setEnabled(false);
+			change.setEnabled(false);
+			selectNode(oldParent);
+		}
+	}
+
+	protected class MoveUpAction extends AbstractAction {
+
+		public MoveUpAction() {
+			super("Move up", arrowUpIcon);
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			//DefaultTreeModel model = tree.getModel();
+
+			IterationStrategyNode aboveNode = aboveSelectedNode();
+			if ((aboveNode == null) || ((aboveNode instanceof TerminalNode) && (aboveNode.getChildCount() > 0))) {
+				logger.warn("Can't move above top");
+				return;
+			}
+			IterationStrategyNode selectedParent = selectedNode.getParent();
+			IterationStrategyNode aboveParent = aboveNode.getParent();
+			if (selectedParent != null && selectedParent.equals(aboveParent)) {
+				// Siblings
+				int aboveChildIndex = selectedParent.getIndex(aboveNode);
+				selectedParent.insert(selectedNode, aboveChildIndex);
+				tree.refreshModel();
+				selectNode(selectedNode);
+			} else if (aboveNode.equals(selectedParent)) {
+				if (aboveParent instanceof TerminalNode
+						&& selectedNode.getAllowsChildren()) {
+					aboveNode.setParent(selectedNode);
+					selectedNode.setParent(aboveParent);
+					tree.refreshModel();
+					selectNode(selectedNode);
+				} else if (!(aboveParent instanceof TerminalNode)){
+					int aboveChildIndex = aboveParent.getIndex(aboveNode);
+					aboveParent.insert(selectedNode, aboveChildIndex);
+					tree.refreshModel();
+					selectNode(selectedNode);
+				}
+			} else {
+
+			}
+
+		}
+
+	}
+
+	protected IterationStrategyNode belowSelectedNode() {
+		return offsetFromSelectedNode(1);
+	}
+
+	protected IterationStrategyNode offsetFromSelectedNode(int offset) {
+		int currentRow = tree.getRowForPath(tree.getSelectionPath());
+		int offsetRow = currentRow + offset;
+		TreePath offsetPath = tree.getPathForRow(offsetRow);
+		if (offsetPath == null) {
+			return null;
+		}
+		IterationStrategyNode offsetNode = (IterationStrategyNode) offsetPath
+				.getLastPathComponent();
+		if (offsetNode == tree.getModel().getRoot()) {
+			return null;
+		}
+		return offsetNode;
+	}
+
+	protected IterationStrategyNode aboveSelectedNode() {
+		return offsetFromSelectedNode(-1);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyTree.java
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyTree.java b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyTree.java
new file mode 100644
index 0000000..c4665c6
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyTree.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * 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 java.util.Enumeration;
+
+import javax.swing.ImageIcon;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+
+import net.sf.taverna.t2.workbench.iterationstrategy.IterationStrategyIcons;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategy;
+
+@SuppressWarnings("serial")
+public class IterationStrategyTree extends JTree implements UIComponentSPI {
+
+	private IterationStrategy strategy = null;
+
+	public IterationStrategyTree() {
+		super();
+		setCellRenderer(new IterationStrategyCellRenderer());
+	}
+
+	public ImageIcon getIcon() {
+		return IterationStrategyIcons.leafnodeicon;
+	}
+
+	public void onDisplay() {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void onDispose() {
+		this.strategy = null;
+		setModel(null);
+	}
+
+	public synchronized void setIterationStrategy(
+			IterationStrategy theStrategy) {
+		if (theStrategy != this.strategy) {
+			this.strategy = theStrategy;
+			TreeNode terminal = theStrategy.getTerminalNode();
+			setModel(new DefaultTreeModel(terminal));
+			this.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+			expandTree();
+			revalidate();
+		}
+	}
+	
+	protected synchronized void refreshModel() {
+		this.getModel().nodeStructureChanged(strategy.getTerminalNode());
+		expandTree();
+	}
+
+	@Override
+	public DefaultTreeModel getModel() {
+		return (DefaultTreeModel) super.getModel();
+	}
+
+	@Override
+	public void setModel(TreeModel newModel) {
+		if (newModel != null && !(newModel instanceof DefaultTreeModel)) {
+			throw new IllegalArgumentException(
+					"Model must be a DefaultTreeModel");
+		}
+		super.setModel(newModel);
+	}
+
+	protected void expandTree() {  
+		DefaultMutableTreeNode root =  
+	        (DefaultMutableTreeNode)this.getModel().getRoot();  
+	    Enumeration e = root.breadthFirstEnumeration();  
+	    while(e.hasMoreElements()) {  
+	        DefaultMutableTreeNode node =  
+	            (DefaultMutableTreeNode)e.nextElement();  
+	        if(node.isLeaf()) continue;  
+	        int row = this.getRowForPath(new TreePath(node.getPath()));  
+	        this.expandRow(row);  
+	    }  
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/menu/IterationStrategyConfigureMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/menu/IterationStrategyConfigureMenuAction.java b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/menu/IterationStrategyConfigureMenuAction.java
new file mode 100644
index 0000000..f8c7f73
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/menu/IterationStrategyConfigureMenuAction.java
@@ -0,0 +1,65 @@
+/**********************************************************************
+ * 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.iterationstrategy.menu;
+
+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.helper.HelpEnabledDialog;
+import net.sf.taverna.t2.workbench.iterationstrategy.contextview.IterationStrategyConfigurationDialog;
+import net.sf.taverna.t2.workbench.iterationstrategy.contextview.IterationStrategyContextualView;
+import net.sf.taverna.t2.workflowmodel.Processor;
+
+public class IterationStrategyConfigureMenuAction extends AbstractContextualMenuAction {
+	
+	
+	
+	public static final URI configureRunningSection = URI
+	.create("http://taverna.sf.net/2009/contextMenu/configureRunning");
+	
+	private static final URI ITERATION_STRATEGY_CONFIGURE_URI = URI
+	.create("http://taverna.sf.net/2008/t2workbench/iterationStrategyConfigure");
+
+	public IterationStrategyConfigureMenuAction() {
+		super(configureRunningSection, 40, ITERATION_STRATEGY_CONFIGURE_URI);
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		return new AbstractAction("List handling...") {
+			public void actionPerformed(ActionEvent e) {
+				Processor p = (Processor) getContextualSelection().getSelection();
+				final HelpEnabledDialog dialog = new IterationStrategyConfigurationDialog(null, p, IterationStrategyContextualView.copyIterationStrategyStack(p.getIterationStrategy()));		
+				dialog.setVisible(true);
+			}
+		};
+	}
+	
+	public boolean isEnabled() {
+		return super.isEnabled() && (getContextualSelection().getSelection() instanceof Processor);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..6f25f4e
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.iterationstrategy.menu.IterationStrategyConfigureMenuAction
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
----------------------------------------------------------------------
diff --git a/taverna-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
new file mode 100644
index 0000000..a6a27b0
--- /dev/null
+++ b/taverna-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.iterationstrategy.contextview.IterationStrategyContextualViewFactory


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

Posted by st...@apache.org.
taverna-workbench-* -> taverna-*


Project: http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/commit/52fd79dd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/tree/52fd79dd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/diff/52fd79dd

Branch: refs/heads/master
Commit: 52fd79dd4fa69ebaf7c8fe4301415300a51e259b
Parents: a227983
Author: Stian Soiland-Reyes <st...@apache.org>
Authored: Fri Mar 20 14:21:43 2015 +0000
Committer: Stian Soiland-Reyes <st...@apache.org>
Committed: Fri Mar 20 14:22:04 2015 +0000

----------------------------------------------------------------------
 pom.xml                                         |  104 +-
 taverna-activity-icons-api/pom.xml              |   37 +
 .../activityicons/ActivityIconManager.java      |   41 +
 .../activityicons/ActivityIconSPI.java          |   57 +
 .../activityicons/DefaultActivityIcon.java      |   54 +
 .../impl/ActivityIconManagerImpl.java           |   85 +
 ...a.t2.workbench.activityicons.ActivityIconSPI |    1 +
 .../spring/activity-icons-api-context-osgi.xml  |   15 +
 .../spring/activity-icons-api-context.xml       |   13 +
 .../main/resources/default-activity-icon.png    |  Bin 0 -> 577 bytes
 taverna-activity-palette-api/pom.xml            |   63 +
 .../AbstractConfigurableServiceProvider.java    |   53 +
 .../AbstractTemplateService.java                |   85 +
 .../ConfigurableServiceProvider.java            |   10 +
 .../CustomizedConfigurePanelProvider.java       |   36 +
 .../servicedescriptions/IdentifiedObject.java   |   30 +
 .../servicedescriptions/ServiceDescription.java |   80 +
 .../ServiceDescriptionProvider.java             |   61 +
 .../ServiceDescriptionRegistry.java             |   50 +
 .../ServiceDescriptionsConfiguration.java       |   36 +
 .../events/AbstractProviderEvent.java           |   16 +
 .../events/AbstractProviderNotification.java    |   18 +
 .../events/AddedProviderEvent.java              |   10 +
 .../PartialServiceDescriptionsNotification.java |   22 +
 .../events/ProviderErrorNotification.java       |   19 +
 .../events/ProviderStatusNotification.java      |   12 +
 .../events/ProviderUpdatingNotification.java    |   11 +
 .../events/ProviderWarningNotification.java     |   12 +
 .../events/RemovedProviderEvent.java            |   10 +
 .../events/ServiceDescriptionProvidedEvent.java |   20 +
 .../events/ServiceDescriptionRegistryEvent.java |    4 +
 taverna-activity-palette-impl/pom.xml           |   98 +
 .../impl/ServiceDescriptionConstants.java       |   10 +
 .../impl/ServiceDescriptionDeserializer.java    |  167 +
 .../impl/ServiceDescriptionRegistryImpl.java    |  652 ++++
 .../impl/ServiceDescriptionSerializer.java      |  102 +
 .../impl/ServiceDescriptionXMLConstants.java    |   15 +
 .../ServiceDescriptionsConfigurationImpl.java   |   92 +
 .../ActivityPaletteConfiguration.java           |   81 +
 .../ActivityPaletteConfigurationPanel.java      |  284 ++
 .../ActivityPaletteConfigurationUIFactory.java  |   52 +
 ...rkbench.configuration.ConfigurationUIFactory |    1 +
 .../activity-palette-impl-context-osgi.xml      |   17 +
 .../spring/activity-palette-impl-context.xml    |   23 +
 .../ActivityPaletteConfigurationTest.java       |   97 +
 ...averna.t2.partition.PartitionAlgorithmSetSPI |    1 +
 ...sf.taverna.t2.partition.PropertyExtractorSPI |    3 +
 .../net.sf.taverna.t2.partition.QueryFactory    |    2 +
 taverna-activity-palette-ui/pom.xml             |   88 +
 .../servicepanel/PathElementFilterTreeNode.java |   34 +
 .../ui/servicepanel/RootFilterTreeNode.java     |   34 +
 .../ui/servicepanel/ServiceFilter.java          |  158 +
 .../ui/servicepanel/ServiceFilterTreeNode.java  |   40 +
 .../workbench/ui/servicepanel/ServicePanel.java |  411 ++
 .../ServicePanelComponentFactory.java           |   82 +
 .../servicepanel/ServiceTreeCellRenderer.java   |   77 +
 .../servicepanel/ServiceTreeClickListener.java  |  252 ++
 .../ui/servicepanel/ServiceTreePanel.java       |  176 +
 .../actions/AddServiceProviderAction.java       |  256 ++
 .../ExportServiceDescriptionsAction.java        |  155 +
 ...ImportServiceDescriptionsFromFileAction.java |  158 +
 .../ImportServiceDescriptionsFromURLAction.java |  129 +
 .../actions/RefreshProviderRegistryAction.java  |   52 +
 .../actions/RemoveDefaultServicesAction.java    |   51 +
 .../actions/RemoveUserServicesAction.java       |   59 +
 .../actions/RestoreDefaultServicesAction.java   |   50 +
 .../config/ServiceDescriptionConfigPanel.java   |  181 +
 .../ServiceDescriptionConfigUIFactory.java      |   57 +
 .../menu/AddServiceProviderMenu.java            |  113 +
 .../workbench/ui/servicepanel/tree/Filter.java  |   33 +
 .../tree/FilterTreeCellRenderer.java            |   59 +
 .../ui/servicepanel/tree/FilterTreeModel.java   |   92 +
 .../ui/servicepanel/tree/FilterTreeNode.java    |  142 +
 .../tree/FilterTreeSelectionModel.java          |   46 +
 .../ui/servicepanel/tree/MyFilter.java          |   89 +
 .../ui/servicepanel/tree/TreePanel.java         |  371 ++
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 +
 .../spring/activity-palette-ui-context-osgi.xml |   20 +
 .../spring/activity-palette-ui-context.xml      |   22 +
 taverna-activity-tools/pom.xml                  |   46 +
 .../AbstractConfigureActivityMenuAction.java    |   64 +
 taverna-configuration-api/pom.xml               |   37 +
 .../configuration/colour/ColourManager.java     |   41 +
 .../configuration/mimetype/MimeTypeManager.java |   42 +
 .../workbench/WorkbenchConfiguration.java       |   44 +
 .../workbench/ui/T2ConfigurationFrame.java      |   30 +
 taverna-configuration-impl/pom.xml              |   76 +
 .../WorkbenchConfigurationImpl.java             |  210 ++
 .../WorkbenchConfigurationPanel.java            |  266 ++
 .../WorkbenchConfigurationUIFactory.java        |   52 +
 .../configuration/colour/ColourManagerImpl.java |  178 +
 .../mimetype/MimeTypeManagerImpl.java           |   82 +
 .../ui/T2ConfigurationFrameImpl.java            |  205 +
 .../ui/WorkbenchConfigurationMenu.java          |   61 +
 .../ui/WorkbenchPreferencesSection.java         |   36 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    2 +
 ...rkbench.configuration.ConfigurationUIFactory |    1 +
 .../spring/configuration-impl-context-osgi.xml  |   27 +
 .../spring/configuration-impl-context.xml       |   34 +
 .../configuration/ConfigurationManagerTest.java |  109 +
 .../configuration/colour/ColourManagerTest.java |   90 +
 taverna-contextual-views-api/pom.xml            |   99 +
 .../activity/ActivityConfigurationAction.java   |  167 +
 .../activity/ActivityContextualView.java        |   69 +
 .../HTMLBasedActivityContextualView.java        |   81 +
 .../contextualviews/AddLayerFactorySPI.java     |   43 +
 .../views/contextualviews/ContextualView.java   |  109 +
 .../activity/ActivityConfigurationDialog.java   |  474 +++
 .../activity/ActivityConfigurationPanel.java    |  214 ++
 .../activity/ActivityPortConfiguration.java     |   84 +
 .../activity/ContextualViewFactory.java         |   63 +
 .../activity/ContextualViewFactoryRegistry.java |   43 +
 .../activity/DependencyConfigurationPanel.java  |  293 ++
 .../activity/ListConfigurationComponent.java    |  119 +
 .../contextualviews/activity/ListLayout.java    |   92 +
 .../MultiPageActivityConfigurationPanel.java    |   65 +
 .../activity/ScriptConfigurationComponent.java  |  150 +
 .../activity/ValidatingTextField.java           |   53 +
 .../activity/ValidatingTextGroup.java           |  119 +
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    2 +
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    3 +
 .../contextual-views-api-context-osgi.xml       |    9 +
 .../spring/contextual-views-api-context.xml     |    6 +
 taverna-contextual-views-impl/pom.xml           |   54 +
 .../impl/ContextualViewFactoryRegistryImpl.java |   75 +
 .../annotated/AnnotatedContextualView.java      |  263 ++
 .../AnnotatedContextualViewFactory.java         |   43 +
 .../condition/ConditionContextualView.java      |   74 +
 .../ConditionContextualViewFactory.java         |   51 +
 .../dataflow/DataflowContextualView.java        |  108 +
 .../dataflow/DataflowContextualViewFactory.java |   41 +
 .../DataflowInputPortContextualView.java        |   96 +
 .../DataflowInputPortContextualViewFactory.java |   54 +
 .../DataflowOutputPortContextualView.java       |  106 +
 ...DataflowOutputPortContextualViewFactory.java |   55 +
 .../datalink/DatalinkContextualView.java        |  106 +
 .../datalink/DatalinkContextualViewFactory.java |   55 +
 .../impl/ContextualViewComponent.java           |  389 ++
 .../impl/ContextualViewComponentFactory.java    |   64 +
 .../inputport/InputPortContextualView.java      |   76 +
 .../InputPortContextualViewFactory.java         |   48 +
 .../merge/MergeConfigurationAction.java         |   79 +
 .../merge/MergeConfigurationView.java           |  233 ++
 .../merge/MergeContextualView.java              |  150 +
 .../merge/MergeContextualViewFactory.java       |   66 +
 .../outputport/OutputPortContextualView.java    |   76 +
 .../OutputPortContextualViewFactory.java        |   48 +
 ...ntextualviews.activity.ContextualViewFactory |    9 +
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 +
 .../contextual-views-impl-context-osgi.xml      |   31 +
 .../spring/contextual-views-impl-context.xml    |   43 +
 .../annotatedcontextualview.properties          |    4 +
 taverna-contextual-views/pom.xml                |   57 +
 .../ProcessorActivitiesContextualView.java      |  154 +
 ...rocessorActivitiesContextualViewFactory.java |   75 +
 ...rPredictedBehaviorContextualViewFactory.java |  177 +
 ...ntextualviews.activity.ContextualViewFactory |    4 +
 .../spring/contextual-views-context-osgi.xml    |   15 +
 .../spring/contextual-views-context.xml         |   12 +
 taverna-credential-manager-ui/pom.xml           |   69 +
 .../ui/credentialmanager/CMStrings.java         |    7 +
 .../ChangeMasterPasswordDialog.java             |  234 ++
 .../ConfirmTrustedCertificateDialog.java        |  520 +++
 .../ConfirmTrustedCertificateUI.java            |   71 +
 .../credentialmanager/CredentialManagerUI.java  | 1512 ++++++++
 .../CredentialManagerUILauncher.java            |   96 +
 .../ui/credentialmanager/CryptoFileFilter.java  |   73 +
 .../GetMasterPasswordDialog.java                |  169 +
 .../ui/credentialmanager/GetPasswordDialog.java |  168 +
 .../credentialmanager/KeyPairsTableModel.java   |  213 ++
 .../NewEditPasswordEntryDialog.java             |  397 ++
 .../NewKeyPairEntryDialog.java                  |  304 ++
 .../credentialmanager/NewTrustCertsDialog.java  |  248 ++
 .../credentialmanager/PasswordsTableModel.java  |  227 ++
 .../SetMasterPasswordDialog.java                |  189 +
 .../ui/credentialmanager/TableCellRenderer.java |  113 +
 .../credentialmanager/TableHeaderRenderer.java  |  100 +
 .../TrustedCertsTableModel.java                 |  216 ++
 .../ViewCertDetailsDialog.java                  |  509 +++
 .../ViewUsernamePasswordEntryDialog.java        |  199 +
 .../WarnUserAboutJCEPolicyDialog.java           |  223 ++
 .../action/CredentialManagerAction.java         |   68 +
 .../menu/CredentialManagerMenu.java             |   72 +
 .../AskUserJavaTruststorePasswordProvider.java  |   46 +
 .../password/AskUserMasterPasswordProvider.java |   55 +
 ...kUserServiceUsernameAndPasswordProvider.java |   23 +
 .../AskUserTrustConfirmationProvider.java       |   35 +
 .../password/GetPasswordDialog.java             |  228 ++
 .../password/SimpleMasterPasswordProvider.java  |   54 +
 .../password/UIMasterPasswordProvider.java      |  126 +
 .../password/UIUsernamePasswordProvider.java    |   92 +
 .../startup/InitialiseSSLStartupHook.java       |   64 +
 .../SetCredManAuthenticatorStartupHook.java     |   24 +
 .../toolbar/CredentialManagerToolbarAction.java |   44 +
 .../CredentialManagerToolbarSection.java        |   38 +
 ...rity.credentialmanager.CredentialProviderSPI |    3 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    3 +
 .../net.sf.taverna.t2.workbench.StartupSPI      |    2 +
 .../credential-manager-ui-context-osgi.xml      |   37 +
 .../spring/credential-manager-ui-context.xml    |   44 +
 .../src/main/resources/images/cred_manager.png  |  Bin 0 -> 10131 bytes
 .../main/resources/images/cred_manager16x16.png |  Bin 0 -> 3622 bytes
 .../images/cred_manager_transparent.png         |  Bin 0 -> 8376 bytes
 .../resources/images/table/entry_heading.png    |  Bin 0 -> 205 bytes
 .../main/resources/images/table/key_entry.png   |  Bin 0 -> 260 bytes
 .../resources/images/table/keypair_entry.png    |  Bin 0 -> 306 bytes
 .../resources/images/table/trustcert_entry.png  |  Bin 0 -> 551 bytes
 taverna-data-management-config-ui/pom.xml       |   84 +
 .../DataManagementConfigurationPanel.java       |  304 ++
 .../DataManagementConfigurationUIFactory.java   |   64 +
 ...rkbench.configuration.ConfigurationUIFactory |    1 +
 .../data-management-config-ui-context-osgi.xml  |   14 +
 .../data-management-config-ui-context.xml       |   11 +
 taverna-design-ui/pom.xml                       |   61 +
 .../design/actions/AddConditionAction.java      |   82 +
 .../design/actions/AddDataflowInputAction.java  |   96 +
 .../design/actions/AddDataflowOutputAction.java |   90 +
 .../design/actions/DataflowEditAction.java      |   57 +
 .../actions/EditDataflowInputPortAction.java    |  115 +
 .../actions/EditDataflowOutputPortAction.java   |   95 +
 .../design/actions/RemoveConditionAction.java   |   69 +
 .../actions/RemoveDataflowInputPortAction.java  |   85 +
 .../actions/RemoveDataflowOutputPortAction.java |   85 +
 .../design/actions/RemoveDatalinkAction.java    |   68 +
 .../design/actions/RemoveProcessorAction.java   |  136 +
 .../design/actions/RenameProcessorAction.java   |   97 +
 .../design/ui/DataflowInputPortPanel.java       |  203 +
 .../design/ui/DataflowOutputPortPanel.java      |  105 +
 .../t2/workbench/design/ui/ProcessorPanel.java  |  101 +
 taverna-edits-api/pom.xml                       |   47 +
 .../t2/workbench/edits/CompoundEdit.java        |  118 +
 .../net/sf/taverna/t2/workbench/edits/Edit.java |   66 +
 .../t2/workbench/edits/EditException.java       |   42 +
 .../taverna/t2/workbench/edits/EditManager.java |  222 ++
 .../t2/workbench/edits/package-info.java        |   48 +
 .../taverna/t2/workflow/edits/AbstractEdit.java |  119 +
 .../t2/workflow/edits/AddActivityEdit.java      |   55 +
 .../edits/AddActivityInputPortMappingEdit.java  |   59 +
 .../edits/AddActivityOutputPortMappingEdit.java |   59 +
 .../taverna/t2/workflow/edits/AddChildEdit.java |   52 +
 .../t2/workflow/edits/AddDataLinkEdit.java      |   90 +
 .../edits/AddIterationStrategyEdit.java         |   49 +
 .../AddIterationStrategyInputPortEdit.java      |   50 +
 .../t2/workflow/edits/AddProcessorEdit.java     |   45 +
 .../edits/AddProcessorInputPortEdit.java        |   45 +
 .../edits/AddProcessorOutputPortEdit.java       |   46 +
 .../edits/AddWorkflowInputPortEdit.java         |  110 +
 .../edits/AddWorkflowOutputPortEdit.java        |  111 +
 .../t2/workflow/edits/ChangeDepthEdit.java      |  104 +
 .../workflow/edits/ChangeGranularDepthEdit.java |   49 +
 .../t2/workflow/edits/ChangeJsonEdit.java       |   50 +
 .../edits/ClearIterationStrategyStackEdit.java  |   50 +
 .../t2/workflow/edits/ConfigureEdit.java        |   55 +
 .../t2/workflow/edits/RemoveActivityEdit.java   |   55 +
 .../RemoveActivityInputPortMappingEdit.java     |   51 +
 .../RemoveActivityOutputPortMappingEdit.java    |   51 +
 .../t2/workflow/edits/RemoveChildEdit.java      |   48 +
 .../t2/workflow/edits/RemoveDataLinkEdit.java   |  111 +
 .../edits/RemoveProcessorInputPortEdit.java     |   31 +
 .../edits/RemoveProcessorOutputPortEdit.java    |   31 +
 .../edits/RemoveWorkflowInputPortEdit.java      |  107 +
 .../edits/RemoveWorkflowOutputPortEdit.java     |  105 +
 .../taverna/t2/workflow/edits/RenameEdit.java   |  136 +
 .../edits/ReorderMergePositionsEdit.java        |   57 +
 .../edits/SetIterationStrategyStackEdit.java    |   51 +
 .../UpdateDataflowInternalIdentifierEdit.java   |   48 +
 taverna-edits-impl/pom.xml                      |   65 +
 .../workbench/edits/impl/EditManagerImpl.java   |  285 ++
 .../edits/impl/menu/AbstractUndoAction.java     |  166 +
 .../edits/impl/menu/RedoMenuAction.java         |   86 +
 .../edits/impl/menu/UndoMenuAction.java         |   86 +
 .../edits/impl/menu/UndoMenuSection.java        |   42 +
 .../edits/impl/toolbar/EditToolbarSection.java  |   36 +
 .../edits/impl/toolbar/RedoToolbarAction.java   |   46 +
 .../edits/impl/toolbar/UndoToolbarAction.java   |   46 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    6 +
 ...et.sf.taverna.t2.workbench.edits.EditManager |    1 +
 .../META-INF/spring/edits-impl-context-osgi.xml |   20 +
 .../META-INF/spring/edits-impl-context.xml      |   33 +
 .../edits/impl/TestEditManagerImpl.java         |  258 ++
 taverna-file-api/pom.xml                        |   49 +
 .../AbstractDataflowPersistenceHandler.java     |   69 +
 .../taverna/t2/workbench/file/DataflowInfo.java |  108 +
 .../file/DataflowPersistenceHandler.java        |  152 +
 .../taverna/t2/workbench/file/FileManager.java  |  573 +++
 .../sf/taverna/t2/workbench/file/FileType.java  |   67 +
 .../file/events/AbstractDataflowEvent.java      |   45 +
 .../file/events/ClosedDataflowEvent.java        |   34 +
 .../file/events/ClosingDataflowEvent.java       |   45 +
 .../workbench/file/events/FileManagerEvent.java |   39 +
 .../file/events/OpenedDataflowEvent.java        |   34 +
 .../file/events/SavedDataflowEvent.java         |   34 +
 .../file/events/SetCurrentDataflowEvent.java    |   35 +
 .../file/exceptions/FileException.java          |   44 +
 .../file/exceptions/OpenException.java          |   40 +
 .../file/exceptions/OverwriteException.java     |   36 +
 .../file/exceptions/SaveException.java          |   40 +
 .../file/exceptions/UnsavedException.java       |   38 +
 taverna-file-impl/pom.xml                       |  120 +
 .../DataflowFromDataflowPersistenceHandler.java |   49 +
 .../DataflowPersistenceHandlerRegistry.java     |  238 ++
 .../workbench/file/impl/FileDataflowInfo.java   |   67 +
 .../t2/workbench/file/impl/FileManagerImpl.java |  601 +++
 .../workbench/file/impl/FileTypeFileFilter.java |   55 +
 .../workbench/file/impl/MultipleFileTypes.java  |   58 +
 .../file/impl/OpenDataflowInProgressDialog.java |   88 +
 .../workbench/file/impl/OpenDataflowInfo.java   |   93 +
 .../file/impl/OpenDataflowRunnable.java         |   71 +
 .../file/impl/OpenDataflowSwingWorker.java      |   67 +
 .../workbench/file/impl/T2DataflowOpener.java   |  144 +
 .../t2/workbench/file/impl/T2FileFilter.java    |   40 +
 .../t2/workbench/file/impl/T2FlowFileType.java  |   42 +
 .../file/impl/WorkflowBundleFileFilter.java     |   40 +
 .../file/impl/WorkflowBundleFileType.java       |   42 +
 .../file/impl/WorkflowBundleOpener.java         |  143 +
 .../file/impl/WorkflowBundleSaver.java          |  145 +
 .../impl/actions/CloseAllWorkflowsAction.java   |   85 +
 .../file/impl/actions/CloseWorkflowAction.java  |  107 +
 .../file/impl/actions/NewWorkflowAction.java    |   58 +
 .../impl/actions/OpenNestedWorkflowAction.java  |   76 +
 .../file/impl/actions/OpenWorkflowAction.java   |  395 ++
 .../impl/actions/OpenWorkflowFromURLAction.java |  139 +
 .../file/impl/actions/PasswordInput.java        |  221 ++
 .../impl/actions/SaveAllWorkflowsAction.java    |  104 +
 .../file/impl/actions/SaveWorkflowAction.java   |  175 +
 .../file/impl/actions/SaveWorkflowAsAction.java |  219 ++
 .../impl/hooks/CloseWorkflowsOnShutdown.java    |   56 +
 .../file/impl/menu/FileCloseAllMenuAction.java  |   51 +
 .../file/impl/menu/FileCloseMenuAction.java     |   50 +
 .../file/impl/menu/FileNewMenuAction.java       |   47 +
 .../impl/menu/FileOpenFromURLMenuAction.java    |   48 +
 .../file/impl/menu/FileOpenMenuAction.java      |   47 +
 .../file/impl/menu/FileOpenMenuSection.java     |   36 +
 .../impl/menu/FileOpenRecentMenuAction.java     |  418 ++
 .../file/impl/menu/FileSaveAllMenuAction.java   |   47 +
 .../file/impl/menu/FileSaveAsMenuAction.java    |   43 +
 .../file/impl/menu/FileSaveMenuAction.java      |   46 +
 .../file/impl/menu/FileSaveMenuSection.java     |   36 +
 .../workbench/file/impl/menu/WorkflowsMenu.java |  163 +
 .../file/impl/toolbar/CloseToolbarAction.java   |   55 +
 .../impl/toolbar/FileToolbarMenuSection.java    |   36 +
 .../file/impl/toolbar/NewToolbarAction.java     |   47 +
 .../file/impl/toolbar/OpenToolbarAction.java    |   47 +
 .../OpenWorkflowFromURLToolbarAction.java       |   47 +
 .../file/impl/toolbar/SaveToolbarAction.java    |   50 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |   20 +
 .../net.sf.taverna.t2.workbench.ShutdownSPI     |    1 +
 ...t2.workbench.file.DataflowPersistenceHandler |    2 +
 ...net.sf.taverna.t2.workbench.file.FileManager |    1 +
 .../META-INF/spring/file-impl-context-osgi.xml  |  100 +
 .../META-INF/spring/file-impl-context.xml       |  123 +
 .../t2/workbench/file/impl/FileManagerTest.java |  385 ++
 .../workbench/file/impl/dummy-workflow.t2flow   |  157 +
 taverna-graph-model/pom.xml                     |  142 +
 .../models/graph/DefaultGraphEventManager.java  |  271 ++
 .../t2/workbench/models/graph/DotWriter.java    |  253 ++
 .../t2/workbench/models/graph/Graph.java        |  165 +
 .../models/graph/GraphColorManager.java         |   75 +
 .../workbench/models/graph/GraphController.java | 1276 +++++++
 .../t2/workbench/models/graph/GraphEdge.java    |  137 +
 .../t2/workbench/models/graph/GraphElement.java |  430 +++
 .../models/graph/GraphEventManager.java         |   47 +
 .../t2/workbench/models/graph/GraphNode.java    |  153 +
 .../models/graph/GraphShapeElement.java         |  119 +
 .../workbench/models/graph/dot/GraphLayout.java |  326 ++
 .../t2/workbench/models/graph/svg/SVGGraph.java |  439 +++
 .../models/graph/svg/SVGGraphController.java    |  555 +++
 .../models/graph/svg/SVGGraphEdge.java          |  311 ++
 .../graph/svg/SVGGraphElementDelegate.java      |  178 +
 .../models/graph/svg/SVGGraphNode.java          |  611 +++
 .../models/graph/svg/SVGGraphSettings.java      |   28 +
 .../models/graph/svg/SVGMonitorShape.java       |   40 +
 .../t2/workbench/models/graph/svg/SVGShape.java |   29 +
 .../t2/workbench/models/graph/svg/SVGUtil.java  |  477 +++
 .../graph/svg/event/SVGEventListener.java       |   56 +
 .../svg/event/SVGMouseClickEventListener.java   |   45 +
 .../svg/event/SVGMouseDownEventListener.java    |   45 +
 .../svg/event/SVGMouseMovedEventListener.java   |   46 +
 .../svg/event/SVGMouseOutEventListener.java     |   46 +
 .../svg/event/SVGMouseOverEventListener.java    |   46 +
 .../svg/event/SVGMouseUpEventListener.java      |   46 +
 .../src/main/jjtree/NamedNode.java              |   65 +
 .../src/main/jjtree/dotparser.jjt               |  289 ++
 .../models/graph/GraphControllerTest.java       |   81 +
 .../workbench/models/graph/GraphEdgeTest.java   |  129 +
 .../models/graph/GraphElementTest.java          |  148 +
 .../workbench/models/graph/GraphNodeTest.java   |  179 +
 .../t2/workbench/models/graph/GraphTest.java    |  138 +
 .../src/test/resources/nested_iteration.xml     |  121 +
 taverna-graph-view/pom.xml                      |  102 +
 .../views/graph/AutoScrollInteractor.java       |  181 +
 .../views/graph/GraphViewComponent.java         |  548 +++
 .../views/graph/GraphViewComponentFactory.java  |  100 +
 .../views/graph/actions/AddWFInputAction.java   |   69 +
 .../views/graph/actions/AddWFOutputAction.java  |   69 +
 .../actions/DeleteGraphComponentAction.java     |  180 +
 .../RenameWFInputOutputProcessorAction.java     |  184 +
 .../graph/config/GraphViewConfiguration.java    |   80 +
 .../config/GraphViewConfigurationPanel.java     |  360 ++
 .../config/GraphViewConfigurationUIFactory.java |   55 +
 .../views/graph/menu/AddWFInputMenuAction.java  |   60 +
 .../views/graph/menu/AddWFOutputMenuAction.java |   60 +
 .../menu/DeleteGraphComponentMenuAction.java    |   61 +
 .../workbench/views/graph/menu/DiagramMenu.java |   44 +
 .../graph/menu/DiagramSaveMenuSection.java      |   39 +
 .../graph/menu/DiagramZoomMenuSection.java      |   40 +
 .../views/graph/menu/GraphCopyMenuSection.java  |   39 +
 .../graph/menu/GraphDeleteMenuSection.java      |   39 +
 .../graph/menu/GraphDetailsMenuSection.java     |   40 +
 .../views/graph/menu/GraphEditMenuSection.java  |   39 +
 .../views/graph/menu/GraphMenuSection.java      |   39 +
 .../workbench/views/graph/menu/InsertMenu.java  |   30 +
 .../RenameWFInputOutputProcessorMenuAction.java |   62 +
 .../views/graph/menu/ResetDiagramAction.java    |   64 +
 .../graph/menu/ResetDiagramMenuAction.java      |   50 +
 .../views/graph/menu/SaveGraphImageSubMenu.java |  315 ++
 .../views/graph/menu/ZoomInAction.java          |   72 +
 .../views/graph/menu/ZoomInMenuAction.java      |   50 +
 .../views/graph/menu/ZoomOutAction.java         |   65 +
 .../views/graph/menu/ZoomOutMenuAction.java     |   50 +
 .../graph/toolbar/AddWFInputToolbarAction.java  |   60 +
 .../graph/toolbar/AddWFOutputToolbarAction.java |   60 +
 .../DeleteGraphComponentToolbarAction.java      |   60 +
 .../toolbar/GraphDeleteToolbarSection.java      |   39 +
 .../graph/toolbar/GraphEditToolbarSection.java  |   39 +
 ...nameWFInputOutputProcessorToolbarAction.java |   63 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |   29 +
 ...rkbench.configuration.ConfigurationUIFactory |    1 +
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 +
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 +
 .../META-INF/spring/graph-view-context-osgi.xml |   46 +
 .../META-INF/spring/graph-view-context.xml      |  107 +
 .../src/test/resources/nested_iteration.t2flow  |  111 +
 .../src/test/resources/nested_iteration.xml     |  120 +
 taverna-helper-api/pom.xml                      |   73 +
 .../t2/workbench/helper/HelpCollator.java       |  307 ++
 .../t2/workbench/helper/HelpEnabledDialog.java  |  101 +
 .../sf/taverna/t2/workbench/helper/Helper.java  |  187 +
 .../helper/NonBlockedHelpEnabledDialog.java     |   40 +
 taverna-httpproxy-config/pom.xml                |   46 +
 .../config/HttpProxyConfigurationPanel.java     |  582 +++
 .../config/HttpProxyConfigurationUIFactory.java |   56 +
 ...rkbench.configuration.ConfigurationUIFactory |    1 +
 .../spring/httpproxy-config-context-osgi.xml    |   13 +
 .../spring/httpproxy-config-context.xml         |   10 +
 taverna-iteration-strategy-ui/pom.xml           |   79 +
 .../IterationStrategyIcons.java                 |   48 +
 .../IterationStrategyConfigurationDialog.java   |  148 +
 .../IterationStrategyContextualView.java        |  231 ++
 .../IterationStrategyContextualViewFactory.java |   54 +
 .../editor/IterationStrategyCellRenderer.java   |   74 +
 .../editor/IterationStrategyEditor.java         |  247 ++
 .../editor/IterationStrategyEditorControl.java  |  439 +++
 .../editor/IterationStrategyTree.java           |  106 +
 .../IterationStrategyConfigureMenuAction.java   |   65 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    1 +
 ...ntextualviews.activity.ContextualViewFactory |    1 +
 .../iteration-strategy-ui-context-osgi.xml      |   14 +
 .../spring/iteration-strategy-ui-context.xml    |   11 +
 .../icons/crossproducticon.png                  |  Bin 0 -> 831 bytes
 .../iterationstrategy/icons/dotproducticon.png  |  Bin 0 -> 663 bytes
 .../iterationstrategy/icons/leafnodeicon.png    |  Bin 0 -> 507 bytes
 .../editor/RunIterationStrategyEditor.java      |   56 +
 taverna-loop-ui/pom.xml                         |   85 +
 .../t2/workbench/loop/ActivityGenerator.java    |  195 +
 .../t2/workbench/loop/AddLoopFactory.java       |  125 +
 .../t2/workbench/loop/LoopAddMenuAction.java    |   73 +
 .../workbench/loop/LoopConfigurationPanel.java  |  588 +++
 .../t2/workbench/loop/LoopConfigureAction.java  |  262 ++
 .../workbench/loop/LoopConfigureMenuAction.java |   97 +
 .../t2/workbench/loop/LoopContextualView.java   |  172 +
 .../loop/LoopContextualViewFactory.java         |   53 +
 .../t2/workbench/loop/LoopRemoveMenuAction.java |   92 +
 .../workbench/loop/comparisons/Comparison.java  |   47 +
 .../t2/workbench/loop/comparisons/EqualTo.java  |   40 +
 .../loop/comparisons/IsGreaterThan.java         |   40 +
 .../workbench/loop/comparisons/IsLessThan.java  |   40 +
 .../t2/workbench/loop/comparisons/Matches.java  |   40 +
 .../workbench/loop/comparisons/NotEqualTo.java  |   40 +
 .../workbench/loop/comparisons/NotMatches.java  |   40 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    3 +
 ....ui.views.contextualviews.AddLayerFactorySPI |    1 +
 ...ntextualviews.activity.ContextualViewFactory |    1 +
 .../META-INF/spring/loop-ui-context-osgi.xml    |   21 +
 .../META-INF/spring/loop-ui-context.xml         |   32 +
 .../t2/workbench/loop/ShowContextualView.java   |  121 +
 .../src/test/resources/log4j.properties         |   10 +
 taverna-menu-api/pom.xml                        |   43 +
 .../ui/menu/AbstractContextualMenuAction.java   |   64 +
 .../net/sf/taverna/t2/ui/menu/AbstractMenu.java |  123 +
 .../taverna/t2/ui/menu/AbstractMenuAction.java  |  135 +
 .../taverna/t2/ui/menu/AbstractMenuCustom.java  |  144 +
 .../sf/taverna/t2/ui/menu/AbstractMenuItem.java |  144 +
 .../t2/ui/menu/AbstractMenuOptionGroup.java     |   79 +
 .../taverna/t2/ui/menu/AbstractMenuSection.java |  113 +
 .../taverna/t2/ui/menu/AbstractMenuToggle.java  |  132 +
 .../sf/taverna/t2/ui/menu/AbstractToolBar.java  |   74 +
 .../t2/ui/menu/ContextualMenuComponent.java     |   35 +
 .../taverna/t2/ui/menu/ContextualSelection.java |   48 +
 .../t2/ui/menu/DefaultContextualMenu.java       |   53 +
 .../sf/taverna/t2/ui/menu/DefaultMenuBar.java   |   50 +
 .../sf/taverna/t2/ui/menu/DefaultToolBar.java   |   51 +
 .../sf/taverna/t2/ui/menu/DesignOnlyAction.java |   32 +
 .../t2/ui/menu/DesignOrResultsAction.java       |   32 +
 .../sf/taverna/t2/ui/menu/MenuComponent.java    |  277 ++
 .../net/sf/taverna/t2/ui/menu/MenuManager.java  |  339 ++
 .../net/sf/taverna/t2/ui/menu/package-info.java |  141 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    4 +
 .../META-INF/spring/menu-api-context-osgi.xml   |   13 +
 .../META-INF/spring/menu-api-context.xml        |   10 +
 taverna-menu-impl/pom.xml                       |   82 +
 .../t2/ui/menu/impl/MenuManagerImpl.java        |  880 +++++
 .../t2/workbench/ui/impl/menu/AdvancedMenu.java |   44 +
 .../t2/workbench/ui/impl/menu/EditMenu.java     |   43 +
 .../ui/impl/menu/FeedbackMenuAction.java        |   75 +
 .../t2/workbench/ui/impl/menu/FileMenu.java     |   48 +
 .../t2/workbench/ui/impl/menu/HelpMenu.java     |   44 +
 .../ui/impl/menu/OnlineHelpMenuAction.java      |   68 +
 .../ui/impl/menu/ShowLogsAndDataMenuAction.java |   89 +
 .../ui/impl/menu/ViewShowMenuSection.java       |   40 +
 .../net.sf.taverna.t2.ui.menu.MenuManager       |    1 +
 .../META-INF/spring/menu-impl-context-osgi.xml  |   26 +
 .../META-INF/spring/menu-impl-context.xml       |   25 +
 taverna-menu-items/pom.xml                      |   88 +
 .../AbstractConnectPortMenuActions.java         |  268 ++
 .../activityport/ActivityInputPortSection.java  |   67 +
 .../activityport/ActivityOutputPortSection.java |   67 +
 .../AddInputPortDefaultValueAction.java         |  150 +
 .../ConnectInputPortMenuActions.java            |   41 +
 .../ConnectOutputPortMenuActions.java           |   41 +
 .../items/activityport/ConnectPortsAction.java  |   68 +
 .../CreateAndConnectDataflowPortAction.java     |  226 ++
 .../SetConstantInputPortValueMenuAction.java    |   73 +
 .../SetDefaultInputPortValueAction.java         |  171 +
 .../annotated/AnnotatedConfigureMenuAction.java |   77 +
 .../ConfigureRunningContextualMenuSection.java  |   50 +
 .../items/contextualviews/ConfigureSection.java |   61 +
 .../menu/items/contextualviews/EditSection.java |   73 +
 .../items/contextualviews/InsertSection.java    |   63 +
 .../items/contextualviews/PasteMenuAction.java  |   74 +
 .../ShowConfigureMenuAction.java                |  165 +
 .../ShowDetailsContextualMenuAction.java        |   65 +
 .../contextualviews/ShowDetailsMenuAction.java  |   81 +
 .../ShowReportsContextualMenuAction.java        |  103 +
 .../items/controllink/ConditionSection.java     |   71 +
 .../controllink/RemoveConditionMenuAction.java  |   67 +
 .../t2/ui/menu/items/datalink/LinkSection.java  |   73 +
 .../items/datalink/RemoveLinkMenuAction.java    |   66 +
 .../ConnectDataflowInputPortMenuActions.java    |   42 +
 .../ConnectDataflowOutputPortMenuActions.java   |   42 +
 .../ports/EditDataflowInputPortMenuAction.java  |   68 +
 .../ports/EditDataflowOutputPortMenuAction.java |   68 +
 .../RemoveDataflowInputPortMenuAction.java      |   68 +
 .../RemoveDataflowOutputPortMenuAction.java     |   68 +
 .../items/ports/WorkflowInputPortSection.java   |   73 +
 .../items/ports/WorkflowOutputPortSection.java  |   73 +
 .../items/processor/ConditionMenuActions.java   |  118 +
 .../menu/items/processor/ProcessorSection.java  |   58 +
 .../processor/RemoveProcessorMenuAction.java    |   67 +
 .../processor/RenameProcessorMenuAction.java    |   68 +
 .../items/workflow/CreateInputMenuAction.java   |   62 +
 .../items/workflow/CreateOutputMenuAction.java  |   62 +
 .../WorkflowServiceTemplatesSection.java        |   76 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |   46 +
 .../META-INF/spring/menu-items-context-osgi.xml |   57 +
 .../META-INF/spring/menu-items-context.xml      |  124 +
 taverna-monitor-view/pom.xml                    |   82 +
 .../views/monitor/MonitorViewComponent.java     |  168 +
 .../views/monitor/graph/GraphMonitor.java       |  140 +
 .../views/monitor/graph/GraphMonitorNode.java   |  115 +
 .../monitor/graph/MonitorGraphComponent.java    |  378 ++
 .../progressreport/TableMonitorComponent.java   |  100 +
 .../WorkflowRunProgressTreeCellRenderer.java    |   96 +
 .../WorkflowRunProgressTreeTable.java           |  112 +
 .../WorkflowRunProgressTreeTableModel.java      |  279 ++
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 +
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 +
 .../spring/monitor-view-context-osgi.xml        |    9 +
 .../META-INF/spring/monitor-view-context.xml    |    6 +
 taverna-parallelize-ui/pom.xml                  |   57 +
 .../ParallelizeConfigurationPanel.java          |   99 +
 .../parallelize/ParallelizeConfigureAction.java |  185 +
 .../ParallelizeConfigureMenuAction.java         |   77 +
 .../parallelize/ParallelizeContextualView.java  |  130 +
 .../ParallelizeContextualViewFactory.java       |   56 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    1 +
 ...ntextualviews.activity.ContextualViewFactory |    1 +
 .../spring/parallelize-ui-context-osgi.xml      |   16 +
 .../META-INF/spring/parallelize-ui-context.xml  |   16 +
 .../LocalTestLauncher.bat                       |   14 +
 .../lib/core-renderer.jar                       |  Bin 0 -> 1079323 bytes
 .../log4j.properties                            |    4 +
 taverna-perspective-biocatalogue/pom.xml        |  178 +
 .../move_scomp_results_into_project.bat         |   29 +
 .../scomp_compile_from_web.bat                  |    9 +
 .../scomp_compile_from_web_to_jar.bat           |    9 +
 .../doc/BioCatalogue Plugin Documentation.odt   |  Bin 0 -> 77721 bytes
 .../src/main/help/Index-TOC-Map-Additions.txt   |   20 +
 .../main/help/biocatalogue-plugin-features.html |  113 +
 .../main/help/biocatalogue-plugin-feedback.html |   28 +
 .../src/main/help/biocatalogue-plugin.html      |   54 +
 .../model/BioCataloguePluginConstants.java      |   77 +
 .../model/HTTPMethodInterpreter.java            |   46 +
 .../model/LoadingExpandedResource.java          |   41 +
 .../biocatalogue/model/LoadingResource.java     |   39 +
 .../net/sf/taverna/biocatalogue/model/Pair.java |   30 +
 .../sf/taverna/biocatalogue/model/Resource.java |  506 +++
 .../biocatalogue/model/ResourceManager.java     |  326 ++
 .../model/ResourcePreviewContent.java           |   38 +
 .../model/SoapOperationIdentity.java            |   77 +
 .../model/SoapOperationPortIdentity.java        |   26 +
 .../model/SoapProcessorIdentity.java            |   27 +
 .../biocatalogue/model/SoapServiceIdentity.java |   45 +
 .../net/sf/taverna/biocatalogue/model/Tag.java  |  218 ++
 .../net/sf/taverna/biocatalogue/model/Util.java |  793 ++++
 .../BeanForPOSTToFilteredIndex.java             |   12 +
 .../model/connectivity/BeansForJSONLiteAPI.java |   84 +
 .../connectivity/BioCatalogueAPIRequest.java    |   47 +
 .../model/connectivity/BioCatalogueClient.java  |  785 ++++
 .../model/connectivity/ServerResponse.java      |   40 +
 .../connectivity/ServerResponseStream.java      |   30 +
 .../biocatalogue/model/search/SearchEngine.java |  221 ++
 .../model/search/SearchInstance.java            |  490 +++
 .../model/search/SearchInstanceTracker.java     |   57 +
 .../model/search/SearchOptions.java             |   70 +
 .../model/search/SearchResults.java             |  214 ++
 .../model/search/ServiceFilteringSettings.java  |  184 +
 .../biocatalogue/test/AnnotationBean.java       |   52 +
 .../biocatalogue/test/DrawDefaultIconTest.java  |   38 +
 .../sf/taverna/biocatalogue/test/GSONTest.java  |   19 +
 .../test/GSONTest_exportingJSON.java            |   30 +
 .../test/GSONTest_forSoapOperationsIndex.java   |   27 +
 .../biocatalogue/test/JWaitDialogTest.java      |   36 +
 .../biocatalogue/test/LinkedListEqualsTest.java |   25 +
 .../biocatalogue/test/TestAPICaller.java        |  241 ++
 .../test/TestDoubleUsageOfSameSwingElement.java |   32 +
 .../biocatalogue/test/TestUtilURLHandling.java  |   87 +
 .../biocatalogue/test/TestXHTMLRenderer.java    |   42 +
 .../biocatalogue/test/WrappableJLabelTest.java  |   35 +
 .../taverna/biocatalogue/test/XStreamTest.java  |   32 +
 .../ui/BioCatalogueExplorationTab.java          |  131 +
 .../ui/HasDefaultFocusCapability.java           |   15 +
 .../biocatalogue/ui/JClickableLabel.java        |  172 +
 .../sf/taverna/biocatalogue/ui/JWaitDialog.java |  234 ++
 .../biocatalogue/ui/SearchOptionsPanel.java     |  167 +
 .../ui/filtertree/FilterTreeNode.java           |   91 +
 .../ui/filtertree/FilterTreePane.java           |  348 ++
 .../biocatalogue/ui/filtertree/JFilterTree.java |   69 +
 ...xpandableOnDemandLoadedListCellRenderer.java |  220 ++
 .../RESTMethodListCellRenderer.java             |  248 ++
 .../SOAPOperationListCellRenderer.java          |  257 ++
 .../SearchResultsListingPanel.java              |  870 +++++
 .../search_results/SearchResultsMainPanel.java  |  498 +++
 .../search_results/SearchResultsRenderer.java   |   47 +
 .../search_results/ServiceListCellRenderer.java |  291 ++
 .../ui/tristatetree/JTriStateTree.java          |  631 ++++
 .../tristatetree/Swing - Tristate CheckBox.7z   |  Bin 0 -> 108419 bytes
 .../biocatalogue/ui/tristatetree/Test.java      |   67 +
 .../ui/tristatetree/TriStateCheckBox.java       |  172 +
 .../TriStateCheckBoxTreeCellRenderer.java       |   62 +
 .../TriStateTreeCheckingListener.java           |   12 +
 .../ui/tristatetree/TriStateTreeNode.java       |  246 ++
 .../biocatalogue/BioCataloguePerspective.java   |   70 +
 .../biocatalogue/MainComponent.java             |  285 ++
 .../biocatalogue/MainComponentFactory.java      |   46 +
 .../biocatalogue/MainComponentShutdownHook.java |   49 +
 .../biocatalogue/TestJFrameForLocalLaunch.java  |   68 +
 .../biocatalogue/integration/Integration.java   |  518 +++
 .../config/BioCataloguePluginConfiguration.java |   68 +
 .../BioCataloguePluginConfigurationPanel.java   |  448 +++
 ...ioCataloguePluginConfigurationUIFactory.java |   27 +
 ...aloguePluginInputPortContextViewFactory.java |   45 +
 ...loguePluginOutputPortContextViewFactory.java |   45 +
 ...aloguePluginProcessorContextViewFactory.java |   43 +
 .../ProcessorInputPortView.java                 |   52 +
 .../ProcessorOutputPortView.java                |   52 +
 .../contextual_views/ProcessorView.java         |  229 ++
 .../BioCatalogueWSDLActivityHealthCheck.java    |   40 +
 ...ueWSDLActivityHealthCheckVisitExplainer.java |  111 +
 .../BioCatalogueWSDLActivityHealthChecker.java  |  199 +
 .../health_check/ServiceHealthChecker.java      |  280 ++
 .../ServiceMonitoringStatusInterpreter.java     |   77 +
 .../BioCatalogueContextualMenuSection.java      |   62 +
 .../integration/menus/MenuActionInputPort.java  |   43 +
 .../menus/MenuActionProcessorHealthCheck.java   |   51 +
 .../BioCatalogueRESTServiceProvider.java        |  117 +
 .../BioCatalogueServiceProvider.java            |  274 ++
 ...ioCatalogueWSDLOperationServiceProvider.java |  215 ++
 .../RESTFromBioCatalogueServiceDescription.java |  194 +
 ...ationFromBioCatalogueServiceDescription.java |  116 +
 ...rvicedescriptions.ServiceDescriptionProvider |    2 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    2 +
 .../net.sf.taverna.t2.workbench.ShutdownSPI     |    1 +
 ...rkbench.configuration.ConfigurationUIFactory |    1 +
 ...t2.workbench.report.explainer.VisitExplainer |    1 +
 ...ntextualviews.activity.ContextualViewFactory |    3 +
 ...taverna.t2.workbench.ui.zaria.PerspectiveSPI |    1 +
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 +
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 +
 ...averna.t2.workflowmodel.health.HealthChecker |    1 +
 .../ajax-loader-grey-bert2-still.png            |  Bin 0 -> 1271 bytes
 .../biocatalogue/ajax-loader-grey-bert2.gif     |  Bin 0 -> 3951 bytes
 .../ajax-loader-orange-bert2-still.png          |  Bin 0 -> 1214 bytes
 .../biocatalogue/ajax-loader-orange-bert2.gif   |  Bin 0 -> 3951 bytes
 .../biocatalogue/ajax-loader-still.gif          |  Bin 0 -> 889 bytes
 .../perspectives/biocatalogue/ajax-loader.gif   |  Bin 0 -> 1456 bytes
 .../biocatalogue/biocatalogue-perspective.xml   |   19 +
 .../biocatalogue/biocatalogue_styles.css        | 2673 +++++++++++++
 .../biocatalogue/blue-sphere-50.png             |  Bin 0 -> 1936 bytes
 .../biocatalogue/cross-sphere-35.png            |  Bin 0 -> 2748 bytes
 .../biocatalogue/cross-sphere-50.png            |  Bin 0 -> 7161 bytes
 .../biocatalogue/famfamfam_silk/accept.png      |  Bin 0 -> 781 bytes
 .../biocatalogue/famfamfam_silk/add - tick.pdn  |  Bin 0 -> 5367 bytes
 .../biocatalogue/famfamfam_silk/add - tick.png  |  Bin 0 -> 784 bytes
 .../biocatalogue/famfamfam_silk/add.png         |  Bin 0 -> 733 bytes
 .../famfamfam_silk/application_form_add.png     |  Bin 0 -> 592 bytes
 .../arrow_join (flipped vertically).png         |  Bin 0 -> 669 bytes
 .../biocatalogue/famfamfam_silk/arrow_left.png  |  Bin 0 -> 557 bytes
 .../famfamfam_silk/arrow_refresh.png            |  Bin 0 -> 685 bytes
 .../biocatalogue/famfamfam_silk/arrow_right.png |  Bin 0 -> 596 bytes
 .../famfamfam_silk/chart_organisation.png       |  Bin 0 -> 444 bytes
 .../biocatalogue/famfamfam_silk/cross.png       |  Bin 0 -> 655 bytes
 .../biocatalogue/famfamfam_silk/disk.png        |  Bin 0 -> 620 bytes
 .../biocatalogue/famfamfam_silk/error.png       |  Bin 0 -> 666 bytes
 .../biocatalogue/famfamfam_silk/exclamation.png |  Bin 0 -> 701 bytes
 .../external_link_listing_small.png             |  Bin 0 -> 456 bytes
 .../famfamfam_silk/folder_explore.png           |  Bin 0 -> 679 bytes
 .../biocatalogue/famfamfam_silk/grey_circle.png |  Bin 0 -> 586 bytes
 .../biocatalogue/famfamfam_silk/help.png        |  Bin 0 -> 786 bytes
 .../biocatalogue/famfamfam_silk/information.png |  Bin 0 -> 778 bytes
 .../biocatalogue/famfamfam_silk/lightbulb.png   |  Bin 0 -> 782 bytes
 .../biocatalogue/famfamfam_silk/lock.png        |  Bin 0 -> 749 bytes
 .../biocatalogue/famfamfam_silk/lock_open.png   |  Bin 0 -> 727 bytes
 .../biocatalogue/famfamfam_silk/magnifier.png   |  Bin 0 -> 615 bytes
 .../famfamfam_silk/multiple_star.png            |  Bin 0 -> 676 bytes
 .../famfamfam_silk/page_white_code.png          |  Bin 0 -> 603 bytes
 .../biocatalogue/famfamfam_silk/plugin.png      |  Bin 0 -> 591 bytes
 .../famfamfam_silk/remote_resource.png          |  Bin 0 -> 957 bytes
 .../biocatalogue/famfamfam_silk/server.png      |  Bin 0 -> 530 bytes
 .../biocatalogue/famfamfam_silk/star.png        |  Bin 0 -> 670 bytes
 .../biocatalogue/famfamfam_silk/style.png       |  Bin 0 -> 813 bytes
 .../biocatalogue/famfamfam_silk/sum.png         |  Bin 0 -> 289 bytes
 .../biocatalogue/famfamfam_silk/tag_blue.png    |  Bin 0 -> 586 bytes
 .../text_linespacing (collapse).png             |  Bin 0 -> 371 bytes
 .../famfamfam_silk/text_linespacing.png         |  Bin 0 -> 363 bytes
 .../famfamfam_silk/text_list_numbers.png        |  Bin 0 -> 357 bytes
 .../biocatalogue/famfamfam_silk/tick.png        |  Bin 0 -> 537 bytes
 .../biocatalogue/famfamfam_silk/user.png        |  Bin 0 -> 741 bytes
 .../t2/ui/perspectives/biocatalogue/favicon.png |  Bin 0 -> 1336 bytes
 .../ui/perspectives/biocatalogue/folds/fold.png |  Bin 0 -> 331 bytes
 .../biocatalogue/folds/fold_16x16.png           |  Bin 0 -> 361 bytes
 .../perspectives/biocatalogue/folds/unfold.png  |  Bin 0 -> 334 bytes
 .../biocatalogue/folds/unfold_16x16.png         |  Bin 0 -> 354 bytes
 .../biocatalogue/info-sphere-35.png             |  Bin 0 -> 2492 bytes
 .../biocatalogue/info-sphere-50.png             |  Bin 0 -> 2439 bytes
 .../biocatalogue/open_in_BioCatalogue.pdn       |  Bin 0 -> 5128 bytes
 .../biocatalogue/open_in_BioCatalogue.png       |  Bin 0 -> 663 bytes
 .../biocatalogue/pling-sphere-35.png            |  Bin 0 -> 2275 bytes
 .../biocatalogue/pling-sphere-50.png            |  Bin 0 -> 1724 bytes
 .../biocatalogue/query-sphere-35.png            |  Bin 0 -> 2473 bytes
 .../biocatalogue/query-sphere-50.png            |  Bin 0 -> 1854 bytes
 .../service_icons/service_type_multitype.png    |  Bin 0 -> 1133 bytes
 .../service_icons/service_type_rest.png         |  Bin 0 -> 1137 bytes
 .../service_icons/service_type_soap.png         |  Bin 0 -> 1131 bytes
 .../service_icons/service_type_unknown.png      |  Bin 0 -> 1076 bytes
 .../soap_rest_multitype_unknown.pdn             |  Bin 0 -> 6693 bytes
 .../t2/ui/perspectives/biocatalogue/styles.css  |  290 ++
 .../t2/ui/perspectives/biocatalogue/test.html   |   78 +
 .../biocatalogue/tick-sphere-35.png             |  Bin 0 -> 2479 bytes
 .../biocatalogue/tick-sphere-50.png             |  Bin 0 -> 1909 bytes
 .../t2/ui/perspectives/biocatalogue/trash.png   |  Bin 0 -> 460 bytes
 .../tristate_checkbox_checked.png               |  Bin 0 -> 1050 bytes
 .../tristate_checkbox_partial.png               |  Bin 0 -> 1042 bytes
 .../tristate_checkbox_partial_green.png         |  Bin 0 -> 993 bytes
 .../tristate_checkbox_unchecked.png             |  Bin 0 -> 1033 bytes
 .../ui/perspectives/biocatalogue/unchecked.png  |  Bin 0 -> 3636 bytes
 .../src/main/xsd/dc.xsd                         |  119 +
 .../src/main/xsd/dcterms.xsd                    |  137 +
 .../src/main/xsd/schema-v1.xsd                  | 3557 ++++++++++++++++++
 .../src/main/xsd/xlink.xsd                      |   83 +
 taverna-perspective-design/pom.xml              |   98 +
 .../perspectives/design/DesignPerspective.java  |  118 +
 .../design/DesignPerspectiveComponent.java      |  115 +
 .../design/WorkflowBundleSelectorComponent.java |  113 +
 .../design/WorkflowSelectorComponent.java       |  167 +
 .../t2/ui/perspectives/design/WorkflowTab.java  |  133 +
 ...taverna.t2.workbench.ui.zaria.PerspectiveSPI |    1 +
 .../spring/perspective-design-context-osgi.xml  |   21 +
 .../spring/perspective-design-context.xml       |   18 +
 taverna-perspective-myexperiment/pom.xml        |  100 +
 .../myexperiment/AddCommentDialog.java          |  330 ++
 .../myexperiment/AddRemoveFavouriteDialog.java  |  277 ++
 .../myexperiment/ExampleWorkflowsPanel.java     |  153 +
 .../HistoryBrowserTabContentPanel.java          |  541 +++
 .../myexperiment/JClickableLabel.java           |  127 +
 .../myexperiment/MainComponent.java             |  645 ++++
 .../myexperiment/MainComponentFactory.java      |   60 +
 .../myexperiment/MainComponentShutdownHook.java |   84 +
 .../myexperiment/MyExperimentPerspective.java   |  190 +
 .../myexperiment/MyStuffContributionsPanel.java |  370 ++
 .../myexperiment/MyStuffSidebarPanel.java       |  359 ++
 .../myexperiment/MyStuffTabContentPanel.java    |  342 ++
 .../myexperiment/PluginPreferencesDialog.java   |  372 ++
 .../myexperiment/PluginStatusBar.java           |  195 +
 .../myexperiment/ResourceListPanel.java         |  182 +
 .../myexperiment/ResourcePreviewBrowser.java    |  711 ++++
 .../myexperiment/ResourcePreviewContent.java    |   78 +
 .../myexperiment/ResourcePreviewFactory.java    | 1359 +++++++
 .../myexperiment/SearchOptionsPanel.java        |  317 ++
 .../myexperiment/SearchResultsPanel.java        |  201 +
 .../myexperiment/SearchTabContentPanel.java     |  450 +++
 .../myexperiment/StyledHTMLEditorKit.java       |   19 +
 .../myexperiment/TagBrowserTabContentPanel.java |  226 ++
 .../myexperiment/TagCloudPanel.java             |  342 ++
 .../myexperiment/TestJFrameForLocalLaunch.java  |   48 +
 .../myexperiment/UploadWorkflowDialog.java      |  849 +++++
 .../myexperiment/model/Base64$InputStream.class |  Bin 0 -> 2083 bytes
 .../model/Base64$OutputStream.class             |  Bin 0 -> 2307 bytes
 .../myexperiment/model/Base64.class             |  Bin 0 -> 15458 bytes
 .../perspectives/myexperiment/model/Base64.java | 1813 +++++++++
 .../myexperiment/model/Comment.java             |  141 +
 .../perspectives/myexperiment/model/File.java   |  237 ++
 .../perspectives/myexperiment/model/Group.java  |  222 ++
 .../myexperiment/model/License.java             |   62 +
 .../myexperiment/model/MyExperimentClient.class |  Bin 0 -> 3174 bytes
 .../myexperiment/model/MyExperimentClient.java  | 1218 ++++++
 .../perspectives/myexperiment/model/Pack.java   |  232 ++
 .../myexperiment/model/PackItem.java            |  186 +
 .../myexperiment/model/Resource.java            |  683 ++++
 .../myexperiment/model/SearchEngine.java        |  321 ++
 .../myexperiment/model/ServerResponse.java      |   59 +
 .../ui/perspectives/myexperiment/model/Tag.java |  125 +
 .../myexperiment/model/TagCloud.java            |   47 +
 .../perspectives/myexperiment/model/User.java   |  297 ++
 .../perspectives/myexperiment/model/Util.java   |  624 +++
 .../myexperiment/model/Workflow.java            |  420 +++
 .../config/MyExperimentConfiguration.java       |   68 +
 .../config/MyExperimentConfigurationPanel.java  |  305 ++
 .../MyExperimentConfigurationUIFactory.java     |   62 +
 .../TestJFrameForPreferencesLocalLaunch.java    |   46 +
 .../net.sf.taverna.t2.workbench.ShutdownSPI     |    1 +
 ...rkbench.configuration.ConfigurationUIFactory |    1 +
 ...taverna.t2.workbench.ui.zaria.PerspectiveSPI |    1 +
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 +
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 +
 .../perspective-myexperiment-context-osgi.xml   |   20 +
 .../spring/perspective-myexperiment-context.xml |   20 +
 .../myexperiment/ajax-loader-still.gif          |  Bin 0 -> 889 bytes
 .../perspectives/myexperiment/ajax-loader.gif   |  Bin 0 -> 1456 bytes
 .../ui/perspectives/myexperiment/arrow_left.png |  Bin 0 -> 557 bytes
 .../perspectives/myexperiment/arrow_refresh.png |  Bin 0 -> 685 bytes
 .../perspectives/myexperiment/arrow_right.png   |  Bin 0 -> 596 bytes
 .../perspectives/myexperiment/comment_add.png   |  Bin 0 -> 530 bytes
 .../myexperiment/comment_delete.png             |  Bin 0 -> 548 bytes
 .../t2/ui/perspectives/myexperiment/cross.png   |  Bin 0 -> 655 bytes
 .../t2/ui/perspectives/myexperiment/denied.png  |  Bin 0 -> 701 bytes
 .../myexperiment/dummy-workflow.t2flow          |  157 +
 .../external_link_listing_small.png             |  Bin 0 -> 456 bytes
 .../perspectives/myexperiment/favourite_add.png |  Bin 0 -> 701 bytes
 .../myexperiment/favourite_delete.png           |  Bin 0 -> 722 bytes
 .../t2/ui/perspectives/myexperiment/file.png    |  Bin 0 -> 562 bytes
 .../t2/ui/perspectives/myexperiment/group.png   |  Bin 0 -> 753 bytes
 .../t2/ui/perspectives/myexperiment/login.png   |  Bin 0 -> 693 bytes
 .../t2/ui/perspectives/myexperiment/logout.png  |  Bin 0 -> 688 bytes
 .../ui/perspectives/myexperiment/myexp_icon.png |  Bin 0 -> 1611 bytes
 .../myexperiment/myexp_icon16x16.png            |  Bin 0 -> 1057 bytes
 .../myexperiment/myexperiment-perspective.xml   |   17 +
 .../myexperiment/open_in_myExperiment.png       |  Bin 0 -> 621 bytes
 .../t2/ui/perspectives/myexperiment/pack.png    |  Bin 0 -> 565 bytes
 .../myexperiment/remote_resource.png            |  Bin 0 -> 957 bytes
 .../t2/ui/perspectives/myexperiment/star.png    |  Bin 0 -> 670 bytes
 .../t2/ui/perspectives/myexperiment/styles.css  |  380 ++
 .../ui/perspectives/myexperiment/tag_blue.png   |  Bin 0 -> 586 bytes
 .../t2/ui/perspectives/myexperiment/tick.png    |  Bin 0 -> 537 bytes
 .../myexperiment/transparent_icon.png           |  Bin 0 -> 1172 bytes
 .../t2/ui/perspectives/myexperiment/user.png    |  Bin 0 -> 741 bytes
 .../ui/perspectives/myexperiment/workflow.png   |  Bin 0 -> 975 bytes
 taverna-perspective-results/pom.xml             |   80 +
 .../results/ResultsPerspective.java             |  158 +
 .../results/ResultsPerspectiveComponent.java    |  221 ++
 .../t2/ui/perspectives/results/RunMonitor.java  |  171 +
 .../results/RunSelectorComponent.java           |   72 +
 .../t2/ui/perspectives/results/RunTab.java      |  133 +
 .../t2/ui/perspectives/results/WorkflowRun.java |   54 +
 .../results/WorkflowRunListCellRenderer.java    |   59 +
 .../results/WorkflowRunListModel.java           |   66 +
 ...taverna.t2.workbench.ui.zaria.PerspectiveSPI |    1 +
 .../spring/perspective-results-context-osgi.xml |   27 +
 .../spring/perspective-results-context.xml      |   19 +
 taverna-plugin-manager/pom.xml                  |   65 +
 .../plugin/impl/AvailablePluginPanel.java       |   72 +
 .../plugin/impl/InstalledPluginPanel.java       |   66 +
 .../plugin/impl/PluginManagerPanel.java         |  210 ++
 .../plugin/impl/PluginManagerView.java          |   71 +
 .../t2/workbench/plugin/impl/PluginPanel.java   |  166 +
 .../plugin/impl/UpdatePluginPanel.java          |   72 +
 .../plugin/impl/menu/PluginMenuAction.java      |   56 +
 .../spring/plugin-manager-context-osgi.xml      |   20 +
 .../META-INF/spring/plugin-manager-context.xml  |   15 +
 taverna-plugins-gui/pom.xml                     |   62 +
 .../raven/plugins/ui/AddPluginSiteFrame.java    |  273 ++
 .../plugins/ui/CheckForNoticeStartupHook.java   |  143 +
 .../raven/plugins/ui/CheckForUpdatesDialog.java |  122 +
 .../plugins/ui/CheckForUpdatesStartupHook.java  |   94 +
 .../plugins/ui/PluginListCellRenderer.java      |  214 ++
 .../raven/plugins/ui/PluginListModel.java       |  108 +
 .../raven/plugins/ui/PluginManagerFrame.java    |  516 +++
 .../plugins/ui/PluginRepositoryListener.java    |  148 +
 .../raven/plugins/ui/PluginSiteFrame.java       |  542 +++
 .../raven/plugins/ui/UpdatesAvailableIcon.java  |  205 +
 .../profile/ui/ProfileVersionCellRenderer.java  |  177 +
 .../profile/ui/ProfileVersionListFrame.java     |  260 ++
 .../profile/ui/ProfileVersionListModel.java     |  101 +
 .../net.sf.taverna.t2.workbench.StartupSPI      |    2 +
 .../spring/plugins-gui-context-osgi.xml         |   11 +
 .../META-INF/spring/plugins-gui-context.xml     |    8 +
 .../net/sf/taverna/raven/plugins/ui/update.png  |  Bin 0 -> 689 bytes
 .../raven/plugins/ui/updateRecommended.png      |  Bin 0 -> 731 bytes
 taverna-reference-ui/pom.xml                    |   81 +
 .../t2/reference/ui/CheckWorkflowStatus.java    |   97 +
 .../ui/CopyWorkflowInProgressDialog.java        |   91 +
 .../reference/ui/CopyWorkflowSwingWorker.java   |   51 +
 .../t2/reference/ui/InvalidDataflowReport.java  |   95 +
 .../t2/reference/ui/RegistrationPanel.java      |  808 ++++
 .../sf/taverna/t2/reference/ui/UrlPanel.java    |  100 +
 .../t2/reference/ui/WorkflowLaunchWindow.java   |  626 +++
 .../net/sf/taverna/t2/reference/ui/package.html |    4 +
 .../ui/referenceactions/LoadInputsFromXML.java  |  120 +
 .../ui/referenceactions/ReferenceActionSPI.java |   38 +
 .../ui/referenceactions/SaveInputsAsXML.java    |  207 +
 .../reference/ui/tree/PreRegistrationTree.java  |  217 ++
 .../tree/PreRegistrationTreeCellRenderer.java   |  115 +
 .../ui/tree/PreRegistrationTreeDnDHandler.java  |  268 ++
 .../ui/tree/PreRegistrationTreeModel.java       |  310 ++
 .../taverna/t2/reference/ui/tree/package.html   |    6 +
 ...rence.ui.referenceactions.ReferenceActionSPI |    2 +
 .../spring/reference-ui-context-osgi.xml        |   12 +
 .../META-INF/spring/reference-ui-context.xml    |    9 +
 .../src/main/resources/icons/addtext_co.gif     |  Bin 0 -> 335 bytes
 .../main/resources/icons/complete_status.gif    |  Bin 0 -> 76 bytes
 .../src/main/resources/icons/deadlock_view.gif  |  Bin 0 -> 239 bytes
 .../src/main/resources/icons/delete_obj.gif     |  Bin 0 -> 351 bytes
 .../src/main/resources/icons/det_pane_hide.gif  |  Bin 0 -> 370 bytes
 .../src/main/resources/icons/error_tsk.gif      |  Bin 0 -> 353 bytes
 .../main/resources/icons/errorwarning_tab.gif   |  Bin 0 -> 577 bytes
 .../resources/icons/genericregister_obj.gif     |  Bin 0 -> 115 bytes
 .../src/main/resources/icons/information.gif    |  Bin 0 -> 267 bytes
 .../main/resources/icons/invalid_build_tool.gif |  Bin 0 -> 339 bytes
 .../src/main/resources/icons/newfolder_wiz.gif  |  Bin 0 -> 349 bytes
 .../src/main/resources/icons/repo_rep.gif       |  Bin 0 -> 588 bytes
 .../src/main/resources/icons/start_task.gif     |  Bin 0 -> 318 bytes
 .../src/main/resources/icons/topic.gif          |  Bin 0 -> 354 bytes
 .../src/main/resources/icons/web.gif            |  Bin 0 -> 362 bytes
 .../src/main/resources/icons/wordassist_co.gif  |  Bin 0 -> 152 bytes
 .../src/main/resources/icons/write_obj.gif      |  Bin 0 -> 210 bytes
 taverna-renderers-api/pom.xml                   |   42 +
 .../net/sf/taverna/t2/renderers/Renderer.java   |   33 +
 .../taverna/t2/renderers/RendererException.java |   46 +
 .../taverna/t2/renderers/RendererRegistry.java  |   48 +
 .../sf/taverna/t2/renderers/RendererUtils.java  |   89 +
 taverna-renderers-exts/pom.xml                  |   77 +
 .../t2/renderers/HTMLBrowserRenderer.java       |   95 +
 .../sf/taverna/t2/renderers/JMolRenderer.java   |  178 +
 .../sf/taverna/t2/renderers/PDFRenderer.java    |  124 +
 .../sf/taverna/t2/renderers/SVGRenderer.java    |  154 +
 .../taverna/t2/renderers/SeqVistaRenderer.java  |  167 +
 .../net.sf.taverna.t2.renderers.Renderer        |    5 +
 .../spring/renderers-exts-context-osgi.xml      |   15 +
 .../META-INF/spring/renderers-exts-context.xml  |   12 +
 .../taverna/t2/renderers/TestRendererSPI.java   |  153 +
 taverna-renderers-impl/pom.xml                  |   80 +
 .../t2/renderers/impl/AbstractRenderer.java     |  124 +
 .../renderers/impl/AdvancedImageRenderer.java   |   94 +
 .../t2/renderers/impl/ExtensionFileFilter.java  |   77 +
 .../t2/renderers/impl/HexBinaryRenderer.java    |   77 +
 .../t2/renderers/impl/RendererConstants.java    |   13 +
 .../t2/renderers/impl/RendererRegistryImpl.java |   54 +
 .../taverna/t2/renderers/impl/TextRenderer.java |  137 +
 .../t2/renderers/impl/TextRtfRenderer.java      |   85 +
 .../t2/renderers/impl/TextXMLRenderer.java      |   86 +
 .../sf/taverna/t2/renderers/impl/XMLTree.java   |  329 ++
 .../net.sf.taverna.t2.renderers.Renderer        |    7 +
 .../spring/renderers-impl-context-osgi.xml      |   19 +
 .../META-INF/spring/renderers-impl-context.xml  |   17 +
 .../taverna/t2/renderers/TestRendererSPI.java   |  154 +
 taverna-report-api/pom.xml                      |   74 +
 .../t2/workbench/report/ProfileReportEvent.java |   21 +
 .../t2/workbench/report/ReportManager.java      |   44 +
 .../t2/workbench/report/ReportManagerEvent.java |   10 +
 .../config/ReportManagerConfiguration.java      |   44 +
 .../services/net.sf.taverna.t2.visit.VisitKind  |    1 +
 ....taverna.t2.visit.fragility.FragilityChecker |    1 +
 .../META-INF/spring/report-api-context-osgi.xml |   13 +
 .../META-INF/spring/report-api-context.xml      |   10 +
 taverna-report-explainer/pom.xml                |  118 +
 .../report/explainer/BasicExplainer.java        | 1294 +++++++
 ...t2.workbench.report.explainer.VisitExplainer |    1 +
 .../spring/report-explainer-context-osgi.xml    |   19 +
 .../spring/report-explainer-context.xml         |   15 +
 taverna-report-impl/pom.xml                     |   56 +
 .../impl/ReportManagerConfigurationImpl.java    |   71 +
 .../report/impl/ReportManagerImpl.java          |  564 +++
 .../spring/report-impl-context-osgi.xml         |   23 +
 .../META-INF/spring/report-impl-context.xml     |   21 +
 taverna-report-view/pom.xml                     |  100 +
 .../ui/ReportManagerConfigurationPanel.java     |  363 ++
 .../ui/ReportManagerConfigurationUIFactory.java |   55 +
 .../ReportOnObjectContextualMenuAction.java     |  189 +
 .../report/view/ReportOnWorkflowAction.java     |  177 +
 .../report/view/ReportViewComponent.java        |  574 +++
 .../report/view/ReportViewComponentFactory.java |   75 +
 .../report/view/ReportViewConfigureAction.java  |   45 +
 .../report/view/ReportViewTableModel.java       |  284 ++
 .../workbench/report/view/StatusRenderer.java   |   46 +
 .../report/view/ValidateInProgressDialog.java   |   92 +
 .../view/ValidateObjectInProgressDialog.java    |   93 +
 .../report/view/ValidateObjectSwingWorker.java  |   71 +
 .../report/view/ValidateSwingWorker.java        |  123 +
 .../workbench/report/view/VisitReportProxy.java |   42 +
 .../report/view/VisitReportProxySet.java        |   36 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    1 +
 ...rkbench.configuration.ConfigurationUIFactory |    1 +
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 +
 .../spring/report-view-context-osgi.xml         |   25 +
 .../META-INF/spring/report-view-context.xml     |   28 +
 taverna-results-view/pom.xml                    |  127 +
 .../views/results/InvocationTreeModel.java      |  103 +
 .../views/results/InvocationTreeNode.java       |  100 +
 .../workbench/views/results/InvocationView.java |  136 +
 .../t2/workbench/views/results/ReportView.java  |  420 +++
 .../views/results/ResultsComponent.java         |  233 ++
 .../views/results/SimpleFilteredTreeModel.java  |   68 +
 .../processor/FilteredIterationTreeModel.java   |   91 +
 .../FilteredProcessorValueTreeModel.java        |   70 +
 .../IntermediateValuesInProgressDialog.java     |   89 +
 .../IntermediateValuesSwingWorker.java          |   47 +
 .../results/processor/IterationTreeNode.java    |   99 +
 .../processor/ProcessorEnactmentsTreeModel.java |  189 +
 .../processor/ProcessorEnactmentsTreeNode.java  |   84 +
 .../processor/ProcessorPortResultsViewTab.java  |  229 ++
 .../processor/ProcessorResultCellRenderer.java  |   89 +
 .../processor/ProcessorResultTreeNode.java      |  181 +
 .../processor/ProcessorResultsComponent.java    | 1004 +++++
 .../processor/ProcessorResultsTreeModel.java    |   73 +
 .../RenderedProcessorResultComponent.java       |  575 +++
 .../saveactions/SaveAllResultsAsExcel.java      |  296 ++
 .../saveactions/SaveAllResultsAsXML.java        |   80 +
 .../results/saveactions/SaveAllResultsSPI.java  |  193 +
 .../saveactions/SaveAllResultsToFileSystem.java |   94 +
 .../saveactions/SaveIndividualResult.java       |  186 +
 .../saveactions/SaveIndividualResultSPI.java    |   48 +
 .../results/workflow/DataBundleTreeModel.java   |  136 +
 .../workflow/FilteredDataBundleTreeModel.java   |  147 +
 .../FilteredWorkflowResultTreeModel.java        |  126 +
 .../workflow/PortResultCellRenderer.java        |  100 +
 .../results/workflow/PortResultsViewTab.java    |  283 ++
 .../workflow/RenderedResultComponent.java       |  601 +++
 .../workflow/WorkflowResultTreeModel.java       |  197 +
 .../workflow/WorkflowResultTreeNode.java        |  120 +
 .../workflow/WorkflowResultsComponent.java      |  352 ++
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 +
 ....views.results.saveactions.SaveAllResultsSPI |    3 +
 ....results.saveactions.SaveIndividualResultSPI |    1 +
 .../spring/results-view-context-osgi.xml        |   14 +
 .../META-INF/spring/results-view-context.xml    |   11 +
 taverna-retry-ui/pom.xml                        |   57 +
 .../retry/RetryConfigurationPanel.java          |  172 +
 .../workbench/retry/RetryConfigureAction.java   |  183 +
 .../retry/RetryConfigureMenuAction.java         |   77 +
 .../t2/workbench/retry/RetryContextualView.java |  165 +
 .../retry/RetryContextualViewFactory.java       |   58 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    1 +
 ...ntextualviews.activity.ContextualViewFactory |    1 +
 .../META-INF/spring/retry-ui-context-osgi.xml   |   16 +
 .../META-INF/spring/retry-ui-context.xml        |   16 +
 taverna-run-ui/pom.xml                          |   71 +
 .../run/actions/OpenWorkflowRunAction.java      |  135 +
 .../run/actions/RunWorkflowAction.java          |  299 ++
 .../run/actions/ValidateWorkflowAction.java     |   56 +
 .../WorkflowRunStatusShutdownDialog.java        |  153 +
 .../cleanup/WorkflowRunStatusShutdownHook.java  |  110 +
 .../run/menu/FileOpenRunMenuAction.java         |   63 +
 .../workbench/run/menu/FileRunMenuAction.java   |   82 +
 .../workbench/run/menu/FileRunMenuSection.java  |   36 +
 .../run/menu/FileValidateMenuAction.java        |   70 +
 .../workbench/run/toolbar/RunToolbarAction.java |   82 +
 .../run/toolbar/RunToolbarSection.java          |   36 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    6 +
 .../net.sf.taverna.t2.workbench.ShutdownSPI     |    5 +
 .../net.sf.taverna.t2.workbench.StartupSPI      |    1 +
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 +
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 +
 .../META-INF/spring/run-ui-context-osgi.xml     |   28 +
 .../META-INF/spring/run-ui-context.xml          |   41 +
 taverna-selection-api/pom.xml                   |   69 +
 .../selection/DataflowSelectionModel.java       |   85 +
 .../workbench/selection/SelectionManager.java   |  113 +
 .../events/DataflowSelectionMessage.java        |   66 +
 .../events/PerspectiveSelectionEvent.java       |   59 +
 .../selection/events/ProfileSelectionEvent.java |   57 +
 .../selection/events/SelectionManagerEvent.java |   34 +
 .../events/WorkflowBundleSelectionEvent.java    |   59 +
 .../events/WorkflowRunSelectionEvent.java       |   51 +
 .../events/WorkflowSelectionEvent.java          |   57 +
 taverna-selection-impl/pom.xml                  |   51 +
 .../impl/DataflowSelectionModelImpl.java        |  116 +
 .../selection/impl/SelectionManagerImpl.java    |  367 ++
 .../spring/selection-impl-context-osgi.xml      |   17 +
 .../META-INF/spring/selection-impl-context.xml  |   12 +
 taverna-update-manager/pom.xml                  |   42 +
 .../update/impl/UpdateManagerView.java          |   45 +
 .../update/impl/menu/UpdateMenuAction.java      |   92 +
 .../updatemanager/PluginMenuAction.java         |   52 +
 .../UpdatesAvailableMenuAction.java             |   19 +
 .../updatemanager/UpdatesToolbarSection.java    |   16 +
 .../spring/update-manager-context-osgi.xml      |   13 +
 .../META-INF/spring/update-manager-context.xml  |   12 +
 taverna-workbench-activity-icons-api/pom.xml    |   37 -
 .../activityicons/ActivityIconManager.java      |   41 -
 .../activityicons/ActivityIconSPI.java          |   57 -
 .../activityicons/DefaultActivityIcon.java      |   54 -
 .../impl/ActivityIconManagerImpl.java           |   85 -
 ...a.t2.workbench.activityicons.ActivityIconSPI |    1 -
 .../spring/activity-icons-api-context-osgi.xml  |   15 -
 .../spring/activity-icons-api-context.xml       |   13 -
 .../main/resources/default-activity-icon.png    |  Bin 577 -> 0 bytes
 taverna-workbench-activity-palette-api/pom.xml  |   63 -
 .../AbstractConfigurableServiceProvider.java    |   53 -
 .../AbstractTemplateService.java                |   85 -
 .../ConfigurableServiceProvider.java            |   10 -
 .../CustomizedConfigurePanelProvider.java       |   36 -
 .../servicedescriptions/IdentifiedObject.java   |   30 -
 .../servicedescriptions/ServiceDescription.java |   80 -
 .../ServiceDescriptionProvider.java             |   61 -
 .../ServiceDescriptionRegistry.java             |   50 -
 .../ServiceDescriptionsConfiguration.java       |   36 -
 .../events/AbstractProviderEvent.java           |   16 -
 .../events/AbstractProviderNotification.java    |   18 -
 .../events/AddedProviderEvent.java              |   10 -
 .../PartialServiceDescriptionsNotification.java |   22 -
 .../events/ProviderErrorNotification.java       |   19 -
 .../events/ProviderStatusNotification.java      |   12 -
 .../events/ProviderUpdatingNotification.java    |   11 -
 .../events/ProviderWarningNotification.java     |   12 -
 .../events/RemovedProviderEvent.java            |   10 -
 .../events/ServiceDescriptionProvidedEvent.java |   20 -
 .../events/ServiceDescriptionRegistryEvent.java |    4 -
 taverna-workbench-activity-palette-impl/pom.xml |   98 -
 .../impl/ServiceDescriptionConstants.java       |   10 -
 .../impl/ServiceDescriptionDeserializer.java    |  167 -
 .../impl/ServiceDescriptionRegistryImpl.java    |  652 ----
 .../impl/ServiceDescriptionSerializer.java      |  102 -
 .../impl/ServiceDescriptionXMLConstants.java    |   15 -
 .../ServiceDescriptionsConfigurationImpl.java   |   92 -
 .../ActivityPaletteConfiguration.java           |   81 -
 .../ActivityPaletteConfigurationPanel.java      |  284 --
 .../ActivityPaletteConfigurationUIFactory.java  |   52 -
 ...rkbench.configuration.ConfigurationUIFactory |    1 -
 .../activity-palette-impl-context-osgi.xml      |   17 -
 .../spring/activity-palette-impl-context.xml    |   23 -
 .../ActivityPaletteConfigurationTest.java       |   97 -
 ...averna.t2.partition.PartitionAlgorithmSetSPI |    1 -
 ...sf.taverna.t2.partition.PropertyExtractorSPI |    3 -
 .../net.sf.taverna.t2.partition.QueryFactory    |    2 -
 taverna-workbench-activity-palette-ui/pom.xml   |   88 -
 .../servicepanel/PathElementFilterTreeNode.java |   34 -
 .../ui/servicepanel/RootFilterTreeNode.java     |   34 -
 .../ui/servicepanel/ServiceFilter.java          |  158 -
 .../ui/servicepanel/ServiceFilterTreeNode.java  |   40 -
 .../workbench/ui/servicepanel/ServicePanel.java |  411 --
 .../ServicePanelComponentFactory.java           |   82 -
 .../servicepanel/ServiceTreeCellRenderer.java   |   77 -
 .../servicepanel/ServiceTreeClickListener.java  |  252 --
 .../ui/servicepanel/ServiceTreePanel.java       |  176 -
 .../actions/AddServiceProviderAction.java       |  256 --
 .../ExportServiceDescriptionsAction.java        |  155 -
 ...ImportServiceDescriptionsFromFileAction.java |  158 -
 .../ImportServiceDescriptionsFromURLAction.java |  129 -
 .../actions/RefreshProviderRegistryAction.java  |   52 -
 .../actions/RemoveDefaultServicesAction.java    |   51 -
 .../actions/RemoveUserServicesAction.java       |   59 -
 .../actions/RestoreDefaultServicesAction.java   |   50 -
 .../config/ServiceDescriptionConfigPanel.java   |  181 -
 .../ServiceDescriptionConfigUIFactory.java      |   57 -
 .../menu/AddServiceProviderMenu.java            |  113 -
 .../workbench/ui/servicepanel/tree/Filter.java  |   33 -
 .../tree/FilterTreeCellRenderer.java            |   59 -
 .../ui/servicepanel/tree/FilterTreeModel.java   |   92 -
 .../ui/servicepanel/tree/FilterTreeNode.java    |  142 -
 .../tree/FilterTreeSelectionModel.java          |   46 -
 .../ui/servicepanel/tree/MyFilter.java          |   89 -
 .../ui/servicepanel/tree/TreePanel.java         |  371 --
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 -
 .../spring/activity-palette-ui-context-osgi.xml |   20 -
 .../spring/activity-palette-ui-context.xml      |   22 -
 taverna-workbench-activity-tools/pom.xml        |   46 -
 .../AbstractConfigureActivityMenuAction.java    |   64 -
 taverna-workbench-api/pom.xml                   |   30 +
 .../net/sf/taverna/t2/workbench/MainWindow.java |   16 +
 .../taverna/t2/workbench/ModelMapConstants.java |   28 +
 .../sf/taverna/t2/workbench/ShutdownSPI.java    |   72 +
 .../net/sf/taverna/t2/workbench/StartupSPI.java |   65 +
 .../t2/workbench/icons/WorkbenchIcons.java      |  214 ++
 .../ui/SwingWorkerCompletionWaiter.java         |   33 +
 .../sf/taverna/t2/workbench/ui/Updatable.java   |   32 +
 .../net/sf/taverna/t2/workbench/ui/Utils.java   |   20 +
 .../sf/taverna/t2/workbench/ui/Workbench.java   |   33 +
 .../t2/workbench/ui/zaria/PerspectiveSPI.java   |   77 +
 .../ui/zaria/UIComponentFactorySPI.java         |   56 +
 .../t2/workbench/ui/zaria/UIComponentSPI.java   |   56 +
 .../t2/workbench/icons/explorer/biomoby.png     |  Bin 0 -> 1218 bytes
 .../t2/workbench/icons/explorer/constraint.gif  |  Bin 0 -> 144 bytes
 .../t2/workbench/icons/explorer/dataflow.png    |  Bin 0 -> 814 bytes
 .../t2/workbench/icons/explorer/datalink.gif    |  Bin 0 -> 124 bytes
 .../t2/workbench/icons/explorer/input.png       |  Bin 0 -> 396 bytes
 .../t2/workbench/icons/explorer/inputport.png   |  Bin 0 -> 251 bytes
 .../t2/workbench/icons/explorer/localworker.png |  Bin 0 -> 706 bytes
 .../t2/workbench/icons/explorer/merge.png       |  Bin 0 -> 422 bytes
 .../t2/workbench/icons/explorer/output.png      |  Bin 0 -> 425 bytes
 .../t2/workbench/icons/explorer/outputport.png  |  Bin 0 -> 235 bytes
 .../t2/workbench/icons/explorer/rserv.png       |  Bin 0 -> 1235 bytes
 .../t2/workbench/icons/explorer/seqhound.png    |  Bin 0 -> 3603 bytes
 .../t2/workbench/icons/explorer/soaplab.png     |  Bin 0 -> 701 bytes
 .../workbench/icons/explorer/stringconstant.png |  Bin 0 -> 733 bytes
 .../t2/workbench/icons/explorer/talisman.png    |  Bin 0 -> 1214 bytes
 .../icons/explorer/unknownprocessor.png         |  Bin 0 -> 1060 bytes
 .../icons/explorer/workflow-explorer-old.png    |  Bin 0 -> 255 bytes
 .../icons/explorer/workflow-explorer.png        |  Bin 0 -> 267 bytes
 .../t2/workbench/icons/explorer/workflow.png    |  Bin 0 -> 1213 bytes
 .../icons/explorer/workflowInputPort.png        |  Bin 0 -> 718 bytes
 .../icons/explorer/workflowOutputPort.png       |  Bin 0 -> 636 bytes
 .../t2/workbench/icons/explorer/wsdl.png        |  Bin 0 -> 748 bytes
 .../taverna/t2/workbench/icons/generic/bin.png  |  Bin 0 -> 393 bytes
 .../t2/workbench/icons/generic/break.gif        |  Bin 0 -> 1120 bytes
 .../t2/workbench/icons/generic/close.gif        |  Bin 0 -> 351 bytes
 .../t2/workbench/icons/generic/closeAll.gif     |  Bin 0 -> 380 bytes
 .../t2/workbench/icons/generic/configure.png    |  Bin 0 -> 610 bytes
 .../taverna/t2/workbench/icons/generic/copy.png |  Bin 0 -> 389 bytes
 .../taverna/t2/workbench/icons/generic/cut.png  |  Bin 0 -> 710 bytes
 .../t2/workbench/icons/generic/database.gif     |  Bin 0 -> 1032 bytes
 .../t2/workbench/icons/generic/delete.png       |  Bin 0 -> 565 bytes
 .../t2/workbench/icons/generic/down-arrow.png   |  Bin 0 -> 331 bytes
 .../taverna/t2/workbench/icons/generic/edit.gif |  Bin 0 -> 579 bytes
 .../t2/workbench/icons/generic/fileimport.png   |  Bin 0 -> 851 bytes
 .../taverna/t2/workbench/icons/generic/find.gif |  Bin 0 -> 346 bytes
 .../workbench/icons/generic/folder-closed.png   |  Bin 0 -> 621 bytes
 .../t2/workbench/icons/generic/folder-open.png  |  Bin 0 -> 626 bytes
 .../t2/workbench/icons/generic/greentick.png    |  Bin 0 -> 331 bytes
 .../t2/workbench/icons/generic/import.gif       |  Bin 0 -> 929 bytes
 .../t2/workbench/icons/generic/inputValue.gif   |  Bin 0 -> 561 bytes
 .../t2/workbench/icons/generic/janus.png        |  Bin 0 -> 395 bytes
 .../taverna/t2/workbench/icons/generic/leaf.gif |  Bin 0 -> 194 bytes
 .../t2/workbench/icons/generic/minus.png        |  Bin 0 -> 214 bytes
 .../t2/workbench/icons/generic/newinput.gif     |  Bin 0 -> 357 bytes
 .../t2/workbench/icons/generic/newlist.gif      |  Bin 0 -> 350 bytes
 .../t2/workbench/icons/generic/normalize.png    |  Bin 0 -> 580 bytes
 .../taverna/t2/workbench/icons/generic/open.gif |  Bin 0 -> 216 bytes
 .../t2/workbench/icons/generic/openmenu.gif     |  Bin 0 -> 251 bytes
 .../t2/workbench/icons/generic/openurl.gif      |  Bin 0 -> 362 bytes
 .../t2/workbench/icons/generic/opmIcon.png      |  Bin 0 -> 376 bytes
 .../t2/workbench/icons/generic/paste.png        |  Bin 0 -> 490 bytes
 .../t2/workbench/icons/generic/pause.png        |  Bin 0 -> 385 bytes
 .../taverna/t2/workbench/icons/generic/play.png |  Bin 0 -> 341 bytes
 .../taverna/t2/workbench/icons/generic/plus.png |  Bin 0 -> 345 bytes
 .../t2/workbench/icons/generic/rbreak.gif       |  Bin 0 -> 1161 bytes
 .../taverna/t2/workbench/icons/generic/redo.png |  Bin 0 -> 513 bytes
 .../t2/workbench/icons/generic/refresh.gif      |  Bin 0 -> 336 bytes
 .../t2/workbench/icons/generic/rename.png       |  Bin 0 -> 212 bytes
 .../icons/generic/results-perspective.png       |  Bin 0 -> 542 bytes
 .../taverna/t2/workbench/icons/generic/run.gif  |  Bin 0 -> 318 bytes
 .../taverna/t2/workbench/icons/generic/save.gif |  Bin 0 -> 639 bytes
 .../taverna/t2/workbench/icons/generic/save.png |  Bin 0 -> 631 bytes
 .../t2/workbench/icons/generic/saveAll.png      |  Bin 0 -> 624 bytes
 .../t2/workbench/icons/generic/saveAs.png       |  Bin 0 -> 529 bytes
 .../t2/workbench/icons/generic/savemenu.gif     |  Bin 0 -> 661 bytes
 .../t2/workbench/icons/generic/savepng.gif      |  Bin 0 -> 988 bytes
 .../t2/workbench/icons/generic/search.png       |  Bin 0 -> 1023 bytes
 .../taverna/t2/workbench/icons/generic/stop.gif |  Bin 0 -> 268 bytes
 .../taverna-wheel-message-dialog-error-icon.png |  Bin 0 -> 11424 bytes
 .../taverna-wheel-message-dialog-info-icon.png  |  Bin 0 -> 11383 bytes
 ...verna-wheel-message-dialog-question-icon.png |  Bin 0 -> 11312 bytes
 ...averna-wheel-message-dialog-warning-icon.png |  Bin 0 -> 11254 bytes
 .../icons/generic/taverna_cogs_32x32.png        |  Bin 0 -> 3094 bytes
 .../icons/generic/taverna_cogs_64x64.png        |  Bin 0 -> 11014 bytes
 .../taverna/t2/workbench/icons/generic/tick.png |  Bin 0 -> 537 bytes
 .../taverna/t2/workbench/icons/generic/undo.png |  Bin 0 -> 548 bytes
 .../t2/workbench/icons/generic/uninstall.png    |  Bin 0 -> 639 bytes
 .../t2/workbench/icons/generic/untick.png       |  Bin 0 -> 664 bytes
 .../t2/workbench/icons/generic/up-arrow.png     |  Bin 0 -> 300 bytes
 .../t2/workbench/icons/generic/update.png       |  Bin 0 -> 689 bytes
 .../icons/generic/updateRecommended.png         |  Bin 0 -> 731 bytes
 .../t2/workbench/icons/generic/urlimport.png    |  Bin 0 -> 969 bytes
 .../taverna/t2/workbench/icons/generic/web.gif  |  Bin 0 -> 362 bytes
 .../workbench/icons/generic/workflowResults.png |  Bin 0 -> 736 bytes
 .../t2/workbench/icons/generic/working.gif      |  Bin 0 -> 673 bytes
 .../workbench/icons/generic/workingStopped.png  |  Bin 0 -> 258 bytes
 .../t2/workbench/icons/generic/xml_node.gif     |  Bin 0 -> 82 bytes
 .../taverna/t2/workbench/icons/generic/zoom.gif |  Bin 0 -> 545 bytes
 .../t2/workbench/icons/generic/zoomin.gif       |   78 +
 .../t2/workbench/icons/generic/zoomin.png       |  Bin 0 -> 659 bytes
 .../t2/workbench/icons/generic/zoomout.png      |  Bin 0 -> 607 bytes
 .../t2/workbench/icons/graph/allport.png        |  Bin 0 -> 652 bytes
 .../t2/workbench/icons/graph/allport.svg        |  124 +
 .../taverna/t2/workbench/icons/graph/blob.png   |  Bin 0 -> 468 bytes
 .../taverna/t2/workbench/icons/graph/blob.svg   |   72 +
 .../t2/workbench/icons/graph/expandnested.png   |  Bin 0 -> 718 bytes
 .../t2/workbench/icons/graph/expandnested.svg   |  130 +
 .../t2/workbench/icons/graph/horizontal.png     |  Bin 0 -> 380 bytes
 .../t2/workbench/icons/graph/horizontal.svg     |  152 +
 .../taverna/t2/workbench/icons/graph/noport.png |  Bin 0 -> 482 bytes
 .../taverna/t2/workbench/icons/graph/noport.svg |   82 +
 .../t2/workbench/icons/graph/saveAsDOT.png      |  Bin 0 -> 1105 bytes
 .../t2/workbench/icons/graph/saveAsPNG.png      |  Bin 0 -> 1337 bytes
 .../t2/workbench/icons/graph/saveAsPS.png       |  Bin 0 -> 1019 bytes
 .../t2/workbench/icons/graph/saveAsPS2.png      |  Bin 0 -> 1018 bytes
 .../t2/workbench/icons/graph/saveAsSVG.png      |  Bin 0 -> 1416 bytes
 .../taverna/t2/workbench/icons/graph/trash.png  |  Bin 0 -> 482 bytes
 .../t2/workbench/icons/graph/vertical.png       |  Bin 0 -> 383 bytes
 .../t2/workbench/icons/graph/vertical.svg       |  150 +
 taverna-workbench-configuration-api/pom.xml     |   37 -
 .../configuration/colour/ColourManager.java     |   41 -
 .../configuration/mimetype/MimeTypeManager.java |   42 -
 .../workbench/WorkbenchConfiguration.java       |   44 -
 .../workbench/ui/T2ConfigurationFrame.java      |   30 -
 taverna-workbench-configuration-impl/pom.xml    |   76 -
 .../WorkbenchConfigurationImpl.java             |  210 --
 .../WorkbenchConfigurationPanel.java            |  266 --
 .../WorkbenchConfigurationUIFactory.java        |   52 -
 .../configuration/colour/ColourManagerImpl.java |  178 -
 .../mimetype/MimeTypeManagerImpl.java           |   82 -
 .../ui/T2ConfigurationFrameImpl.java            |  205 -
 .../ui/WorkbenchConfigurationMenu.java          |   61 -
 .../ui/WorkbenchPreferencesSection.java         |   36 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    2 -
 ...rkbench.configuration.ConfigurationUIFactory |    1 -
 .../spring/configuration-impl-context-osgi.xml  |   27 -
 .../spring/configuration-impl-context.xml       |   34 -
 .../configuration/ConfigurationManagerTest.java |  109 -
 .../configuration/colour/ColourManagerTest.java |   90 -
 taverna-workbench-contextual-views-api/pom.xml  |   99 -
 .../activity/ActivityConfigurationAction.java   |  167 -
 .../activity/ActivityContextualView.java        |   69 -
 .../HTMLBasedActivityContextualView.java        |   81 -
 .../contextualviews/AddLayerFactorySPI.java     |   43 -
 .../views/contextualviews/ContextualView.java   |  109 -
 .../activity/ActivityConfigurationDialog.java   |  474 ---
 .../activity/ActivityConfigurationPanel.java    |  214 --
 .../activity/ActivityPortConfiguration.java     |   84 -
 .../activity/ContextualViewFactory.java         |   63 -
 .../activity/ContextualViewFactoryRegistry.java |   43 -
 .../activity/DependencyConfigurationPanel.java  |  293 --
 .../activity/ListConfigurationComponent.java    |  119 -
 .../contextualviews/activity/ListLayout.java    |   92 -
 .../MultiPageActivityConfigurationPanel.java    |   65 -
 .../activity/ScriptConfigurationComponent.java  |  150 -
 .../activity/ValidatingTextField.java           |   53 -
 .../activity/ValidatingTextGroup.java           |  119 -
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    2 -
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    3 -
 .../contextual-views-api-context-osgi.xml       |    9 -
 .../spring/contextual-views-api-context.xml     |    6 -
 taverna-workbench-contextual-views-impl/pom.xml |   54 -
 .../impl/ContextualViewFactoryRegistryImpl.java |   75 -
 .../annotated/AnnotatedContextualView.java      |  263 --
 .../AnnotatedContextualViewFactory.java         |   43 -
 .../condition/ConditionContextualView.java      |   74 -
 .../ConditionContextualViewFactory.java         |   51 -
 .../dataflow/DataflowContextualView.java        |  108 -
 .../dataflow/DataflowContextualViewFactory.java |   41 -
 .../DataflowInputPortContextualView.java        |   96 -
 .../DataflowInputPortContextualViewFactory.java |   54 -
 .../DataflowOutputPortContextualView.java       |  106 -
 ...DataflowOutputPortContextualViewFactory.java |   55 -
 .../datalink/DatalinkContextualView.java        |  106 -
 .../datalink/DatalinkContextualViewFactory.java |   55 -
 .../impl/ContextualViewComponent.java           |  389 --
 .../impl/ContextualViewComponentFactory.java    |   64 -
 .../inputport/InputPortContextualView.java      |   76 -
 .../InputPortContextualViewFactory.java         |   48 -
 .../merge/MergeConfigurationAction.java         |   79 -
 .../merge/MergeConfigurationView.java           |  233 --
 .../merge/MergeContextualView.java              |  150 -
 .../merge/MergeContextualViewFactory.java       |   66 -
 .../outputport/OutputPortContextualView.java    |   76 -
 .../OutputPortContextualViewFactory.java        |   48 -
 ...ntextualviews.activity.ContextualViewFactory |    9 -
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 -
 .../contextual-views-impl-context-osgi.xml      |   31 -
 .../spring/contextual-views-impl-context.xml    |   43 -
 .../annotatedcontextualview.properties          |    4 -
 taverna-workbench-contextual-views/pom.xml      |   57 -
 .../ProcessorActivitiesContextualView.java      |  154 -
 ...rocessorActivitiesContextualViewFactory.java |   75 -
 ...rPredictedBehaviorContextualViewFactory.java |  177 -
 ...ntextualviews.activity.ContextualViewFactory |    4 -
 .../spring/contextual-views-context-osgi.xml    |   15 -
 .../spring/contextual-views-context.xml         |   12 -
 taverna-workbench-credential-manager-ui/pom.xml |   69 -
 .../ui/credentialmanager/CMStrings.java         |    7 -
 .../ChangeMasterPasswordDialog.java             |  234 --
 .../ConfirmTrustedCertificateDialog.java        |  520 ---
 .../ConfirmTrustedCertificateUI.java            |   71 -
 .../credentialmanager/CredentialManagerUI.java  | 1512 --------
 .../CredentialManagerUILauncher.java            |   96 -
 .../ui/credentialmanager/CryptoFileFilter.java  |   73 -
 .../GetMasterPasswordDialog.java                |  169 -
 .../ui/credentialmanager/GetPasswordDialog.java |  168 -
 .../credentialmanager/KeyPairsTableModel.java   |  213 --
 .../NewEditPasswordEntryDialog.java             |  397 --
 .../NewKeyPairEntryDialog.java                  |  304 --
 .../credentialmanager/NewTrustCertsDialog.java  |  248 --
 .../credentialmanager/PasswordsTableModel.java  |  227 --
 .../SetMasterPasswordDialog.java                |  189 -
 .../ui/credentialmanager/TableCellRenderer.java |  113 -
 .../credentialmanager/TableHeaderRenderer.java  |  100 -
 .../TrustedCertsTableModel.java                 |  216 --
 .../ViewCertDetailsDialog.java                  |  509 ---
 .../ViewUsernamePasswordEntryDialog.java        |  199 -
 .../WarnUserAboutJCEPolicyDialog.java           |  223 --
 .../action/CredentialManagerAction.java         |   68 -
 .../menu/CredentialManagerMenu.java             |   72 -
 .../AskUserJavaTruststorePasswordProvider.java  |   46 -
 .../password/AskUserMasterPasswordProvider.java |   55 -
 ...kUserServiceUsernameAndPasswordProvider.java |   23 -
 .../AskUserTrustConfirmationProvider.java       |   35 -
 .../password/GetPasswordDialog.java             |  228 --
 .../password/SimpleMasterPasswordProvider.java  |   54 -
 .../password/UIMasterPasswordProvider.java      |  126 -
 .../password/UIUsernamePasswordProvider.java    |   92 -
 .../startup/InitialiseSSLStartupHook.java       |   64 -
 .../SetCredManAuthenticatorStartupHook.java     |   24 -
 .../toolbar/CredentialManagerToolbarAction.java |   44 -
 .../CredentialManagerToolbarSection.java        |   38 -
 ...rity.credentialmanager.CredentialProviderSPI |    3 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    3 -
 .../net.sf.taverna.t2.workbench.StartupSPI      |    2 -
 .../credential-manager-ui-context-osgi.xml      |   37 -
 .../spring/credential-manager-ui-context.xml    |   44 -
 .../src/main/resources/images/cred_manager.png  |  Bin 10131 -> 0 bytes
 .../main/resources/images/cred_manager16x16.png |  Bin 3622 -> 0 bytes
 .../images/cred_manager_transparent.png         |  Bin 8376 -> 0 bytes
 .../resources/images/table/entry_heading.png    |  Bin 205 -> 0 bytes
 .../main/resources/images/table/key_entry.png   |  Bin 260 -> 0 bytes
 .../resources/images/table/keypair_entry.png    |  Bin 306 -> 0 bytes
 .../resources/images/table/trustcert_entry.png  |  Bin 551 -> 0 bytes
 .../pom.xml                                     |   84 -
 .../DataManagementConfigurationPanel.java       |  304 --
 .../DataManagementConfigurationUIFactory.java   |   64 -
 ...rkbench.configuration.ConfigurationUIFactory |    1 -
 .../data-management-config-ui-context-osgi.xml  |   14 -
 .../data-management-config-ui-context.xml       |   11 -
 taverna-workbench-design-ui/pom.xml             |   61 -
 .../design/actions/AddConditionAction.java      |   82 -
 .../design/actions/AddDataflowInputAction.java  |   96 -
 .../design/actions/AddDataflowOutputAction.java |   90 -
 .../design/actions/DataflowEditAction.java      |   57 -
 .../actions/EditDataflowInputPortAction.java    |  115 -
 .../actions/EditDataflowOutputPortAction.java   |   95 -
 .../design/actions/RemoveConditionAction.java   |   69 -
 .../actions/RemoveDataflowInputPortAction.java  |   85 -
 .../actions/RemoveDataflowOutputPortAction.java |   85 -
 .../design/actions/RemoveDatalinkAction.java    |   68 -
 .../design/actions/RemoveProcessorAction.java   |  136 -
 .../design/actions/RenameProcessorAction.java   |   97 -
 .../design/ui/DataflowInputPortPanel.java       |  203 -
 .../design/ui/DataflowOutputPortPanel.java      |  105 -
 .../t2/workbench/design/ui/ProcessorPanel.java  |  101 -
 taverna-workbench-edits-api/pom.xml             |   47 -
 .../t2/workbench/edits/CompoundEdit.java        |  118 -
 .../net/sf/taverna/t2/workbench/edits/Edit.java |   66 -
 .../t2/workbench/edits/EditException.java       |   42 -
 .../taverna/t2/workbench/edits/EditManager.java |  222 --
 .../t2/workbench/edits/package-info.java        |   48 -
 .../taverna/t2/workflow/edits/AbstractEdit.java |  119 -
 .../t2/workflow/edits/AddActivityEdit.java      |   55 -
 .../edits/AddActivityInputPortMappingEdit.java  |   59 -
 .../edits/AddActivityOutputPortMappingEdit.java |   59 -
 .../taverna/t2/workflow/edits/AddChildEdit.java |   52 -
 .../t2/workflow/edits/AddDataLinkEdit.java      |   90 -
 .../edits/AddIterationStrategyEdit.java         |   49 -
 .../AddIterationStrategyInputPortEdit.java      |   50 -
 .../t2/workflow/edits/AddProcessorEdit.java     |   45 -
 .../edits/AddProcessorInputPortEdit.java        |   45 -
 .../edits/AddProcessorOutputPortEdit.java       |   46 -
 .../edits/AddWorkflowInputPortEdit.java         |  110 -
 .../edits/AddWorkflowOutputPortEdit.java        |  111 -
 .../t2/workflow/edits/ChangeDepthEdit.java      |  104 -
 .../workflow/edits/ChangeGranularDepthEdit.java |   49 -
 .../t2/workflow/edits/ChangeJsonEdit.java       |   50 -
 .../edits/ClearIterationStrategyStackEdit.java  |   50 -
 .../t2/workflow/edits/ConfigureEdit.java        |   55 -
 .../t2/workflow/edits/RemoveActivityEdit.java   |   55 -
 .../RemoveActivityInputPortMappingEdit.java     |   51 -
 .../RemoveActivityOutputPortMappingEdit.java    |   51 -
 .../t2/workflow/edits/RemoveChildEdit.java      |   48 -
 .../t2/workflow/edits/RemoveDataLinkEdit.java   |  111 -
 .../edits/RemoveProcessorInputPortEdit.java     |   31 -
 .../edits/RemoveProcessorOutputPortEdit.java    |   31 -
 .../edits/RemoveWorkflowInputPortEdit.java      |  107 -
 .../edits/RemoveWorkflowOutputPortEdit.java     |  105 -
 .../taverna/t2/workflow/edits/RenameEdit.java   |  136 -
 .../edits/ReorderMergePositionsEdit.java        |   57 -
 .../edits/SetIterationStrategyStackEdit.java    |   51 -
 .../UpdateDataflowInternalIdentifierEdit.java   |   48 -
 taverna-workbench-edits-impl/pom.xml            |   65 -
 .../workbench/edits/impl/EditManagerImpl.java   |  285 --
 .../edits/impl/menu/AbstractUndoAction.java     |  166 -
 .../edits/impl/menu/RedoMenuAction.java         |   86 -
 .../edits/impl/menu/UndoMenuAction.java         |   86 -
 .../edits/impl/menu/UndoMenuSection.java        |   42 -
 .../edits/impl/toolbar/EditToolbarSection.java  |   36 -
 .../edits/impl/toolbar/RedoToolbarAction.java   |   46 -
 .../edits/impl/toolbar/UndoToolbarAction.java   |   46 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    6 -
 ...et.sf.taverna.t2.workbench.edits.EditManager |    1 -
 .../META-INF/spring/edits-impl-context-osgi.xml |   20 -
 .../META-INF/spring/edits-impl-context.xml      |   33 -
 .../edits/impl/TestEditManagerImpl.java         |  258 --
 taverna-workbench-file-api/pom.xml              |   49 -
 .../AbstractDataflowPersistenceHandler.java     |   69 -
 .../taverna/t2/workbench/file/DataflowInfo.java |  108 -
 .../file/DataflowPersistenceHandler.java        |  152 -
 .../taverna/t2/workbench/file/FileManager.java  |  573 ---
 .../sf/taverna/t2/workbench/file/FileType.java  |   67 -
 .../file/events/AbstractDataflowEvent.java      |   45 -
 .../file/events/ClosedDataflowEvent.java        |   34 -
 .../file/events/ClosingDataflowEvent.java       |   45 -
 .../workbench/file/events/FileManagerEvent.java |   39 -
 .../file/events/OpenedDataflowEvent.java        |   34 -
 .../file/events/SavedDataflowEvent.java         |   34 -
 .../file/events/SetCurrentDataflowEvent.java    |   35 -
 .../file/exceptions/FileException.java          |   44 -
 .../file/exceptions/OpenException.java          |   40 -
 .../file/exceptions/OverwriteException.java     |   36 -
 .../file/exceptions/SaveException.java          |   40 -
 .../file/exceptions/UnsavedException.java       |   38 -
 taverna-workbench-file-impl/pom.xml             |  120 -
 .../DataflowFromDataflowPersistenceHandler.java |   49 -
 .../DataflowPersistenceHandlerRegistry.java     |  238 --
 .../workbench/file/impl/FileDataflowInfo.java   |   67 -
 .../t2/workbench/file/impl/FileManagerImpl.java |  601 ---
 .../workbench/file/impl/FileTypeFileFilter.java |   55 -
 .../workbench/file/impl/MultipleFileTypes.java  |   58 -
 .../file/impl/OpenDataflowInProgressDialog.java |   88 -
 .../workbench/file/impl/OpenDataflowInfo.java   |   93 -
 .../file/impl/OpenDataflowRunnable.java         |   71 -
 .../file/impl/OpenDataflowSwingWorker.java      |   67 -
 .../workbench/file/impl/T2DataflowOpener.java   |  144 -
 .../t2/workbench/file/impl/T2FileFilter.java    |   40 -
 .../t2/workbench/file/impl/T2FlowFileType.java  |   42 -
 .../file/impl/WorkflowBundleFileFilter.java     |   40 -
 .../file/impl/WorkflowBundleFileType.java       |   42 -
 .../file/impl/WorkflowBundleOpener.java         |  143 -
 .../file/impl/WorkflowBundleSaver.java          |  145 -
 .../impl/actions/CloseAllWorkflowsAction.java   |   85 -
 .../file/impl/actions/CloseWorkflowAction.java  |  107 -
 .../file/impl/actions/NewWorkflowAction.java    |   58 -
 .../impl/actions/OpenNestedWorkflowAction.java  |   76 -
 .../file/impl/actions/OpenWorkflowAction.java   |  395 --
 .../impl/actions/OpenWorkflowFromURLAction.java |  139 -
 .../file/impl/actions/PasswordInput.java        |  221 --
 .../impl/actions/SaveAllWorkflowsAction.java    |  104 -
 .../file/impl/actions/SaveWorkflowAction.java   |  175 -
 .../file/impl/actions/SaveWorkflowAsAction.java |  219 --
 .../impl/hooks/CloseWorkflowsOnShutdown.java    |   56 -
 .../file/impl/menu/FileCloseAllMenuAction.java  |   51 -
 .../file/impl/menu/FileCloseMenuAction.java     |   50 -
 .../file/impl/menu/FileNewMenuAction.java       |   47 -
 .../impl/menu/FileOpenFromURLMenuAction.java    |   48 -
 .../file/impl/menu/FileOpenMenuAction.java      |   47 -
 .../file/impl/menu/FileOpenMenuSection.java     |   36 -
 .../impl/menu/FileOpenRecentMenuAction.java     |  418 --
 .../file/impl/menu/FileSaveAllMenuAction.java   |   47 -
 .../file/impl/menu/FileSaveAsMenuAction.java    |   43 -
 .../file/impl/menu/FileSaveMenuAction.java      |   46 -
 .../file/impl/menu/FileSaveMenuSection.java     |   36 -
 .../workbench/file/impl/menu/WorkflowsMenu.java |  163 -
 .../file/impl/toolbar/CloseToolbarAction.java   |   55 -
 .../impl/toolbar/FileToolbarMenuSection.java    |   36 -
 .../file/impl/toolbar/NewToolbarAction.java     |   47 -
 .../file/impl/toolbar/OpenToolbarAction.java    |   47 -
 .../OpenWorkflowFromURLToolbarAction.java       |   47 -
 .../file/impl/toolbar/SaveToolbarAction.java    |   50 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |   20 -
 .../net.sf.taverna.t2.workbench.ShutdownSPI     |    1 -
 ...t2.workbench.file.DataflowPersistenceHandler |    2 -
 ...net.sf.taverna.t2.workbench.file.FileManager |    1 -
 .../META-INF/spring/file-impl-context-osgi.xml  |  100 -
 .../META-INF/spring/file-impl-context.xml       |  123 -
 .../t2/workbench/file/impl/FileManagerTest.java |  385 --
 .../workbench/file/impl/dummy-workflow.t2flow   |  157 -
 taverna-workbench-graph-model/pom.xml           |  142 -
 .../models/graph/DefaultGraphEventManager.java  |  271 --
 .../t2/workbench/models/graph/DotWriter.java    |  253 --
 .../t2/workbench/models/graph/Graph.java        |  165 -
 .../models/graph/GraphColorManager.java         |   75 -
 .../workbench/models/graph/GraphController.java | 1276 -------
 .../t2/workbench/models/graph/GraphEdge.java    |  137 -
 .../t2/workbench/models/graph/GraphElement.java |  430 ---
 .../models/graph/GraphEventManager.java         |   47 -
 .../t2/workbench/models/graph/GraphNode.java    |  153 -
 .../models/graph/GraphShapeElement.java         |  119 -
 .../workbench/models/graph/dot/GraphLayout.java |  326 --
 .../t2/workbench/models/graph/svg/SVGGraph.java |  439 ---
 .../models/graph/svg/SVGGraphController.java    |  555 ---
 .../models/graph/svg/SVGGraphEdge.java          |  311 --
 .../graph/svg/SVGGraphElementDelegate.java      |  178 -
 .../models/graph/svg/SVGGraphNode.java          |  611 ---
 .../models/graph/svg/SVGGraphSettings.java      |   28 -
 .../models/graph/svg/SVGMonitorShape.java       |   40 -
 .../t2/workbench/models/graph/svg/SVGShape.java |   29 -
 .../t2/workbench/models/graph/svg/SVGUtil.java  |  477 ---
 .../graph/svg/event/SVGEventListener.java       |   56 -
 .../svg/event/SVGMouseClickEventListener.java   |   45 -
 .../svg/event/SVGMouseDownEventListener.java    |   45 -
 .../svg/event/SVGMouseMovedEventListener.java   |   46 -
 .../svg/event/SVGMouseOutEventListener.java     |   46 -
 .../svg/event/SVGMouseOverEventListener.java    |   46 -
 .../svg/event/SVGMouseUpEventListener.java      |   46 -
 .../src/main/jjtree/NamedNode.java              |   65 -
 .../src/main/jjtree/dotparser.jjt               |  289 --
 .../models/graph/GraphControllerTest.java       |   81 -
 .../workbench/models/graph/GraphEdgeTest.java   |  129 -
 .../models/graph/GraphElementTest.java          |  148 -
 .../workbench/models/graph/GraphNodeTest.java   |  179 -
 .../t2/workbench/models/graph/GraphTest.java    |  138 -
 .../src/test/resources/nested_iteration.xml     |  121 -
 taverna-workbench-graph-view/pom.xml            |  102 -
 .../views/graph/AutoScrollInteractor.java       |  181 -
 .../views/graph/GraphViewComponent.java         |  548 ---
 .../views/graph/GraphViewComponentFactory.java  |  100 -
 .../views/graph/actions/AddWFInputAction.java   |   69 -
 .../views/graph/actions/AddWFOutputAction.java  |   69 -
 .../actions/DeleteGraphComponentAction.java     |  180 -
 .../RenameWFInputOutputProcessorAction.java     |  184 -
 .../graph/config/GraphViewConfiguration.java    |   80 -
 .../config/GraphViewConfigurationPanel.java     |  360 --
 .../config/GraphViewConfigurationUIFactory.java |   55 -
 .../views/graph/menu/AddWFInputMenuAction.java  |   60 -
 .../views/graph/menu/AddWFOutputMenuAction.java |   60 -
 .../menu/DeleteGraphComponentMenuAction.java    |   61 -
 .../workbench/views/graph/menu/DiagramMenu.java |   44 -
 .../graph/menu/DiagramSaveMenuSection.java      |   39 -
 .../graph/menu/DiagramZoomMenuSection.java      |   40 -
 .../views/graph/menu/GraphCopyMenuSection.java  |   39 -
 .../graph/menu/GraphDeleteMenuSection.java      |   39 -
 .../graph/menu/GraphDetailsMenuSection.java     |   40 -
 .../views/graph/menu/GraphEditMenuSection.java  |   39 -
 .../views/graph/menu/GraphMenuSection.java      |   39 -
 .../workbench/views/graph/menu/InsertMenu.java  |   30 -
 .../RenameWFInputOutputProcessorMenuAction.java |   62 -
 .../views/graph/menu/ResetDiagramAction.java    |   64 -
 .../graph/menu/ResetDiagramMenuAction.java      |   50 -
 .../views/graph/menu/SaveGraphImageSubMenu.java |  315 --
 .../views/graph/menu/ZoomInAction.java          |   72 -
 .../views/graph/menu/ZoomInMenuAction.java      |   50 -
 .../views/graph/menu/ZoomOutAction.java         |   65 -
 .../views/graph/menu/ZoomOutMenuAction.java     |   50 -
 .../graph/toolbar/AddWFInputToolbarAction.java  |   60 -
 .../graph/toolbar/AddWFOutputToolbarAction.java |   60 -
 .../DeleteGraphComponentToolbarAction.java      |   60 -
 .../toolbar/GraphDeleteToolbarSection.java      |   39 -
 .../graph/toolbar/GraphEditToolbarSection.java  |   39 -
 ...nameWFInputOutputProcessorToolbarAction.java |   63 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |   29 -
 ...rkbench.configuration.ConfigurationUIFactory |    1 -
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 -
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 -
 .../META-INF/spring/graph-view-context-osgi.xml |   46 -
 .../META-INF/spring/graph-view-context.xml      |  107 -
 .../src/test/resources/nested_iteration.t2flow  |  111 -
 .../src/test/resources/nested_iteration.xml     |  120 -
 taverna-workbench-helper-api/pom.xml            |   73 -
 .../t2/workbench/helper/HelpCollator.java       |  307 --
 .../t2/workbench/helper/HelpEnabledDialog.java  |  101 -
 .../sf/taverna/t2/workbench/helper/Helper.java  |  187 -
 .../helper/NonBlockedHelpEnabledDialog.java     |   40 -
 taverna-workbench-httpproxy-config/pom.xml      |   46 -
 .../config/HttpProxyConfigurationPanel.java     |  582 ---
 .../config/HttpProxyConfigurationUIFactory.java |   56 -
 ...rkbench.configuration.ConfigurationUIFactory |    1 -
 .../spring/httpproxy-config-context-osgi.xml    |   13 -
 .../spring/httpproxy-config-context.xml         |   10 -
 taverna-workbench-impl/pom.xml                  |  131 +
 .../ui/impl/DataflowEditsListener.java          |   93 +
 .../t2/workbench/ui/impl/LoggerStream.java      |  136 +
 .../ui/impl/SetConsoleLoggerStartup.java        |   62 +
 .../ui/impl/StoreWindowStateOnShutdown.java     |   58 +
 .../workbench/ui/impl/UserRegistrationData.java |  105 +
 .../workbench/ui/impl/UserRegistrationForm.java |  995 +++++
 .../workbench/ui/impl/UserRegistrationHook.java |  163 +
 .../t2/workbench/ui/impl/WorkbenchImpl.java     |  538 +++
 .../ui/impl/WorkbenchPerspectives.java          |  229 ++
 .../t2/workbench/ui/impl/menu/ExitAction.java   |   66 +
 .../net.sf.taverna.raven.launcher.Launchable    |    1 +
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |   18 +
 .../net.sf.taverna.t2.workbench.ShutdownSPI     |    1 +
 .../net.sf.taverna.t2.workbench.StartupSPI      |    2 +
 .../spring/workbench-impl-context-osgi.xml      |   38 +
 .../META-INF/spring/workbench-impl-context.xml  |   43 +
 .../src/main/resources/Map.jhm                  |   28 +
 .../resources/example-registration-form.xml     |   12 +
 .../src/main/resources/registration-form.xsd    |   27 +
 .../src/main/resources/registration.php         |  137 +
 .../src/main/resources/sample.hs                |   64 +
 .../workbench/ui/impl/UserRegistrationTest.java |  190 +
 .../src/test/resources/log4j.properties         |   10 +
 taverna-workbench-iteration-strategy-ui/pom.xml |   79 -
 .../IterationStrategyIcons.java                 |   48 -
 .../IterationStrategyConfigurationDialog.java   |  148 -
 .../IterationStrategyContextualView.java        |  231 --
 .../IterationStrategyContextualViewFactory.java |   54 -
 .../editor/IterationStrategyCellRenderer.java   |   74 -
 .../editor/IterationStrategyEditor.java         |  247 --
 .../editor/IterationStrategyEditorControl.java  |  439 ---
 .../editor/IterationStrategyTree.java           |  106 -
 .../IterationStrategyConfigureMenuAction.java   |   65 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    1 -
 ...ntextualviews.activity.ContextualViewFactory |    1 -
 .../iteration-strategy-ui-context-osgi.xml      |   14 -
 .../spring/iteration-strategy-ui-context.xml    |   11 -
 .../icons/crossproducticon.png                  |  Bin 831 -> 0 bytes
 .../iterationstrategy/icons/dotproducticon.png  |  Bin 663 -> 0 bytes
 .../iterationstrategy/icons/leafnodeicon.png    |  Bin 507 -> 0 bytes
 .../editor/RunIterationStrategyEditor.java      |   56 -
 taverna-workbench-loop-ui/pom.xml               |   85 -
 .../t2/workbench/loop/ActivityGenerator.java    |  195 -
 .../t2/workbench/loop/AddLoopFactory.java       |  125 -
 .../t2/workbench/loop/LoopAddMenuAction.java    |   73 -
 .../workbench/loop/LoopConfigurationPanel.java  |  588 ---
 .../t2/workbench/loop/LoopConfigureAction.java  |  262 --
 .../workbench/loop/LoopConfigureMenuAction.java |   97 -
 .../t2/workbench/loop/LoopContextualView.java   |  172 -
 .../loop/LoopContextualViewFactory.java         |   53 -
 .../t2/workbench/loop/LoopRemoveMenuAction.java |   92 -
 .../workbench/loop/comparisons/Comparison.java  |   47 -
 .../t2/workbench/loop/comparisons/EqualTo.java  |   40 -
 .../loop/comparisons/IsGreaterThan.java         |   40 -
 .../workbench/loop/comparisons/IsLessThan.java  |   40 -
 .../t2/workbench/loop/comparisons/Matches.java  |   40 -
 .../workbench/loop/comparisons/NotEqualTo.java  |   40 -
 .../workbench/loop/comparisons/NotMatches.java  |   40 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    3 -
 ....ui.views.contextualviews.AddLayerFactorySPI |    1 -
 ...ntextualviews.activity.ContextualViewFactory |    1 -
 .../META-INF/spring/loop-ui-context-osgi.xml    |   21 -
 .../META-INF/spring/loop-ui-context.xml         |   32 -
 .../t2/workbench/loop/ShowContextualView.java   |  121 -
 .../src/test/resources/log4j.properties         |   10 -
 taverna-workbench-menu-api/pom.xml              |   43 -
 .../ui/menu/AbstractContextualMenuAction.java   |   64 -
 .../net/sf/taverna/t2/ui/menu/AbstractMenu.java |  123 -
 .../taverna/t2/ui/menu/AbstractMenuAction.java  |  135 -
 .../taverna/t2/ui/menu/AbstractMenuCustom.java  |  144 -
 .../sf/taverna/t2/ui/menu/AbstractMenuItem.java |  144 -
 .../t2/ui/menu/AbstractMenuOptionGroup.java     |   79 -
 .../taverna/t2/ui/menu/AbstractMenuSection.java |  113 -
 .../taverna/t2/ui/menu/AbstractMenuToggle.java  |  132 -
 .../sf/taverna/t2/ui/menu/AbstractToolBar.java  |   74 -
 .../t2/ui/menu/ContextualMenuComponent.java     |   35 -
 .../taverna/t2/ui/menu/ContextualSelection.java |   48 -
 .../t2/ui/menu/DefaultContextualMenu.java       |   53 -
 .../sf/taverna/t2/ui/menu/DefaultMenuBar.java   |   50 -
 .../sf/taverna/t2/ui/menu/DefaultToolBar.java   |   51 -
 .../sf/taverna/t2/ui/menu/DesignOnlyAction.java |   32 -
 .../t2/ui/menu/DesignOrResultsAction.java       |   32 -
 .../sf/taverna/t2/ui/menu/MenuComponent.java    |  277 --
 .../net/sf/taverna/t2/ui/menu/MenuManager.java  |  339 --
 .../net/sf/taverna/t2/ui/menu/package-info.java |  141 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    4 -
 .../META-INF/spring/menu-api-context-osgi.xml   |   13 -
 .../META-INF/spring/menu-api-context.xml        |   10 -
 taverna-workbench-menu-impl/pom.xml             |   82 -
 .../t2/ui/menu/impl/MenuManagerImpl.java        |  880 -----
 .../t2/workbench/ui/impl/menu/AdvancedMenu.java |   44 -
 .../t2/workbench/ui/impl/menu/EditMenu.java     |   43 -
 .../ui/impl/menu/FeedbackMenuAction.java        |   75 -
 .../t2/workbench/ui/impl/menu/FileMenu.java     |   48 -
 .../t2/workbench/ui/impl/menu/HelpMenu.java     |   44 -
 .../ui/impl/menu/OnlineHelpMenuAction.java      |   68 -
 .../ui/impl/menu/ShowLogsAndDataMenuAction.java |   89 -
 .../ui/impl/menu/ViewShowMenuSection.java       |   40 -
 .../net.sf.taverna.t2.ui.menu.MenuManager       |    1 -
 .../META-INF/spring/menu-impl-context-osgi.xml  |   26 -
 .../META-INF/spring/menu-impl-context.xml       |   25 -
 taverna-workbench-menu-items/pom.xml            |   88 -
 .../AbstractConnectPortMenuActions.java         |  268 --
 .../activityport/ActivityInputPortSection.java  |   67 -
 .../activityport/ActivityOutputPortSection.java |   67 -
 .../AddInputPortDefaultValueAction.java         |  150 -
 .../ConnectInputPortMenuActions.java            |   41 -
 .../ConnectOutputPortMenuActions.java           |   41 -
 .../items/activityport/ConnectPortsAction.java  |   68 -
 .../CreateAndConnectDataflowPortAction.java     |  226 --
 .../SetConstantInputPortValueMenuAction.java    |   73 -
 .../SetDefaultInputPortValueAction.java         |  171 -
 .../annotated/AnnotatedConfigureMenuAction.java |   77 -
 .../ConfigureRunningContextualMenuSection.java  |   50 -
 .../items/contextualviews/ConfigureSection.java |   61 -
 .../menu/items/contextualviews/EditSection.java |   73 -
 .../items/contextualviews/InsertSection.java    |   63 -
 .../items/contextualviews/PasteMenuAction.java  |   74 -
 .../ShowConfigureMenuAction.java                |  165 -
 .../ShowDetailsContextualMenuAction.java        |   65 -
 .../contextualviews/ShowDetailsMenuAction.java  |   81 -
 .../ShowReportsContextualMenuAction.java        |  103 -
 .../items/controllink/ConditionSection.java     |   71 -
 .../controllink/RemoveConditionMenuAction.java  |   67 -
 .../t2/ui/menu/items/datalink/LinkSection.java  |   73 -
 .../items/datalink/RemoveLinkMenuAction.java    |   66 -
 .../ConnectDataflowInputPortMenuActions.java    |   42 -
 .../ConnectDataflowOutputPortMenuActions.java   |   42 -
 .../ports/EditDataflowInputPortMenuAction.java  |   68 -
 .../ports/EditDataflowOutputPortMenuAction.java |   68 -
 .../RemoveDataflowInputPortMenuAction.java      |   68 -
 .../RemoveDataflowOutputPortMenuAction.java     |   68 -
 .../items/ports/WorkflowInputPortSection.java   |   73 -
 .../items/ports/WorkflowOutputPortSection.java  |   73 -
 .../items/processor/ConditionMenuActions.java   |  118 -
 .../menu/items/processor/ProcessorSection.java  |   58 -
 .../processor/RemoveProcessorMenuAction.java    |   67 -
 .../processor/RenameProcessorMenuAction.java    |   68 -
 .../items/workflow/CreateInputMenuAction.java   |   62 -
 .../items/workflow/CreateOutputMenuAction.java  |   62 -
 .../WorkflowServiceTemplatesSection.java        |   76 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |   46 -
 .../META-INF/spring/menu-items-context-osgi.xml |   57 -
 .../META-INF/spring/menu-items-context.xml      |  124 -
 taverna-workbench-monitor-view/pom.xml          |   82 -
 .../views/monitor/MonitorViewComponent.java     |  168 -
 .../views/monitor/graph/GraphMonitor.java       |  140 -
 .../views/monitor/graph/GraphMonitorNode.java   |  115 -
 .../monitor/graph/MonitorGraphComponent.java    |  378 --
 .../progressreport/TableMonitorComponent.java   |  100 -
 .../WorkflowRunProgressTreeCellRenderer.java    |   96 -
 .../WorkflowRunProgressTreeTable.java           |  112 -
 .../WorkflowRunProgressTreeTableModel.java      |  279 --
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 -
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 -
 .../spring/monitor-view-context-osgi.xml        |    9 -
 .../META-INF/spring/monitor-view-context.xml    |    6 -
 taverna-workbench-parallelize-ui/pom.xml        |   57 -
 .../ParallelizeConfigurationPanel.java          |   99 -
 .../parallelize/ParallelizeConfigureAction.java |  185 -
 .../ParallelizeConfigureMenuAction.java         |   77 -
 .../parallelize/ParallelizeContextualView.java  |  130 -
 .../ParallelizeContextualViewFactory.java       |   56 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    1 -
 ...ntextualviews.activity.ContextualViewFactory |    1 -
 .../spring/parallelize-ui-context-osgi.xml      |   16 -
 .../META-INF/spring/parallelize-ui-context.xml  |   16 -
 .../LocalTestLauncher.bat                       |   14 -
 .../lib/core-renderer.jar                       |  Bin 1079323 -> 0 bytes
 .../log4j.properties                            |    4 -
 .../pom.xml                                     |  178 -
 .../move_scomp_results_into_project.bat         |   29 -
 .../scomp_compile_from_web.bat                  |    9 -
 .../scomp_compile_from_web_to_jar.bat           |    9 -
 .../doc/BioCatalogue Plugin Documentation.odt   |  Bin 77721 -> 0 bytes
 .../src/main/help/Index-TOC-Map-Additions.txt   |   20 -
 .../main/help/biocatalogue-plugin-features.html |  113 -
 .../main/help/biocatalogue-plugin-feedback.html |   28 -
 .../src/main/help/biocatalogue-plugin.html      |   54 -
 .../model/BioCataloguePluginConstants.java      |   77 -
 .../model/HTTPMethodInterpreter.java            |   46 -
 .../model/LoadingExpandedResource.java          |   41 -
 .../biocatalogue/model/LoadingResource.java     |   39 -
 .../net/sf/taverna/biocatalogue/model/Pair.java |   30 -
 .../sf/taverna/biocatalogue/model/Resource.java |  506 ---
 .../biocatalogue/model/ResourceManager.java     |  326 --
 .../model/ResourcePreviewContent.java           |   38 -
 .../model/SoapOperationIdentity.java            |   77 -
 .../model/SoapOperationPortIdentity.java        |   26 -
 .../model/SoapProcessorIdentity.java            |   27 -
 .../biocatalogue/model/SoapServiceIdentity.java |   45 -
 .../net/sf/taverna/biocatalogue/model/Tag.java  |  218 --
 .../net/sf/taverna/biocatalogue/model/Util.java |  793 ----
 .../BeanForPOSTToFilteredIndex.java             |   12 -
 .../model/connectivity/BeansForJSONLiteAPI.java |   84 -
 .../connectivity/BioCatalogueAPIRequest.java    |   47 -
 .../model/connectivity/BioCatalogueClient.java  |  785 ----
 .../model/connectivity/ServerResponse.java      |   40 -
 .../connectivity/ServerResponseStream.java      |   30 -
 .../biocatalogue/model/search/SearchEngine.java |  221 --
 .../model/search/SearchInstance.java            |  490 ---
 .../model/search/SearchInstanceTracker.java     |   57 -
 .../model/search/SearchOptions.java             |   70 -
 .../model/search/SearchResults.java             |  214 --
 .../model/search/ServiceFilteringSettings.java  |  184 -
 .../biocatalogue/test/AnnotationBean.java       |   52 -
 .../biocatalogue/test/DrawDefaultIconTest.java  |   38 -
 .../sf/taverna/biocatalogue/test/GSONTest.java  |   19 -
 .../test/GSONTest_exportingJSON.java            |   30 -
 .../test/GSONTest_forSoapOperationsIndex.java   |   27 -
 .../biocatalogue/test/JWaitDialogTest.java      |   36 -
 .../biocatalogue/test/LinkedListEqualsTest.java |   25 -
 .../biocatalogue/test/TestAPICaller.java        |  241 --
 .../test/TestDoubleUsageOfSameSwingElement.java |   32 -
 .../biocatalogue/test/TestUtilURLHandling.java  |   87 -
 .../biocatalogue/test/TestXHTMLRenderer.java    |   42 -
 .../biocatalogue/test/WrappableJLabelTest.java  |   35 -
 .../taverna/biocatalogue/test/XStreamTest.java  |   32 -
 .../ui/BioCatalogueExplorationTab.java          |  131 -
 .../ui/HasDefaultFocusCapability.java           |   15 -
 .../biocatalogue/ui/JClickableLabel.java        |  172 -
 .../sf/taverna/biocatalogue/ui/JWaitDialog.java |  234 --
 .../biocatalogue/ui/SearchOptionsPanel.java     |  167 -
 .../ui/filtertree/FilterTreeNode.java           |   91 -
 .../ui/filtertree/FilterTreePane.java           |  348 --
 .../biocatalogue/ui/filtertree/JFilterTree.java |   69 -
 ...xpandableOnDemandLoadedListCellRenderer.java |  220 --
 .../RESTMethodListCellRenderer.java             |  248 --
 .../SOAPOperationListCellRenderer.java          |  257 --
 .../SearchResultsListingPanel.java              |  870 -----
 .../search_results/SearchResultsMainPanel.java  |  498 ---
 .../search_results/SearchResultsRenderer.java   |   47 -
 .../search_results/ServiceListCellRenderer.java |  291 --
 .../ui/tristatetree/JTriStateTree.java          |  631 ----
 .../tristatetree/Swing - Tristate CheckBox.7z   |  Bin 108419 -> 0 bytes
 .../biocatalogue/ui/tristatetree/Test.java      |   67 -
 .../ui/tristatetree/TriStateCheckBox.java       |  172 -
 .../TriStateCheckBoxTreeCellRenderer.java       |   62 -
 .../TriStateTreeCheckingListener.java           |   12 -
 .../ui/tristatetree/TriStateTreeNode.java       |  246 --
 .../biocatalogue/BioCataloguePerspective.java   |   70 -
 .../biocatalogue/MainComponent.java             |  285 --
 .../biocatalogue/MainComponentFactory.java      |   46 -
 .../biocatalogue/MainComponentShutdownHook.java |   49 -
 .../biocatalogue/TestJFrameForLocalLaunch.java  |   68 -
 .../biocatalogue/integration/Integration.java   |  518 ---
 .../config/BioCataloguePluginConfiguration.java |   68 -
 .../BioCataloguePluginConfigurationPanel.java   |  448 ---
 ...ioCataloguePluginConfigurationUIFactory.java |   27 -
 ...aloguePluginInputPortContextViewFactory.java |   45 -
 ...loguePluginOutputPortContextViewFactory.java |   45 -
 ...aloguePluginProcessorContextViewFactory.java |   43 -
 .../ProcessorInputPortView.java                 |   52 -
 .../ProcessorOutputPortView.java                |   52 -
 .../contextual_views/ProcessorView.java         |  229 --
 .../BioCatalogueWSDLActivityHealthCheck.java    |   40 -
 ...ueWSDLActivityHealthCheckVisitExplainer.java |  111 -
 .../BioCatalogueWSDLActivityHealthChecker.java  |  199 -
 .../health_check/ServiceHealthChecker.java      |  280 --
 .../ServiceMonitoringStatusInterpreter.java     |   77 -
 .../BioCatalogueContextualMenuSection.java      |   62 -
 .../integration/menus/MenuActionInputPort.java  |   43 -
 .../menus/MenuActionProcessorHealthCheck.java   |   51 -
 .../BioCatalogueRESTServiceProvider.java        |  117 -
 .../BioCatalogueServiceProvider.java            |  274 --
 ...ioCatalogueWSDLOperationServiceProvider.java |  215 --
 .../RESTFromBioCatalogueServiceDescription.java |  194 -
 ...ationFromBioCatalogueServiceDescription.java |  116 -
 ...rvicedescriptions.ServiceDescriptionProvider |    2 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    2 -
 .../net.sf.taverna.t2.workbench.ShutdownSPI     |    1 -
 ...rkbench.configuration.ConfigurationUIFactory |    1 -
 ...t2.workbench.report.explainer.VisitExplainer |    1 -
 ...ntextualviews.activity.ContextualViewFactory |    3 -
 ...taverna.t2.workbench.ui.zaria.PerspectiveSPI |    1 -
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 -
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 -
 ...averna.t2.workflowmodel.health.HealthChecker |    1 -
 .../ajax-loader-grey-bert2-still.png            |  Bin 1271 -> 0 bytes
 .../biocatalogue/ajax-loader-grey-bert2.gif     |  Bin 3951 -> 0 bytes
 .../ajax-loader-orange-bert2-still.png          |  Bin 1214 -> 0 bytes
 .../biocatalogue/ajax-loader-orange-bert2.gif   |  Bin 3951 -> 0 bytes
 .../biocatalogue/ajax-loader-still.gif          |  Bin 889 -> 0 bytes
 .../perspectives/biocatalogue/ajax-loader.gif   |  Bin 1456 -> 0 bytes
 .../biocatalogue/biocatalogue-perspective.xml   |   19 -
 .../biocatalogue/biocatalogue_styles.css        | 2673 -------------
 .../biocatalogue/blue-sphere-50.png             |  Bin 1936 -> 0 bytes
 .../biocatalogue/cross-sphere-35.png            |  Bin 2748 -> 0 bytes
 .../biocatalogue/cross-sphere-50.png            |  Bin 7161 -> 0 bytes
 .../biocatalogue/famfamfam_silk/accept.png      |  Bin 781 -> 0 bytes
 .../biocatalogue/famfamfam_silk/add - tick.pdn  |  Bin 5367 -> 0 bytes
 .../biocatalogue/famfamfam_silk/add - tick.png  |  Bin 784 -> 0 bytes
 .../biocatalogue/famfamfam_silk/add.png         |  Bin 733 -> 0 bytes
 .../famfamfam_silk/application_form_add.png     |  Bin 592 -> 0 bytes
 .../arrow_join (flipped vertically).png         |  Bin 669 -> 0 bytes
 .../biocatalogue/famfamfam_silk/arrow_left.png  |  Bin 557 -> 0 bytes
 .../famfamfam_silk/arrow_refresh.png            |  Bin 685 -> 0 bytes
 .../biocatalogue/famfamfam_silk/arrow_right.png |  Bin 596 -> 0 bytes
 .../famfamfam_silk/chart_organisation.png       |  Bin 444 -> 0 bytes
 .../biocatalogue/famfamfam_silk/cross.png       |  Bin 655 -> 0 bytes
 .../biocatalogue/famfamfam_silk/disk.png        |  Bin 620 -> 0 bytes
 .../biocatalogue/famfamfam_silk/error.png       |  Bin 666 -> 0 bytes
 .../biocatalogue/famfamfam_silk/exclamation.png |  Bin 701 -> 0 bytes
 .../external_link_listing_small.png             |  Bin 456 -> 0 bytes
 .../famfamfam_silk/folder_explore.png           |  Bin 679 -> 0 bytes
 .../biocatalogue/famfamfam_silk/grey_circle.png |  Bin 586 -> 0 bytes
 .../biocatalogue/famfamfam_silk/help.png        |  Bin 786 -> 0 bytes
 .../biocatalogue/famfamfam_silk/information.png |  Bin 778 -> 0 bytes
 .../biocatalogue/famfamfam_silk/lightbulb.png   |  Bin 782 -> 0 bytes
 .../biocatalogue/famfamfam_silk/lock.png        |  Bin 749 -> 0 bytes
 .../biocatalogue/famfamfam_silk/lock_open.png   |  Bin 727 -> 0 bytes
 .../biocatalogue/famfamfam_silk/magnifier.png   |  Bin 615 -> 0 bytes
 .../famfamfam_silk/multiple_star.png            |  Bin 676 -> 0 bytes
 .../famfamfam_silk/page_white_code.png          |  Bin 603 -> 0 bytes
 .../biocatalogue/famfamfam_silk/plugin.png      |  Bin 591 -> 0 bytes
 .../famfamfam_silk/remote_resource.png          |  Bin 957 -> 0 bytes
 .../biocatalogue/famfamfam_silk/server.png      |  Bin 530 -> 0 bytes
 .../biocatalogue/famfamfam_silk/star.png        |  Bin 670 -> 0 bytes
 .../biocatalogue/famfamfam_silk/style.png       |  Bin 813 -> 0 bytes
 .../biocatalogue/famfamfam_silk/sum.png         |  Bin 289 -> 0 bytes
 .../biocatalogue/famfamfam_silk/tag_blue.png    |  Bin 586 -> 0 bytes
 .../text_linespacing (collapse).png             |  Bin 371 -> 0 bytes
 .../famfamfam_silk/text_linespacing.png         |  Bin 363 -> 0 bytes
 .../famfamfam_silk/text_list_numbers.png        |  Bin 357 -> 0 bytes
 .../biocatalogue/famfamfam_silk/tick.png        |  Bin 537 -> 0 bytes
 .../biocatalogue/famfamfam_silk/user.png        |  Bin 741 -> 0 bytes
 .../t2/ui/perspectives/biocatalogue/favicon.png |  Bin 1336 -> 0 bytes
 .../ui/perspectives/biocatalogue/folds/fold.png |  Bin 331 -> 0 bytes
 .../biocatalogue/folds/fold_16x16.png           |  Bin 361 -> 0 bytes
 .../perspectives/biocatalogue/folds/unfold.png  |  Bin 334 -> 0 bytes
 .../biocatalogue/folds/unfold_16x16.png         |  Bin 354 -> 0 bytes
 .../biocatalogue/info-sphere-35.png             |  Bin 2492 -> 0 bytes
 .../biocatalogue/info-sphere-50.png             |  Bin 2439 -> 0 bytes
 .../biocatalogue/open_in_BioCatalogue.pdn       |  Bin 5128 -> 0 bytes
 .../biocatalogue/open_in_BioCatalogue.png       |  Bin 663 -> 0 bytes
 .../biocatalogue/pling-sphere-35.png            |  Bin 2275 -> 0 bytes
 .../biocatalogue/pling-sphere-50.png            |  Bin 1724 -> 0 bytes
 .../biocatalogue/query-sphere-35.png            |  Bin 2473 -> 0 bytes
 .../biocatalogue/query-sphere-50.png            |  Bin 1854 -> 0 bytes
 .../service_icons/service_type_multitype.png    |  Bin 1133 -> 0 bytes
 .../service_icons/service_type_rest.png         |  Bin 1137 -> 0 bytes
 .../service_icons/service_type_soap.png         |  Bin 1131 -> 0 bytes
 .../service_icons/service_type_unknown.png      |  Bin 1076 -> 0 bytes
 .../soap_rest_multitype_unknown.pdn             |  Bin 6693 -> 0 bytes
 .../t2/ui/perspectives/biocatalogue/styles.css  |  290 --
 .../t2/ui/perspectives/biocatalogue/test.html   |   78 -
 .../biocatalogue/tick-sphere-35.png             |  Bin 2479 -> 0 bytes
 .../biocatalogue/tick-sphere-50.png             |  Bin 1909 -> 0 bytes
 .../t2/ui/perspectives/biocatalogue/trash.png   |  Bin 460 -> 0 bytes
 .../tristate_checkbox_checked.png               |  Bin 1050 -> 0 bytes
 .../tristate_checkbox_partial.png               |  Bin 1042 -> 0 bytes
 .../tristate_checkbox_partial_green.png         |  Bin 993 -> 0 bytes
 .../tristate_checkbox_unchecked.png             |  Bin 1033 -> 0 bytes
 .../ui/perspectives/biocatalogue/unchecked.png  |  Bin 3636 -> 0 bytes
 .../src/main/xsd/dc.xsd                         |  119 -
 .../src/main/xsd/dcterms.xsd                    |  137 -
 .../src/main/xsd/schema-v1.xsd                  | 3557 ------------------
 .../src/main/xsd/xlink.xsd                      |   83 -
 taverna-workbench-perspective-design/pom.xml    |   98 -
 .../perspectives/design/DesignPerspective.java  |  118 -
 .../design/DesignPerspectiveComponent.java      |  115 -
 .../design/WorkflowBundleSelectorComponent.java |  113 -
 .../design/WorkflowSelectorComponent.java       |  167 -
 .../t2/ui/perspectives/design/WorkflowTab.java  |  133 -
 ...taverna.t2.workbench.ui.zaria.PerspectiveSPI |    1 -
 .../spring/perspective-design-context-osgi.xml  |   21 -
 .../spring/perspective-design-context.xml       |   18 -
 .../pom.xml                                     |  100 -
 .../myexperiment/AddCommentDialog.java          |  330 --
 .../myexperiment/AddRemoveFavouriteDialog.java  |  277 --
 .../myexperiment/ExampleWorkflowsPanel.java     |  153 -
 .../HistoryBrowserTabContentPanel.java          |  541 ---
 .../myexperiment/JClickableLabel.java           |  127 -
 .../myexperiment/MainComponent.java             |  645 ----
 .../myexperiment/MainComponentFactory.java      |   60 -
 .../myexperiment/MainComponentShutdownHook.java |   84 -
 .../myexperiment/MyExperimentPerspective.java   |  190 -
 .../myexperiment/MyStuffContributionsPanel.java |  370 --
 .../myexperiment/MyStuffSidebarPanel.java       |  359 --
 .../myexperiment/MyStuffTabContentPanel.java    |  342 --
 .../myexperiment/PluginPreferencesDialog.java   |  372 --
 .../myexperiment/PluginStatusBar.java           |  195 -
 .../myexperiment/ResourceListPanel.java         |  182 -
 .../myexperiment/ResourcePreviewBrowser.java    |  711 ----
 .../myexperiment/ResourcePreviewContent.java    |   78 -
 .../myexperiment/ResourcePreviewFactory.java    | 1359 -------
 .../myexperiment/SearchOptionsPanel.java        |  317 --
 .../myexperiment/SearchResultsPanel.java        |  201 -
 .../myexperiment/SearchTabContentPanel.java     |  450 ---
 .../myexperiment/StyledHTMLEditorKit.java       |   19 -
 .../myexperiment/TagBrowserTabContentPanel.java |  226 --
 .../myexperiment/TagCloudPanel.java             |  342 --
 .../myexperiment/TestJFrameForLocalLaunch.java  |   48 -
 .../myexperiment/UploadWorkflowDialog.java      |  849 -----
 .../myexperiment/model/Base64$InputStream.class |  Bin 2083 -> 0 bytes
 .../model/Base64$OutputStream.class             |  Bin 2307 -> 0 bytes
 .../myexperiment/model/Base64.class             |  Bin 15458 -> 0 bytes
 .../perspectives/myexperiment/model/Base64.java | 1813 ---------
 .../myexperiment/model/Comment.java             |  141 -
 .../perspectives/myexperiment/model/File.java   |  237 --
 .../perspectives/myexperiment/model/Group.java  |  222 --
 .../myexperiment/model/License.java             |   62 -
 .../myexperiment/model/MyExperimentClient.class |  Bin 3174 -> 0 bytes
 .../myexperiment/model/MyExperimentClient.java  | 1218 ------
 .../perspectives/myexperiment/model/Pack.java   |  232 --
 .../myexperiment/model/PackItem.java            |  186 -
 .../myexperiment/model/Resource.java            |  683 ----
 .../myexperiment/model/SearchEngine.java        |  321 --
 .../myexperiment/model/ServerResponse.java      |   59 -
 .../ui/perspectives/myexperiment/model/Tag.java |  125 -
 .../myexperiment/model/TagCloud.java            |   47 -
 .../perspectives/myexperiment/model/User.java   |  297 --
 .../perspectives/myexperiment/model/Util.java   |  624 ---
 .../myexperiment/model/Workflow.java            |  420 ---
 .../config/MyExperimentConfiguration.java       |   68 -
 .../config/MyExperimentConfigurationPanel.java  |  305 --
 .../MyExperimentConfigurationUIFactory.java     |   62 -
 .../TestJFrameForPreferencesLocalLaunch.java    |   46 -
 .../net.sf.taverna.t2.workbench.ShutdownSPI     |    1 -
 ...rkbench.configuration.ConfigurationUIFactory |    1 -
 ...taverna.t2.workbench.ui.zaria.PerspectiveSPI |    1 -
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 -
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 -
 .../perspective-myexperiment-context-osgi.xml   |   20 -
 .../spring/perspective-myexperiment-context.xml |   20 -
 .../myexperiment/ajax-loader-still.gif          |  Bin 889 -> 0 bytes
 .../perspectives/myexperiment/ajax-loader.gif   |  Bin 1456 -> 0 bytes
 .../ui/perspectives/myexperiment/arrow_left.png |  Bin 557 -> 0 bytes
 .../perspectives/myexperiment/arrow_refresh.png |  Bin 685 -> 0 bytes
 .../perspectives/myexperiment/arrow_right.png   |  Bin 596 -> 0 bytes
 .../perspectives/myexperiment/comment_add.png   |  Bin 530 -> 0 bytes
 .../myexperiment/comment_delete.png             |  Bin 548 -> 0 bytes
 .../t2/ui/perspectives/myexperiment/cross.png   |  Bin 655 -> 0 bytes
 .../t2/ui/perspectives/myexperiment/denied.png  |  Bin 701 -> 0 bytes
 .../myexperiment/dummy-workflow.t2flow          |  157 -
 .../external_link_listing_small.png             |  Bin 456 -> 0 bytes
 .../perspectives/myexperiment/favourite_add.png |  Bin 701 -> 0 bytes
 .../myexperiment/favourite_delete.png           |  Bin 722 -> 0 bytes
 .../t2/ui/perspectives/myexperiment/file.png    |  Bin 562 -> 0 bytes
 .../t2/ui/perspectives/myexperiment/group.png   |  Bin 753 -> 0 bytes
 .../t2/ui/perspectives/myexperiment/login.png   |  Bin 693 -> 0 bytes
 .../t2/ui/perspectives/myexperiment/logout.png  |  Bin 688 -> 0 bytes
 .../ui/perspectives/myexperiment/myexp_icon.png |  Bin 1611 -> 0 bytes
 .../myexperiment/myexp_icon16x16.png            |  Bin 1057 -> 0 bytes
 .../myexperiment/myexperiment-perspective.xml   |   17 -
 .../myexperiment/open_in_myExperiment.png       |  Bin 621 -> 0 bytes
 .../t2/ui/perspectives/myexperiment/pack.png    |  Bin 565 -> 0 bytes
 .../myexperiment/remote_resource.png            |  Bin 957 -> 0 bytes
 .../t2/ui/perspectives/myexperiment/star.png    |  Bin 670 -> 0 bytes
 .../t2/ui/perspectives/myexperiment/styles.css  |  380 --
 .../ui/perspectives/myexperiment/tag_blue.png   |  Bin 586 -> 0 bytes
 .../t2/ui/perspectives/myexperiment/tick.png    |  Bin 537 -> 0 bytes
 .../myexperiment/transparent_icon.png           |  Bin 1172 -> 0 bytes
 .../t2/ui/perspectives/myexperiment/user.png    |  Bin 741 -> 0 bytes
 .../ui/perspectives/myexperiment/workflow.png   |  Bin 975 -> 0 bytes
 taverna-workbench-perspective-results/pom.xml   |   80 -
 .../results/ResultsPerspective.java             |  158 -
 .../results/ResultsPerspectiveComponent.java    |  221 --
 .../t2/ui/perspectives/results/RunMonitor.java  |  171 -
 .../results/RunSelectorComponent.java           |   72 -
 .../t2/ui/perspectives/results/RunTab.java      |  133 -
 .../t2/ui/perspectives/results/WorkflowRun.java |   54 -
 .../results/WorkflowRunListCellRenderer.java    |   59 -
 .../results/WorkflowRunListModel.java           |   66 -
 ...taverna.t2.workbench.ui.zaria.PerspectiveSPI |    1 -
 .../spring/perspective-results-context-osgi.xml |   27 -
 .../spring/perspective-results-context.xml      |   19 -
 taverna-workbench-plugin-manager/pom.xml        |   65 -
 .../plugin/impl/AvailablePluginPanel.java       |   72 -
 .../plugin/impl/InstalledPluginPanel.java       |   66 -
 .../plugin/impl/PluginManagerPanel.java         |  210 --
 .../plugin/impl/PluginManagerView.java          |   71 -
 .../t2/workbench/plugin/impl/PluginPanel.java   |  166 -
 .../plugin/impl/UpdatePluginPanel.java          |   72 -
 .../plugin/impl/menu/PluginMenuAction.java      |   56 -
 .../spring/plugin-manager-context-osgi.xml      |   20 -
 .../META-INF/spring/plugin-manager-context.xml  |   15 -
 taverna-workbench-plugins-gui/pom.xml           |   62 -
 .../raven/plugins/ui/AddPluginSiteFrame.java    |  273 --
 .../plugins/ui/CheckForNoticeStartupHook.java   |  143 -
 .../raven/plugins/ui/CheckForUpdatesDialog.java |  122 -
 .../plugins/ui/CheckForUpdatesStartupHook.java  |   94 -
 .../plugins/ui/PluginListCellRenderer.java      |  214 --
 .../raven/plugins/ui/PluginListModel.java       |  108 -
 .../raven/plugins/ui/PluginManagerFrame.java    |  516 ---
 .../plugins/ui/PluginRepositoryListener.java    |  148 -
 .../raven/plugins/ui/PluginSiteFrame.java       |  542 ---
 .../raven/plugins/ui/UpdatesAvailableIcon.java  |  205 -
 .../profile/ui/ProfileVersionCellRenderer.java  |  177 -
 .../profile/ui/ProfileVersionListFrame.java     |  260 --
 .../profile/ui/ProfileVersionListModel.java     |  101 -
 .../net.sf.taverna.t2.workbench.StartupSPI      |    2 -
 .../spring/plugins-gui-context-osgi.xml         |   11 -
 .../META-INF/spring/plugins-gui-context.xml     |    8 -
 .../net/sf/taverna/raven/plugins/ui/update.png  |  Bin 689 -> 0 bytes
 .../raven/plugins/ui/updateRecommended.png      |  Bin 731 -> 0 bytes
 taverna-workbench-reference-ui/pom.xml          |   81 -
 .../t2/reference/ui/CheckWorkflowStatus.java    |   97 -
 .../ui/CopyWorkflowInProgressDialog.java        |   91 -
 .../reference/ui/CopyWorkflowSwingWorker.java   |   51 -
 .../t2/reference/ui/InvalidDataflowReport.java  |   95 -
 .../t2/reference/ui/RegistrationPanel.java      |  808 ----
 .../sf/taverna/t2/reference/ui/UrlPanel.java    |  100 -
 .../t2/reference/ui/WorkflowLaunchWindow.java   |  626 ---
 .../net/sf/taverna/t2/reference/ui/package.html |    4 -
 .../ui/referenceactions/LoadInputsFromXML.java  |  120 -
 .../ui/referenceactions/ReferenceActionSPI.java |   38 -
 .../ui/referenceactions/SaveInputsAsXML.java    |  207 -
 .../reference/ui/tree/PreRegistrationTree.java  |  217 --
 .../tree/PreRegistrationTreeCellRenderer.java   |  115 -
 .../ui/tree/PreRegistrationTreeDnDHandler.java  |  268 --
 .../ui/tree/PreRegistrationTreeModel.java       |  310 --
 .../taverna/t2/reference/ui/tree/package.html   |    6 -
 ...rence.ui.referenceactions.ReferenceActionSPI |    2 -
 .../spring/reference-ui-context-osgi.xml        |   12 -
 .../META-INF/spring/reference-ui-context.xml    |    9 -
 .../src/main/resources/icons/addtext_co.gif     |  Bin 335 -> 0 bytes
 .../main/resources/icons/complete_status.gif    |  Bin 76 -> 0 bytes
 .../src/main/resources/icons/deadlock_view.gif  |  Bin 239 -> 0 bytes
 .../src/main/resources/icons/delete_obj.gif     |  Bin 351 -> 0 bytes
 .../src/main/resources/icons/det_pane_hide.gif  |  Bin 370 -> 0 bytes
 .../src/main/resources/icons/error_tsk.gif      |  Bin 353 -> 0 bytes
 .../main/resources/icons/errorwarning_tab.gif   |  Bin 577 -> 0 bytes
 .../resources/icons/genericregister_obj.gif     |  Bin 115 -> 0 bytes
 .../src/main/resources/icons/information.gif    |  Bin 267 -> 0 bytes
 .../main/resources/icons/invalid_build_tool.gif |  Bin 339 -> 0 bytes
 .../src/main/resources/icons/newfolder_wiz.gif  |  Bin 349 -> 0 bytes
 .../src/main/resources/icons/repo_rep.gif       |  Bin 588 -> 0 bytes
 .../src/main/resources/icons/start_task.gif     |  Bin 318 -> 0 bytes
 .../src/main/resources/icons/topic.gif          |  Bin 354 -> 0 bytes
 .../src/main/resources/icons/web.gif            |  Bin 362 -> 0 bytes
 .../src/main/resources/icons/wordassist_co.gif  |  Bin 152 -> 0 bytes
 .../src/main/resources/icons/write_obj.gif      |  Bin 210 -> 0 bytes
 taverna-workbench-renderers-api/pom.xml         |   42 -
 .../net/sf/taverna/t2/renderers/Renderer.java   |   33 -
 .../taverna/t2/renderers/RendererException.java |   46 -
 .../taverna/t2/renderers/RendererRegistry.java  |   48 -
 .../sf/taverna/t2/renderers/RendererUtils.java  |   89 -
 taverna-workbench-renderers-exts/pom.xml        |   77 -
 .../t2/renderers/HTMLBrowserRenderer.java       |   95 -
 .../sf/taverna/t2/renderers/JMolRenderer.java   |  178 -
 .../sf/taverna/t2/renderers/PDFRenderer.java    |  124 -
 .../sf/taverna/t2/renderers/SVGRenderer.java    |  154 -
 .../taverna/t2/renderers/SeqVistaRenderer.java  |  167 -
 .../net.sf.taverna.t2.renderers.Renderer        |    5 -
 .../spring/renderers-exts-context-osgi.xml      |   15 -
 .../META-INF/spring/renderers-exts-context.xml  |   12 -
 .../taverna/t2/renderers/TestRendererSPI.java   |  153 -
 taverna-workbench-renderers-impl/pom.xml        |   80 -
 .../t2/renderers/impl/AbstractRenderer.java     |  124 -
 .../renderers/impl/AdvancedImageRenderer.java   |   94 -
 .../t2/renderers/impl/ExtensionFileFilter.java  |   77 -
 .../t2/renderers/impl/HexBinaryRenderer.java    |   77 -
 .../t2/renderers/impl/RendererConstants.java    |   13 -
 .../t2/renderers/impl/RendererRegistryImpl.java |   54 -
 .../taverna/t2/renderers/impl/TextRenderer.java |  137 -
 .../t2/renderers/impl/TextRtfRenderer.java      |   85 -
 .../t2/renderers/impl/TextXMLRenderer.java      |   86 -
 .../sf/taverna/t2/renderers/impl/XMLTree.java   |  329 --
 .../net.sf.taverna.t2.renderers.Renderer        |    7 -
 .../spring/renderers-impl-context-osgi.xml      |   19 -
 .../META-INF/spring/renderers-impl-context.xml  |   17 -
 .../taverna/t2/renderers/TestRendererSPI.java   |  154 -
 taverna-workbench-report-api/pom.xml            |   74 -
 .../t2/workbench/report/ProfileReportEvent.java |   21 -
 .../t2/workbench/report/ReportManager.java      |   44 -
 .../t2/workbench/report/ReportManagerEvent.java |   10 -
 .../config/ReportManagerConfiguration.java      |   44 -
 .../services/net.sf.taverna.t2.visit.VisitKind  |    1 -
 ....taverna.t2.visit.fragility.FragilityChecker |    1 -
 .../META-INF/spring/report-api-context-osgi.xml |   13 -
 .../META-INF/spring/report-api-context.xml      |   10 -
 taverna-workbench-report-explainer/pom.xml      |  118 -
 .../report/explainer/BasicExplainer.java        | 1294 -------
 ...t2.workbench.report.explainer.VisitExplainer |    1 -
 .../spring/report-explainer-context-osgi.xml    |   19 -
 .../spring/report-explainer-context.xml         |   15 -
 taverna-workbench-report-impl/pom.xml           |   56 -
 .../impl/ReportManagerConfigurationImpl.java    |   71 -
 .../report/impl/ReportManagerImpl.java          |  564 ---
 .../spring/report-impl-context-osgi.xml         |   23 -
 .../META-INF/spring/report-impl-context.xml     |   21 -
 taverna-workbench-report-view/pom.xml           |  100 -
 .../ui/ReportManagerConfigurationPanel.java     |  363 --
 .../ui/ReportManagerConfigurationUIFactory.java |   55 -
 .../ReportOnObjectContextualMenuAction.java     |  189 -
 .../report/view/ReportOnWorkflowAction.java     |  177 -
 .../report/view/ReportViewComponent.java        |  574 ---
 .../report/view/ReportViewComponentFactory.java |   75 -
 .../report/view/ReportViewConfigureAction.java  |   45 -
 .../report/view/ReportViewTableModel.java       |  284 --
 .../workbench/report/view/StatusRenderer.java   |   46 -
 .../report/view/ValidateInProgressDialog.java   |   92 -
 .../view/ValidateObjectInProgressDialog.java    |   93 -
 .../report/view/ValidateObjectSwingWorker.java  |   71 -
 .../report/view/ValidateSwingWorker.java        |  123 -
 .../workbench/report/view/VisitReportProxy.java |   42 -
 .../report/view/VisitReportProxySet.java        |   36 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    1 -
 ...rkbench.configuration.ConfigurationUIFactory |    1 -
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 -
 .../spring/report-view-context-osgi.xml         |   25 -
 .../META-INF/spring/report-view-context.xml     |   28 -
 taverna-workbench-results-view/pom.xml          |  127 -
 .../views/results/InvocationTreeModel.java      |  103 -
 .../views/results/InvocationTreeNode.java       |  100 -
 .../workbench/views/results/InvocationView.java |  136 -
 .../t2/workbench/views/results/ReportView.java  |  420 ---
 .../views/results/ResultsComponent.java         |  233 --
 .../views/results/SimpleFilteredTreeModel.java  |   68 -
 .../processor/FilteredIterationTreeModel.java   |   91 -
 .../FilteredProcessorValueTreeModel.java        |   70 -
 .../IntermediateValuesInProgressDialog.java     |   89 -
 .../IntermediateValuesSwingWorker.java          |   47 -
 .../results/processor/IterationTreeNode.java    |   99 -
 .../processor/ProcessorEnactmentsTreeModel.java |  189 -
 .../processor/ProcessorEnactmentsTreeNode.java  |   84 -
 .../processor/ProcessorPortResultsViewTab.java  |  229 --
 .../processor/ProcessorResultCellRenderer.java  |   89 -
 .../processor/ProcessorResultTreeNode.java      |  181 -
 .../processor/ProcessorResultsComponent.java    | 1004 -----
 .../processor/ProcessorResultsTreeModel.java    |   73 -
 .../RenderedProcessorResultComponent.java       |  575 ---
 .../saveactions/SaveAllResultsAsExcel.java      |  296 --
 .../saveactions/SaveAllResultsAsXML.java        |   80 -
 .../results/saveactions/SaveAllResultsSPI.java  |  193 -
 .../saveactions/SaveAllResultsToFileSystem.java |   94 -
 .../saveactions/SaveIndividualResult.java       |  186 -
 .../saveactions/SaveIndividualResultSPI.java    |   48 -
 .../results/workflow/DataBundleTreeModel.java   |  136 -
 .../workflow/FilteredDataBundleTreeModel.java   |  147 -
 .../FilteredWorkflowResultTreeModel.java        |  126 -
 .../workflow/PortResultCellRenderer.java        |  100 -
 .../results/workflow/PortResultsViewTab.java    |  283 --
 .../workflow/RenderedResultComponent.java       |  601 ---
 .../workflow/WorkflowResultTreeModel.java       |  197 -
 .../workflow/WorkflowResultTreeNode.java        |  120 -
 .../workflow/WorkflowResultsComponent.java      |  352 --
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 -
 ....views.results.saveactions.SaveAllResultsSPI |    3 -
 ....results.saveactions.SaveIndividualResultSPI |    1 -
 .../spring/results-view-context-osgi.xml        |   14 -
 .../META-INF/spring/results-view-context.xml    |   11 -
 taverna-workbench-retry-ui/pom.xml              |   57 -
 .../retry/RetryConfigurationPanel.java          |  172 -
 .../workbench/retry/RetryConfigureAction.java   |  183 -
 .../retry/RetryConfigureMenuAction.java         |   77 -
 .../t2/workbench/retry/RetryContextualView.java |  165 -
 .../retry/RetryContextualViewFactory.java       |   58 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    1 -
 ...ntextualviews.activity.ContextualViewFactory |    1 -
 .../META-INF/spring/retry-ui-context-osgi.xml   |   16 -
 .../META-INF/spring/retry-ui-context.xml        |   16 -
 taverna-workbench-run-ui/pom.xml                |   71 -
 .../run/actions/OpenWorkflowRunAction.java      |  135 -
 .../run/actions/RunWorkflowAction.java          |  299 --
 .../run/actions/ValidateWorkflowAction.java     |   56 -
 .../WorkflowRunStatusShutdownDialog.java        |  153 -
 .../cleanup/WorkflowRunStatusShutdownHook.java  |  110 -
 .../run/menu/FileOpenRunMenuAction.java         |   63 -
 .../workbench/run/menu/FileRunMenuAction.java   |   82 -
 .../workbench/run/menu/FileRunMenuSection.java  |   36 -
 .../run/menu/FileValidateMenuAction.java        |   70 -
 .../workbench/run/toolbar/RunToolbarAction.java |   82 -
 .../run/toolbar/RunToolbarSection.java          |   36 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |    6 -
 .../net.sf.taverna.t2.workbench.ShutdownSPI     |    5 -
 .../net.sf.taverna.t2.workbench.StartupSPI      |    1 -
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 -
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 -
 .../META-INF/spring/run-ui-context-osgi.xml     |   28 -
 .../META-INF/spring/run-ui-context.xml          |   41 -
 taverna-workbench-selection-api/pom.xml         |   69 -
 .../selection/DataflowSelectionModel.java       |   85 -
 .../workbench/selection/SelectionManager.java   |  113 -
 .../events/DataflowSelectionMessage.java        |   66 -
 .../events/PerspectiveSelectionEvent.java       |   59 -
 .../selection/events/ProfileSelectionEvent.java |   57 -
 .../selection/events/SelectionManagerEvent.java |   34 -
 .../events/WorkflowBundleSelectionEvent.java    |   59 -
 .../events/WorkflowRunSelectionEvent.java       |   51 -
 .../events/WorkflowSelectionEvent.java          |   57 -
 taverna-workbench-selection-impl/pom.xml        |   51 -
 .../impl/DataflowSelectionModelImpl.java        |  116 -
 .../selection/impl/SelectionManagerImpl.java    |  367 --
 .../spring/selection-impl-context-osgi.xml      |   17 -
 .../META-INF/spring/selection-impl-context.xml  |   12 -
 taverna-workbench-update-manager/pom.xml        |   42 -
 .../update/impl/UpdateManagerView.java          |   45 -
 .../update/impl/menu/UpdateMenuAction.java      |   92 -
 .../updatemanager/PluginMenuAction.java         |   52 -
 .../UpdatesAvailableMenuAction.java             |   19 -
 .../updatemanager/UpdatesToolbarSection.java    |   16 -
 .../spring/update-manager-context-osgi.xml      |   13 -
 .../META-INF/spring/update-manager-context.xml  |   12 -
 taverna-workbench-workbench-api/pom.xml         |   30 -
 .../net/sf/taverna/t2/workbench/MainWindow.java |   16 -
 .../taverna/t2/workbench/ModelMapConstants.java |   28 -
 .../sf/taverna/t2/workbench/ShutdownSPI.java    |   72 -
 .../net/sf/taverna/t2/workbench/StartupSPI.java |   65 -
 .../t2/workbench/icons/WorkbenchIcons.java      |  214 --
 .../ui/SwingWorkerCompletionWaiter.java         |   33 -
 .../sf/taverna/t2/workbench/ui/Updatable.java   |   32 -
 .../net/sf/taverna/t2/workbench/ui/Utils.java   |   20 -
 .../sf/taverna/t2/workbench/ui/Workbench.java   |   33 -
 .../t2/workbench/ui/zaria/PerspectiveSPI.java   |   77 -
 .../ui/zaria/UIComponentFactorySPI.java         |   56 -
 .../t2/workbench/ui/zaria/UIComponentSPI.java   |   56 -
 .../t2/workbench/icons/explorer/biomoby.png     |  Bin 1218 -> 0 bytes
 .../t2/workbench/icons/explorer/constraint.gif  |  Bin 144 -> 0 bytes
 .../t2/workbench/icons/explorer/dataflow.png    |  Bin 814 -> 0 bytes
 .../t2/workbench/icons/explorer/datalink.gif    |  Bin 124 -> 0 bytes
 .../t2/workbench/icons/explorer/input.png       |  Bin 396 -> 0 bytes
 .../t2/workbench/icons/explorer/inputport.png   |  Bin 251 -> 0 bytes
 .../t2/workbench/icons/explorer/localworker.png |  Bin 706 -> 0 bytes
 .../t2/workbench/icons/explorer/merge.png       |  Bin 422 -> 0 bytes
 .../t2/workbench/icons/explorer/output.png      |  Bin 425 -> 0 bytes
 .../t2/workbench/icons/explorer/outputport.png  |  Bin 235 -> 0 bytes
 .../t2/workbench/icons/explorer/rserv.png       |  Bin 1235 -> 0 bytes
 .../t2/workbench/icons/explorer/seqhound.png    |  Bin 3603 -> 0 bytes
 .../t2/workbench/icons/explorer/soaplab.png     |  Bin 701 -> 0 bytes
 .../workbench/icons/explorer/stringconstant.png |  Bin 733 -> 0 bytes
 .../t2/workbench/icons/explorer/talisman.png    |  Bin 1214 -> 0 bytes
 .../icons/explorer/unknownprocessor.png         |  Bin 1060 -> 0 bytes
 .../icons/explorer/workflow-explorer-old.png    |  Bin 255 -> 0 bytes
 .../icons/explorer/workflow-explorer.png        |  Bin 267 -> 0 bytes
 .../t2/workbench/icons/explorer/workflow.png    |  Bin 1213 -> 0 bytes
 .../icons/explorer/workflowInputPort.png        |  Bin 718 -> 0 bytes
 .../icons/explorer/workflowOutputPort.png       |  Bin 636 -> 0 bytes
 .../t2/workbench/icons/explorer/wsdl.png        |  Bin 748 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/bin.png  |  Bin 393 -> 0 bytes
 .../t2/workbench/icons/generic/break.gif        |  Bin 1120 -> 0 bytes
 .../t2/workbench/icons/generic/close.gif        |  Bin 351 -> 0 bytes
 .../t2/workbench/icons/generic/closeAll.gif     |  Bin 380 -> 0 bytes
 .../t2/workbench/icons/generic/configure.png    |  Bin 610 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/copy.png |  Bin 389 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/cut.png  |  Bin 710 -> 0 bytes
 .../t2/workbench/icons/generic/database.gif     |  Bin 1032 -> 0 bytes
 .../t2/workbench/icons/generic/delete.png       |  Bin 565 -> 0 bytes
 .../t2/workbench/icons/generic/down-arrow.png   |  Bin 331 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/edit.gif |  Bin 579 -> 0 bytes
 .../t2/workbench/icons/generic/fileimport.png   |  Bin 851 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/find.gif |  Bin 346 -> 0 bytes
 .../workbench/icons/generic/folder-closed.png   |  Bin 621 -> 0 bytes
 .../t2/workbench/icons/generic/folder-open.png  |  Bin 626 -> 0 bytes
 .../t2/workbench/icons/generic/greentick.png    |  Bin 331 -> 0 bytes
 .../t2/workbench/icons/generic/import.gif       |  Bin 929 -> 0 bytes
 .../t2/workbench/icons/generic/inputValue.gif   |  Bin 561 -> 0 bytes
 .../t2/workbench/icons/generic/janus.png        |  Bin 395 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/leaf.gif |  Bin 194 -> 0 bytes
 .../t2/workbench/icons/generic/minus.png        |  Bin 214 -> 0 bytes
 .../t2/workbench/icons/generic/newinput.gif     |  Bin 357 -> 0 bytes
 .../t2/workbench/icons/generic/newlist.gif      |  Bin 350 -> 0 bytes
 .../t2/workbench/icons/generic/normalize.png    |  Bin 580 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/open.gif |  Bin 216 -> 0 bytes
 .../t2/workbench/icons/generic/openmenu.gif     |  Bin 251 -> 0 bytes
 .../t2/workbench/icons/generic/openurl.gif      |  Bin 362 -> 0 bytes
 .../t2/workbench/icons/generic/opmIcon.png      |  Bin 376 -> 0 bytes
 .../t2/workbench/icons/generic/paste.png        |  Bin 490 -> 0 bytes
 .../t2/workbench/icons/generic/pause.png        |  Bin 385 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/play.png |  Bin 341 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/plus.png |  Bin 345 -> 0 bytes
 .../t2/workbench/icons/generic/rbreak.gif       |  Bin 1161 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/redo.png |  Bin 513 -> 0 bytes
 .../t2/workbench/icons/generic/refresh.gif      |  Bin 336 -> 0 bytes
 .../t2/workbench/icons/generic/rename.png       |  Bin 212 -> 0 bytes
 .../icons/generic/results-perspective.png       |  Bin 542 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/run.gif  |  Bin 318 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/save.gif |  Bin 639 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/save.png |  Bin 631 -> 0 bytes
 .../t2/workbench/icons/generic/saveAll.png      |  Bin 624 -> 0 bytes
 .../t2/workbench/icons/generic/saveAs.png       |  Bin 529 -> 0 bytes
 .../t2/workbench/icons/generic/savemenu.gif     |  Bin 661 -> 0 bytes
 .../t2/workbench/icons/generic/savepng.gif      |  Bin 988 -> 0 bytes
 .../t2/workbench/icons/generic/search.png       |  Bin 1023 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/stop.gif |  Bin 268 -> 0 bytes
 .../taverna-wheel-message-dialog-error-icon.png |  Bin 11424 -> 0 bytes
 .../taverna-wheel-message-dialog-info-icon.png  |  Bin 11383 -> 0 bytes
 ...verna-wheel-message-dialog-question-icon.png |  Bin 11312 -> 0 bytes
 ...averna-wheel-message-dialog-warning-icon.png |  Bin 11254 -> 0 bytes
 .../icons/generic/taverna_cogs_32x32.png        |  Bin 3094 -> 0 bytes
 .../icons/generic/taverna_cogs_64x64.png        |  Bin 11014 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/tick.png |  Bin 537 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/undo.png |  Bin 548 -> 0 bytes
 .../t2/workbench/icons/generic/uninstall.png    |  Bin 639 -> 0 bytes
 .../t2/workbench/icons/generic/untick.png       |  Bin 664 -> 0 bytes
 .../t2/workbench/icons/generic/up-arrow.png     |  Bin 300 -> 0 bytes
 .../t2/workbench/icons/generic/update.png       |  Bin 689 -> 0 bytes
 .../icons/generic/updateRecommended.png         |  Bin 731 -> 0 bytes
 .../t2/workbench/icons/generic/urlimport.png    |  Bin 969 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/web.gif  |  Bin 362 -> 0 bytes
 .../workbench/icons/generic/workflowResults.png |  Bin 736 -> 0 bytes
 .../t2/workbench/icons/generic/working.gif      |  Bin 673 -> 0 bytes
 .../workbench/icons/generic/workingStopped.png  |  Bin 258 -> 0 bytes
 .../t2/workbench/icons/generic/xml_node.gif     |  Bin 82 -> 0 bytes
 .../taverna/t2/workbench/icons/generic/zoom.gif |  Bin 545 -> 0 bytes
 .../t2/workbench/icons/generic/zoomin.gif       |   78 -
 .../t2/workbench/icons/generic/zoomin.png       |  Bin 659 -> 0 bytes
 .../t2/workbench/icons/generic/zoomout.png      |  Bin 607 -> 0 bytes
 .../t2/workbench/icons/graph/allport.png        |  Bin 652 -> 0 bytes
 .../t2/workbench/icons/graph/allport.svg        |  124 -
 .../taverna/t2/workbench/icons/graph/blob.png   |  Bin 468 -> 0 bytes
 .../taverna/t2/workbench/icons/graph/blob.svg   |   72 -
 .../t2/workbench/icons/graph/expandnested.png   |  Bin 718 -> 0 bytes
 .../t2/workbench/icons/graph/expandnested.svg   |  130 -
 .../t2/workbench/icons/graph/horizontal.png     |  Bin 380 -> 0 bytes
 .../t2/workbench/icons/graph/horizontal.svg     |  152 -
 .../taverna/t2/workbench/icons/graph/noport.png |  Bin 482 -> 0 bytes
 .../taverna/t2/workbench/icons/graph/noport.svg |   82 -
 .../t2/workbench/icons/graph/saveAsDOT.png      |  Bin 1105 -> 0 bytes
 .../t2/workbench/icons/graph/saveAsPNG.png      |  Bin 1337 -> 0 bytes
 .../t2/workbench/icons/graph/saveAsPS.png       |  Bin 1019 -> 0 bytes
 .../t2/workbench/icons/graph/saveAsPS2.png      |  Bin 1018 -> 0 bytes
 .../t2/workbench/icons/graph/saveAsSVG.png      |  Bin 1416 -> 0 bytes
 .../taverna/t2/workbench/icons/graph/trash.png  |  Bin 482 -> 0 bytes
 .../t2/workbench/icons/graph/vertical.png       |  Bin 383 -> 0 bytes
 .../t2/workbench/icons/graph/vertical.svg       |  150 -
 taverna-workbench-workbench-impl/pom.xml        |  131 -
 .../ui/impl/DataflowEditsListener.java          |   93 -
 .../t2/workbench/ui/impl/LoggerStream.java      |  136 -
 .../ui/impl/SetConsoleLoggerStartup.java        |   62 -
 .../ui/impl/StoreWindowStateOnShutdown.java     |   58 -
 .../workbench/ui/impl/UserRegistrationData.java |  105 -
 .../workbench/ui/impl/UserRegistrationForm.java |  995 -----
 .../workbench/ui/impl/UserRegistrationHook.java |  163 -
 .../t2/workbench/ui/impl/WorkbenchImpl.java     |  538 ---
 .../ui/impl/WorkbenchPerspectives.java          |  229 --
 .../t2/workbench/ui/impl/menu/ExitAction.java   |   66 -
 .../net.sf.taverna.raven.launcher.Launchable    |    1 -
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |   18 -
 .../net.sf.taverna.t2.workbench.ShutdownSPI     |    1 -
 .../net.sf.taverna.t2.workbench.StartupSPI      |    2 -
 .../spring/workbench-impl-context-osgi.xml      |   38 -
 .../META-INF/spring/workbench-impl-context.xml  |   43 -
 .../src/main/resources/Map.jhm                  |   28 -
 .../resources/example-registration-form.xml     |   12 -
 .../src/main/resources/registration-form.xsd    |   27 -
 .../src/main/resources/registration.php         |  137 -
 .../src/main/resources/sample.hs                |   64 -
 .../workbench/ui/impl/UserRegistrationTest.java |  190 -
 .../src/test/resources/log4j.properties         |   10 -
 taverna-workbench-workflow-explorer/pom.xml     |   91 -
 .../ui/workflowexplorer/WorkflowExplorer.java   |  824 ----
 .../WorkflowExplorerFactory.java                |   95 -
 .../WorkflowExplorerTreeCellRenderer.java       |  216 --
 .../WorkflowExplorerTreeModel.java              |  404 --
 .../WorkflowExplorerTreeSelectionModel.java     |   45 -
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 -
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 -
 .../spring/workflow-explorer-context-osgi.xml   |   19 -
 .../spring/workflow-explorer-context.xml        |   18 -
 taverna-workbench-workflow-view/pom.xml         |   89 -
 .../ui/actions/CopyGraphComponentAction.java    |  143 -
 .../ui/actions/CopyProcessorAction.java         |   53 -
 .../ui/actions/CutGraphComponentAction.java     |  153 -
 .../ui/actions/CutProcessorAction.java          |   69 -
 .../ui/actions/PasteGraphComponentAction.java   |   98 -
 .../ui/dndhandler/ServiceTransferHandler.java   |  119 -
 .../ui/menu/CopyGraphComponentMenuAction.java   |   54 -
 .../ui/menu/CopyProcessorMenuAction.java        |   56 -
 .../ui/menu/CutGraphComponentMenuAction.java    |   60 -
 .../ui/menu/CutProcessorMenuAction.java         |   69 -
 .../ui/menu/PasteGraphComponentMenuAction.java  |   73 -
 .../workbench/ui/toolbar/CopyToolbarAction.java |   51 -
 .../workbench/ui/toolbar/CutToolbarAction.java  |   57 -
 .../ui/toolbar/PasteToolbarAction.java          |   70 -
 .../ui/workflowview/ShowExceptionRunnable.java  |   26 -
 .../workbench/ui/workflowview/WorkflowView.java |  414 --
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |   10 -
 .../spring/workflow-view-context-osgi.xml       |   23 -
 .../META-INF/spring/workflow-view-context.xml   |   38 -
 taverna-workflow-explorer/pom.xml               |   91 +
 .../ui/workflowexplorer/WorkflowExplorer.java   |  824 ++++
 .../WorkflowExplorerFactory.java                |   95 +
 .../WorkflowExplorerTreeCellRenderer.java       |  216 ++
 .../WorkflowExplorerTreeModel.java              |  404 ++
 .../WorkflowExplorerTreeSelectionModel.java     |   45 +
 ....t2.workbench.ui.zaria.UIComponentFactorySPI |    1 +
 ...taverna.t2.workbench.ui.zaria.UIComponentSPI |    1 +
 .../spring/workflow-explorer-context-osgi.xml   |   19 +
 .../spring/workflow-explorer-context.xml        |   18 +
 taverna-workflow-view/pom.xml                   |   89 +
 .../ui/actions/CopyGraphComponentAction.java    |  143 +
 .../ui/actions/CopyProcessorAction.java         |   53 +
 .../ui/actions/CutGraphComponentAction.java     |  153 +
 .../ui/actions/CutProcessorAction.java          |   69 +
 .../ui/actions/PasteGraphComponentAction.java   |   98 +
 .../ui/dndhandler/ServiceTransferHandler.java   |  119 +
 .../ui/menu/CopyGraphComponentMenuAction.java   |   54 +
 .../ui/menu/CopyProcessorMenuAction.java        |   56 +
 .../ui/menu/CutGraphComponentMenuAction.java    |   60 +
 .../ui/menu/CutProcessorMenuAction.java         |   69 +
 .../ui/menu/PasteGraphComponentMenuAction.java  |   73 +
 .../workbench/ui/toolbar/CopyToolbarAction.java |   51 +
 .../workbench/ui/toolbar/CutToolbarAction.java  |   57 +
 .../ui/toolbar/PasteToolbarAction.java          |   70 +
 .../ui/workflowview/ShowExceptionRunnable.java  |   26 +
 .../workbench/ui/workflowview/WorkflowView.java |  414 ++
 .../net.sf.taverna.t2.ui.menu.MenuComponent     |   10 +
 .../spring/workflow-view-context-osgi.xml       |   23 +
 .../META-INF/spring/workflow-view-context.xml   |   38 +
 2585 files changed, 132282 insertions(+), 132282 deletions(-)
----------------------------------------------------------------------



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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanel.java
new file mode 100644
index 0000000..0b01fe2
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanel.java
@@ -0,0 +1,411 @@
+/*******************************************************************************
+ * 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.ui.servicepanel;
+
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.SwingUtilities.invokeLater;
+import static net.sf.taverna.t2.servicedescriptions.ServiceDescription.LOCAL_SERVICES;
+import static net.sf.taverna.t2.servicedescriptions.ServiceDescription.SERVICE_TEMPLATES;
+
+import java.awt.BorderLayout;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.servicedescriptions.events.AbstractProviderEvent;
+import net.sf.taverna.t2.servicedescriptions.events.AbstractProviderNotification;
+import net.sf.taverna.t2.servicedescriptions.events.PartialServiceDescriptionsNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderErrorNotification;
+import net.sf.taverna.t2.servicedescriptions.events.RemovedProviderEvent;
+import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionProvidedEvent;
+import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionRegistryEvent;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeModel;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.commons.services.ServiceRegistry;
+
+/**
+ * A panel of available services
+ *
+ * @author Stian Soiland-Reyes
+ */
+@SuppressWarnings("serial")
+public class ServicePanel extends JPanel implements UIComponentSPI {
+	private static Logger logger = Logger.getLogger(ServicePanel.class);
+	private static final int INITIAL_BLANK_OUT_COUNTER = 2;
+	public static final String AVAILABLE_SERVICES = "Available services";
+	public static final String MATCHING_SERVIES = "Matching services";
+	public static final String NO_MATCHING_SERVICES = "No matching services";
+	public static final String MOBY_OBJECTS = "MOBY Objects";
+	/**
+	 * A Comparable constant to be used with buildPathMap
+	 */
+	private static final String SERVICES = "4DA84170-7746-4817-8C2E-E29AF8B2984D";
+	private static final int STATUS_LINE_MESSAGE_MS = 600;
+	private static ServicePathElementComparator servicePathElementComparator = new ServicePathElementComparator();
+
+	public int blankOutCounter = 0;
+	private TreeUpdaterThread updaterThread;
+	private RootFilterTreeNode root = new RootFilterTreeNode(AVAILABLE_SERVICES);
+	private ServiceTreePanel serviceTreePanel;
+	private JLabel statusLine;
+	private FilterTreeModel treeModel;
+	protected Timer statusUpdateTimer;
+
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+	protected final ServiceDescriptionRegistryObserver serviceDescriptionRegistryObserver = new ServiceDescriptionRegistryObserver();
+	protected final Object updateLock = new Object();
+	private final EditManager editManager;
+	private final MenuManager menuManager;
+	private final SelectionManager selectionManager;
+	private final ServiceRegistry serviceRegistry;
+
+	public ServicePanel(ServiceDescriptionRegistry serviceDescriptionRegistry,
+			EditManager editManager, MenuManager menuManager,
+			SelectionManager selectionManager, ServiceRegistry serviceRegistry) {
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+		this.editManager = editManager;
+		this.menuManager = menuManager;
+		this.selectionManager = selectionManager;
+		this.serviceRegistry = serviceRegistry;
+		serviceDescriptionRegistry.addObserver(serviceDescriptionRegistryObserver);
+		initialise();
+	}
+
+	@Override
+	public ImageIcon getIcon() {
+		return null;
+	}
+
+	@Override
+	public String getName() {
+		return "Service panel";
+	}
+
+	@Override
+	public void onDisplay() {
+	}
+
+	@Override
+	public void onDispose() {
+	}
+
+	public void providerStatus(ServiceDescriptionProvider provider, String message) {
+		logger.info(message + " " + provider);
+		final String htmlMessage = "<small>" + message + " [" + provider + "]</small>";
+
+		invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				blankOutCounter = INITIAL_BLANK_OUT_COUNTER;
+				statusLine.setText("<html>" + htmlMessage + "</html>");
+				statusLine.setVisible(true);
+			}
+		});
+	}
+
+	protected void initialise() {
+		removeAll();
+		setLayout(new BorderLayout());
+		treeModel = new FilterTreeModel(root);
+		serviceTreePanel = new ServiceTreePanel(treeModel, serviceDescriptionRegistry, editManager, menuManager, selectionManager, serviceRegistry);
+		serviceTreePanel.setAvailableObjectsString(AVAILABLE_SERVICES);
+		serviceTreePanel.setMatchingObjectsString(MATCHING_SERVIES);
+		serviceTreePanel.setNoMatchingObjectsString(NO_MATCHING_SERVICES);
+		add(serviceTreePanel);
+		statusLine = new JLabel();
+		add(statusLine, BorderLayout.SOUTH);
+		if (statusUpdateTimer != null)
+			statusUpdateTimer.cancel();
+		statusUpdateTimer = new Timer("Clear status line", true);
+		statusUpdateTimer
+				.scheduleAtFixedRate(new UpdateStatusLineTask(), 0, STATUS_LINE_MESSAGE_MS);
+		updateTree();
+	}
+
+	protected void updateTree() {
+		synchronized (updateLock) {
+			if (updaterThread != null && updaterThread.isAlive()) {
+				return;
+			}
+			updaterThread = new TreeUpdaterThread();
+			updaterThread.start();
+		}
+	}
+
+	protected static class ServicePathElementComparator implements Comparator<Object> {
+		@Override
+		public int compare(Object o1, Object o2) {
+			if ((o1 instanceof String) && (o2 instanceof String)) {
+				if (o1.equals(SERVICE_TEMPLATES))
+					return -1;
+				else if (o2.equals(SERVICE_TEMPLATES))
+					return 1;
+				if (o1.equals(LOCAL_SERVICES))
+					return -1;
+				else if (o2.equals(LOCAL_SERVICES))
+					return 1;
+				if (o1.equals(MOBY_OBJECTS))
+					return -1;
+				else if (o2.equals(MOBY_OBJECTS))
+					return 1;
+			}
+			return o1.toString().compareToIgnoreCase(o2.toString());
+		}
+	}
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	//FIXME this class is type-disastrous! Really bad.
+	public class TreeUpdaterThread extends Thread {
+		private boolean aborting = false;
+
+		private TreeUpdaterThread() {
+			super("Updating service panel");
+			setDaemon(true);
+		}
+
+		public void abort() {
+			aborting = true;
+			interrupt();
+		}
+
+		@Override
+		public void run() {
+			Map<Comparable, Map> pathMap = buildPathMap();
+			populateChildren(root, pathMap);
+			invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					try {
+						serviceTreePanel.runFilter();
+					} catch (InterruptedException | InvocationTargetException e) {
+						logger.error("failed to filter", e);
+					}
+				}
+			});
+		}
+
+		protected Map<Comparable, Map> buildPathMap() {
+			Map<Comparable, Map> paths = new TreeMap<>();
+			for (ServiceDescription serviceDescription : serviceDescriptionRegistry
+					.getServiceDescriptions()) {
+				if (aborting)
+					return paths;
+				Map currentPath = paths;
+				for (Object pathElem : serviceDescription.getPath()) {
+					Map pathEntry = (Map) currentPath.get(pathElem);
+					if (pathEntry == null) {
+						pathEntry = new TreeMap();
+						currentPath.put(pathElem, pathEntry);
+					}
+					currentPath = pathEntry;
+				}
+				TreeMap<String, Set<ServiceDescription>> services = (TreeMap) currentPath
+						.get(SERVICES);
+				if (services == null) {
+					services = new TreeMap<>();
+					currentPath.put(SERVICES, services);
+				}
+				String serviceDescriptionName = serviceDescription.getName();
+				if (!services.containsKey(serviceDescriptionName)) {
+					Set<ServiceDescription> serviceSet = new HashSet<>();
+					services.put(serviceDescriptionName, serviceSet);
+				}
+				services.get(serviceDescriptionName).add(serviceDescription);
+			}
+			return paths;
+		}
+
+		protected void populateChildren(FilterTreeNode node, Map pathMap) {
+			if (aborting)
+				return;
+			if (node == root) {
+				// Clear top root
+				invokeLater(new Runnable() {
+					@Override
+					public void run() {
+						if (aborting)
+							return;
+						serviceTreePanel.setFilter(null);
+						root.removeAllChildren();
+					}
+				});
+			}
+
+			Set<Comparable> paths = new TreeSet<>(servicePathElementComparator);
+			Map<String, Set<ServiceDescription>> services = (Map) pathMap
+					.get(SERVICES);
+			if (services == null)
+				services = new TreeMap<>();
+			paths.addAll(pathMap.keySet());
+			paths.addAll(services.keySet());
+
+			for (Comparable pathElement : paths) {
+				if (aborting)
+					return;
+				if (pathElement.equals(SERVICES))
+					continue;
+				Set<FilterTreeNode> childNodes = new HashSet<>();
+				if (services.containsKey(pathElement)) {
+					for (ServiceDescription sd : services.get(pathElement))
+						childNodes.add(new ServiceFilterTreeNode(sd));
+				} else
+					childNodes.add(new PathElementFilterTreeNode((String) pathElement));
+				invokeLater(new AddNodeRunnable(node, childNodes));
+				if ((pathMap.containsKey(pathElement)) && !childNodes.isEmpty())
+					populateChildren(childNodes.iterator().next(), (Map) pathMap.get(pathElement));
+			}
+			// if (!services.isEmpty()) {
+			// Collections.sort(services, serviceComparator);
+			// for (String serviceName : services.keySet()) {
+			// if (aborting) {
+			// return;
+			// }
+			// if (pathMap.containsKey(serviceName)) {
+			// continue;
+			// }
+			// SwingUtilities.invokeLater(new AddNodeRunnable(node,
+			// new ServiceFilterTreeNode(services.get(serviceName))));
+			// }
+			// }
+		}
+
+		public class AddNodeRunnable implements Runnable {
+			private final Set<FilterTreeNode> nodes;
+			private final FilterTreeNode root;
+
+			public AddNodeRunnable(FilterTreeNode root, Set<FilterTreeNode> nodes) {
+				this.root = root;
+				this.nodes = nodes;
+			}
+
+			@Override
+			public void run() {
+				if (aborting)
+					return;
+				for (FilterTreeNode n : nodes)
+					root.add(n);
+			}
+		}
+	}
+
+	public static class RemoveNodeRunnable implements Runnable {
+		private final FilterTreeNode root;
+
+		public RemoveNodeRunnable(FilterTreeNode root) {
+			this.root = root;
+		}
+
+		@Override
+		public void run() {
+			root.removeFromParent();
+		}
+	}
+
+	private final class ServiceDescriptionRegistryObserver implements
+			Observer<ServiceDescriptionRegistryEvent> {
+		Set<ServiceDescriptionProvider> alreadyComplainedAbout = new HashSet<>();
+
+		@Override
+		public void notify(Observable<ServiceDescriptionRegistryEvent> sender,
+				ServiceDescriptionRegistryEvent message) throws Exception {
+			if (message instanceof ProviderErrorNotification)
+				reportServiceProviderError((ProviderErrorNotification) message);
+			else if (message instanceof ServiceDescriptionProvidedEvent
+					|| message instanceof RemovedProviderEvent) {
+				AbstractProviderEvent ape = (AbstractProviderEvent) message;
+				alreadyComplainedAbout.remove(ape.getProvider());
+			}
+
+			if (message instanceof AbstractProviderNotification) {
+				AbstractProviderNotification abstractProviderNotification = (AbstractProviderNotification) message;
+				providerStatus(abstractProviderNotification.getProvider(),
+						abstractProviderNotification.getMessage());
+			}
+			if (message instanceof PartialServiceDescriptionsNotification)
+				/*
+				 * TODO: Support other events and only update relevant parts of
+				 * tree, or at least select the recently added provider
+				 */
+				updateTree();
+			else if (message instanceof RemovedProviderEvent)
+				updateTree();
+		}
+
+		private void reportServiceProviderError(
+				final ProviderErrorNotification pen) {
+			ServiceDescriptionProvider provider = pen.getProvider();
+			if (serviceDescriptionRegistry
+					.getDefaultServiceDescriptionProviders().contains(provider))
+				return;
+			if (alreadyComplainedAbout.contains(provider))
+				return;
+
+			alreadyComplainedAbout.add(provider);
+			invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					showMessageDialog(ServicePanel.this, pen.getMessage()
+							+ "\n" + pen.getProvider(), "Import service error",
+							ERROR_MESSAGE);
+				}
+			});
+		}
+	}
+
+	private final class UpdateStatusLineTask extends TimerTask {
+		@Override
+		public void run() {
+			if (blankOutCounter < 0 || blankOutCounter-- > 0)
+				// Only clear it once
+				return;
+			invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					if (blankOutCounter < 0)
+						statusLine.setVisible(false);
+				}
+			});
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanelComponentFactory.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanelComponentFactory.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanelComponentFactory.java
new file mode 100644
index 0000000..2eb97ef
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanelComponentFactory.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.servicepanel;
+
+import javax.swing.ImageIcon;
+
+import uk.org.taverna.commons.services.ServiceRegistry;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+
+/**
+ * Service panel factory
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class ServicePanelComponentFactory implements UIComponentFactorySPI {
+	private ServiceDescriptionRegistry serviceDescriptionRegistry;
+	private EditManager editManager;
+	private MenuManager menuManager;
+	private SelectionManager selectionManager;
+	private ServiceRegistry serviceRegistry;
+
+	@Override
+	public UIComponentSPI getComponent() {
+		return new ServicePanel(serviceDescriptionRegistry, editManager,
+				menuManager, selectionManager, serviceRegistry);
+	}
+
+	@Override
+	public ImageIcon getIcon() {
+		return null;
+	}
+
+	@Override
+	public String getName() {
+		return "Service panel";
+	}
+
+	public void setServiceDescriptionRegistry(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setMenuManager(MenuManager menuManager) {
+		this.menuManager = menuManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setServiceRegistry(ServiceRegistry serviceRegistry) {
+		this.serviceRegistry = serviceRegistry;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeCellRenderer.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeCellRenderer.java
new file mode 100644
index 0000000..4b7388d
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeCellRenderer.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * 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.ui.servicepanel;
+
+import static net.sf.taverna.t2.workbench.activityicons.DefaultActivityIcon.getDefaultIcon;
+
+import java.awt.Component;
+
+import javax.swing.Icon;
+import javax.swing.JTree;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeCellRenderer;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode;
+
+@SuppressWarnings("serial")
+public class ServiceTreeCellRenderer extends FilterTreeCellRenderer {
+	@Override
+	public Component getTreeCellRendererComponent(JTree tree, Object value,
+			boolean sel, boolean expanded, boolean leaf, int row,
+			boolean hasFocus) {
+		Component result = super.getTreeCellRendererComponent(tree, value, sel,
+				expanded, leaf, row, hasFocus);
+		if (result instanceof ServiceTreeCellRenderer
+				&& value instanceof FilterTreeNode
+				&& ((FilterTreeNode) value).getUserObject() instanceof ServiceDescription)
+			prettifyServiceTreeCell((ServiceTreeCellRenderer) result,
+					(ServiceDescription) ((FilterTreeNode) value)
+							.getUserObject());
+		else {
+			// Commented out - these are ugly, use the default folder icons instead
+			/*
+			 * if (expanded) { ((ServiceTreeCellRenderer) result)
+			 * .setIcon(WorkbenchIcons.folderOpenIcon); } else {
+			 * ((ServiceTreeCellRenderer) result)
+			 * .setIcon(WorkbenchIcons.folderClosedIcon); }
+			 */
+		}
+		return result;
+	}
+
+	private void prettifyServiceTreeCell(ServiceTreeCellRenderer renderer,
+			ServiceDescription item) {
+		String name = item.getName();
+		if (getFilter() != null)
+			name = getFilter().filterRepresentation(name);
+		// serviceTreeCellRenderer.setForeground(Color.red);
+		String displayName = name;
+
+		String textualDescription = item.getDescription();
+		if (textualDescription != null && !textualDescription.isEmpty())
+			displayName = displayName + " - " + textualDescription;
+		renderer.setText(displayName);
+
+		Icon activityIcon = item.getIcon();
+		renderer.setIcon(activityIcon != null ? activityIcon
+				: getDefaultIcon());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeClickListener.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeClickListener.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeClickListener.java
new file mode 100644
index 0000000..a76dcc9
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeClickListener.java
@@ -0,0 +1,252 @@
+/*******************************************************************************
+ * 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.ui.servicepanel;
+
+import static java.awt.Color.RED;
+import static javax.swing.SwingUtilities.invokeLater;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.BLUE;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.GREEN;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.ORANGE;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.halfShade;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.minusIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.plusIcon;
+import static net.sf.taverna.t2.workbench.ui.workflowview.WorkflowView.importServiceDescription;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.swing.AbstractAction;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JTree;
+import javax.swing.tree.TreePath;
+
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.ExportServiceDescriptionsAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.ImportServiceDescriptionsFromFileAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.ImportServiceDescriptionsFromURLAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.RemoveDefaultServicesAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.RemoveUserServicesAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.RestoreDefaultServicesAction;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeSelectionModel;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.TreePanel;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.commons.services.ServiceRegistry;
+
+/**
+ * @author alanrw
+ */
+public class ServiceTreeClickListener extends MouseAdapter {
+	private static Logger logger = Logger.getLogger(ServiceTreeClickListener.class);
+
+	private JTree tree;
+	private TreePanel panel;
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+	private final EditManager editManager;
+	private final MenuManager menuManager;
+	private final SelectionManager selectionManager;
+	private final ServiceRegistry serviceRegistry;
+
+	public ServiceTreeClickListener(JTree tree, TreePanel panel,
+			ServiceDescriptionRegistry serviceDescriptionRegistry, EditManager editManager,
+			MenuManager menuManager, SelectionManager selectionManager, ServiceRegistry serviceRegistry) {
+		this.tree = tree;
+		this.panel = panel;
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+		this.editManager = editManager;
+		this.menuManager = menuManager;
+		this.selectionManager = selectionManager;
+		this.serviceRegistry = serviceRegistry;
+	}
+
+	@SuppressWarnings("serial")
+	private void handleMouseEvent(MouseEvent evt) {
+		FilterTreeSelectionModel selectionModel = (FilterTreeSelectionModel) tree
+				.getSelectionModel();
+		// Discover the tree row that was clicked on
+		int selRow = tree.getRowForLocation(evt.getX(), evt.getY());
+		if (selRow == -1)
+			return;
+
+		// Get the selection path for the row
+		TreePath selectionPath = tree
+				.getPathForLocation(evt.getX(), evt.getY());
+		if (selectionPath == null)
+			return;
+
+		// Get the selected node
+		final FilterTreeNode selectedNode = (FilterTreeNode) selectionPath
+				.getLastPathComponent();
+
+		selectionModel.clearSelection();
+		selectionModel.mySetSelectionPath(selectionPath);
+
+		if (evt.isPopupTrigger()) {
+			JPopupMenu menu = new JPopupMenu();
+			Object selectedObject = selectedNode.getUserObject();
+			logger.info(selectedObject.getClass().getName());
+			if (!(selectedObject instanceof ServiceDescription)) {
+				menu.add(new ShadedLabel("Tree", BLUE));
+				menu.add(new JMenuItem(new AbstractAction("Expand all",
+						plusIcon) {
+					@Override
+					public void actionPerformed(ActionEvent evt) {
+						invokeLater(new Runnable() {
+							@Override
+							public void run() {
+								panel.expandAll(selectedNode, true);
+							}
+						});
+					}
+				}));
+				menu.add(new JMenuItem(new AbstractAction("Collapse all",
+						minusIcon) {
+					@Override
+					public void actionPerformed(ActionEvent evt) {
+						invokeLater(new Runnable() {
+							@Override
+							public void run() {
+								panel.expandAll(selectedNode, false);
+							}
+						});
+					}
+				}));
+			}
+
+			if (selectedObject instanceof ServiceDescription) {
+				final ServiceDescription sd = (ServiceDescription) selectedObject;
+				menu.add(new ShadedLabel(sd.getName(), ORANGE));
+				menu.add(new AbstractAction("Add to workflow") {
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						importServiceDescription(sd, false, editManager,
+								menuManager, selectionManager, serviceRegistry);
+					}
+				});
+				menu.add(new AbstractAction("Add to workflow with name...") {
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						importServiceDescription(sd, true, editManager,
+								menuManager, selectionManager, serviceRegistry);
+					}
+				});
+			}
+
+			Map<String, ServiceDescriptionProvider> nameMap = getServiceDescriptionProviderMap(selectedNode);
+
+			boolean first = true;
+			for (String name : nameMap.keySet()) {
+				final ServiceDescriptionProvider sdp = nameMap.get(name);
+				if (!(sdp instanceof ConfigurableServiceProvider))
+					continue;
+				if (first) {
+					menu.add(new ShadedLabel(
+							"Remove individual service provider", GREEN));
+					first = false;
+				}
+				menu.add(new AbstractAction(name) {
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						serviceDescriptionRegistry
+								.removeServiceDescriptionProvider(sdp);
+					}
+				});
+			}
+
+			if (selectedNode.isRoot()) { // Root "Available services"
+				menu.add(new ShadedLabel("Default and added service providers",
+						ORANGE));
+				menu.add(new RemoveUserServicesAction(
+						serviceDescriptionRegistry));
+				menu.add(new RemoveDefaultServicesAction(
+						serviceDescriptionRegistry));
+				menu.add(new RestoreDefaultServicesAction(
+						serviceDescriptionRegistry));
+
+				menu.add(new ShadedLabel("Import/export services", halfShade(RED)));
+				menu.add(new ImportServiceDescriptionsFromFileAction(
+						serviceDescriptionRegistry));
+				menu.add(new ImportServiceDescriptionsFromURLAction(
+						serviceDescriptionRegistry));
+				menu.add(new ExportServiceDescriptionsAction(
+						serviceDescriptionRegistry));
+			}
+
+			menu.show(evt.getComponent(), evt.getX(), evt.getY());
+		}
+	}
+
+	private Map<String, ServiceDescriptionProvider> getServiceDescriptionProviderMap(
+			FilterTreeNode selectedNode) {
+		Set<ServiceDescriptionProvider> providers;
+
+		if (selectedNode.isRoot())
+			providers = serviceDescriptionRegistry
+					.getServiceDescriptionProviders();
+		else {
+			providers = new HashSet<>();
+			for (FilterTreeNode leaf : selectedNode.getLeaves()) {
+				if (!leaf.isLeaf())
+					logger.info("Not a leaf");
+				if (!(leaf.getUserObject() instanceof ServiceDescription)) {
+					logger.info(leaf.getUserObject().getClass()
+							.getCanonicalName());
+					logger.info(leaf.getUserObject().toString());
+					continue;
+				}
+				providers
+						.addAll(serviceDescriptionRegistry
+								.getServiceDescriptionProviders((ServiceDescription) leaf
+										.getUserObject()));
+			}
+		}
+
+		TreeMap<String, ServiceDescriptionProvider> nameMap = new TreeMap<>();
+		for (ServiceDescriptionProvider sdp : providers)
+			nameMap.put(sdp.toString(), sdp);
+		return nameMap;
+	}
+
+	@Override
+	public void mousePressed(MouseEvent evt) {
+		handleMouseEvent(evt);
+	}
+
+	@Override
+	public void mouseReleased(MouseEvent evt) {
+		handleMouseEvent(evt);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreePanel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreePanel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreePanel.java
new file mode 100644
index 0000000..ce3e336
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreePanel.java
@@ -0,0 +1,176 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.ui.servicepanel;
+
+import static java.awt.datatransfer.DataFlavor.javaJVMLocalObjectMimeType;
+import static javax.swing.SwingUtilities.invokeLater;
+
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.TransferHandler;
+import javax.swing.event.TreeExpansionEvent;
+import javax.swing.event.TreeWillExpandListener;
+import javax.swing.tree.ExpandVetoException;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreePath;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.servicepanel.menu.AddServiceProviderMenu;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.Filter;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeModel;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.TreePanel;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.commons.services.ServiceRegistry;
+
+public class ServiceTreePanel extends TreePanel {
+	private static final long serialVersionUID = 6611462684296693909L;
+	private static Logger logger = Logger.getLogger(ServiceTreePanel.class);
+
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+	private final EditManager editManager;
+	private final MenuManager menuManager;
+	private final SelectionManager selectionManager;
+	private final ServiceRegistry serviceRegistry;
+
+	public ServiceTreePanel(FilterTreeModel treeModel,
+			ServiceDescriptionRegistry serviceDescriptionRegistry, EditManager editManager,
+			MenuManager menuManager, SelectionManager selectionManager, ServiceRegistry serviceRegistry) {
+		super(treeModel);
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+		this.editManager = editManager;
+		this.menuManager = menuManager;
+		this.selectionManager = selectionManager;
+		this.serviceRegistry = serviceRegistry;
+		initialize();
+	}
+
+	@Override
+	protected void initialize() {
+		super.initialize();
+		tree.setDragEnabled(true);
+		tree.setTransferHandler(new ServiceTransferHandler());
+		tree.addTreeWillExpandListener(new AvoidRootCollapse());
+		tree.expandRow(0);
+
+		invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				tree.addMouseListener(new ServiceTreeClickListener(tree,
+						ServiceTreePanel.this, serviceDescriptionRegistry,
+						editManager, menuManager, selectionManager,
+						serviceRegistry));
+			}
+		});
+	}
+
+	@Override
+	protected Component createExtraComponent() {
+		JComponent buttonPanel = new JPanel(new FlowLayout());
+		buttonPanel.add(new AddServiceProviderMenu(serviceDescriptionRegistry));
+		// buttonPanel.add(new JButton(new RefreshProviderRegistryAction()));
+		return buttonPanel;
+	}
+
+	@Override
+	public Filter createFilter(String text) {
+		return new ServiceFilter(text, filterTreeModel.getRoot());
+	}
+
+	@Override
+	protected TreeCellRenderer createCellRenderer() {
+		return new ServiceTreeCellRenderer();
+	}
+
+	public static class AvoidRootCollapse implements TreeWillExpandListener {
+		@Override
+		public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
+			if (event.getPath().getPathCount() == 1)
+				throw new ExpandVetoException(event, "Can't collapse root");
+		}
+
+		@Override
+		public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
+		}
+	}
+
+	private final class ServiceTransferHandler extends TransferHandler {
+		private static final long serialVersionUID = 4347965626386951176L;
+
+		/**
+		 * Triggered when a node ie. an {@link ActivityItem} is dragged out of
+		 * the tree. Figures out what node it is being dragged and then starts a
+		 * drag action with it
+		 */
+		@Override
+		protected Transferable createTransferable(JComponent c) {
+			TreePath selectionPath = tree.getSelectionPath();
+			if (selectionPath == null)
+				return null;
+			FilterTreeNode lastPathComponent = (FilterTreeNode) selectionPath
+					.getLastPathComponent();
+			if (!(lastPathComponent.getUserObject() instanceof ServiceDescription))
+				return null;
+			final ServiceDescription serviceDescription = (ServiceDescription) lastPathComponent
+					.getUserObject();
+
+			return new Transferable() {
+				@Override
+				public Object getTransferData(DataFlavor flavor)
+						throws UnsupportedFlavorException, IOException {
+					return serviceDescription;
+				}
+
+				@Override
+				public DataFlavor[] getTransferDataFlavors() {
+					DataFlavor[] flavors = new DataFlavor[1];
+					try {
+						flavors[0] = getFlavorForClass(ServiceDescription.class);
+					} catch (ClassNotFoundException e) {
+						logger.error("Error casting Dataflavor", e);
+						flavors[0] = null;
+					}
+					return flavors;
+				}
+
+				@Override
+				public boolean isDataFlavorSupported(DataFlavor flavor) {
+					DataFlavor thisFlavor = null;
+					try {
+						thisFlavor = getFlavorForClass(ServiceDescription.class);
+					} catch (ClassNotFoundException e) {
+						logger.error("Error casting Dataflavor", e);
+					}
+					return flavor.equals(thisFlavor);
+				}
+			};
+		}
+
+		@Override
+		public int getSourceActions(JComponent c) {
+			return COPY_OR_MOVE;
+		}
+	}
+
+	private DataFlavor getFlavorForClass(Class<?> clazz)
+			throws ClassNotFoundException {
+		String name = clazz.getName();
+		return new DataFlavor(javaJVMLocalObjectMimeType + ";class=" + clazz,
+				name.substring(name.lastIndexOf('.') + 1),
+				clazz.getClassLoader());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/AddServiceProviderAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/AddServiceProviderAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/AddServiceProviderAction.java
new file mode 100644
index 0000000..c8f2bfa
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/AddServiceProviderAction.java
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.actions;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.BorderLayout.WEST;
+import static java.awt.event.KeyEvent.VK_ENTER;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow;
+import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptors;
+import static org.apache.log4j.Logger.getLogger;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.lang.uibuilder.UIBuilder;
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.CustomizedConfigurePanelProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.servicedescriptions.CustomizedConfigurePanelProvider.CustomizedConfigureCallBack;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderErrorNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionProvidedEvent;
+import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionRegistryEvent;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+
+/**
+ * Action for adding a service provider
+ * 
+ * @author Stian Soiland-Reyes
+ * @author Alan R Williams
+ */
+@SuppressWarnings("serial")
+public class AddServiceProviderAction extends AbstractAction {
+	private static Logger logger = getLogger(AddServiceProviderAction.class);
+
+	// protected static Dimension DIALOG_SIZE = new Dimension(400, 300);
+
+	private ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	private final ConfigurableServiceProvider confProvider;
+	private final Component owner;
+
+	public AddServiceProviderAction(ConfigurableServiceProvider confProvider,
+			Component owner) {
+		super(confProvider.getName() + "...", confProvider.getIcon());
+		this.confProvider = confProvider;
+		this.owner = owner;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		if (confProvider instanceof CustomizedConfigurePanelProvider) {
+			final CustomizedConfigurePanelProvider provider = (CustomizedConfigurePanelProvider) confProvider;
+			provider.createCustomizedConfigurePanel(new CustomizedConfigureCallBack() {
+				@Override
+				public Configuration getTemplateConfig() {
+					return (Configuration) provider.getConfiguration().clone();
+				}
+
+				@Override
+				public ServiceDescriptionRegistry getServiceDescriptionRegistry() {
+					return AddServiceProviderAction.this.getServiceDescriptionRegistry();
+				}
+
+				@Override
+				public void newProviderConfiguration(Configuration providerConfig) {
+					addNewProvider(providerConfig);
+				}
+			});
+			return;
+		}
+
+		Configuration configuration;
+		try {
+			configuration = (Configuration) confProvider.getConfiguration().clone();
+		} catch (Exception ex) {
+			throw new RuntimeException("Can't clone configuration bean", ex);
+		}
+		JPanel buildEditor = buildEditor(configuration);
+		String title = "Add " + confProvider.getName();
+		JDialog dialog = new HelpEnabledDialog(getMainWindow(), title, true, null);
+		JPanel iconPanel = new JPanel();
+		iconPanel.add(new JLabel(confProvider.getIcon()), NORTH);
+		dialog.add(iconPanel, WEST);
+		dialog.add(buildEditor, CENTER);
+		JPanel buttonPanel = new JPanel(new BorderLayout());
+		final AddProviderAction addProviderAction = new AddProviderAction(configuration,
+				dialog);
+		JButton addProviderButton = new JButton(addProviderAction);
+		buttonPanel.add(addProviderButton, WEST);
+		
+		dialog.add(buttonPanel, SOUTH);
+	    // When user presses "Return" key fire the action on the "Add" button
+		addProviderButton.addKeyListener(new KeyAdapter() {
+			@Override
+			public void keyPressed(KeyEvent evt) {
+				if (evt.getKeyCode() == VK_ENTER)
+					addProviderAction.actionPerformed(null);
+			}
+		});
+		dialog.getRootPane().setDefaultButton(addProviderButton);
+		
+		// dialog.setSize(buttonPanel.getPreferredSize());
+		dialog.pack();
+		dialog.setLocationRelativeTo(owner);
+//		dialog.setLocation(owner.getLocationOnScreen().x + owner.getWidth(),
+//				owner.getLocationOnScreen().y + owner.getHeight());
+		dialog.setVisible(true);
+	}
+
+	protected void addNewProvider(Configuration configurationBean) {
+		ConfigurableServiceProvider cloned = (ConfigurableServiceProvider) confProvider
+				.newInstance();
+		try {
+			cloned.configure(configurationBean);
+			getServiceDescriptionRegistry().addObserver(
+					new CheckAddedCorrectlyObserver(cloned));
+			getServiceDescriptionRegistry().addServiceDescriptionProvider(
+					cloned);
+		} catch (Exception ex) {
+			logger.warn("Can't configure provider " + cloned + " using "
+					+ configurationBean, ex);
+			showMessageDialog(owner, "Can't configure service provider "
+					+ cloned.getName(), "Can't add service provider",
+					ERROR_MESSAGE);
+		}
+	}
+
+	private PropertyDescriptor[] getProperties(Configuration configuration) {
+		// FIXME This is *so* wrong!
+		try {
+			return getPropertyDescriptors(configuration);
+		} catch (Exception ex) {
+			throw new RuntimeException("Can't inspect configuration bean", ex);
+		}
+	}
+
+	// TODO This is probably not right
+	protected JPanel buildEditor(Configuration configuration) {
+		List<String> uiBuilderConfig = new ArrayList<>();
+		int lastPreferred = 0;
+		for (PropertyDescriptor property : getProperties(configuration)) {
+			if (property.isHidden() || property.isExpert())
+				// TODO: Add support for expert properties
+				continue;
+			String propertySpec = property.getName() + ":name="
+					+ property.getDisplayName();
+			if (property.isPreferred())
+				// Add it to the front
+				uiBuilderConfig.add(lastPreferred++, propertySpec);
+			else
+				uiBuilderConfig.add(propertySpec);
+		}
+
+		return UIBuilder.buildEditor(configuration, uiBuilderConfig
+				.toArray(new String[0]));
+	}
+
+	public void setServiceDescriptionRegistry(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	public ServiceDescriptionRegistry getServiceDescriptionRegistry() {
+		return serviceDescriptionRegistry;
+	}
+
+	public class AddProviderAction extends AbstractAction {
+		private final Configuration configurationBean;
+		private final JDialog dialog;
+
+		private AddProviderAction(Configuration configurationBean, JDialog dialog) {
+			super("Add");
+			this.configurationBean = configurationBean;
+			this.dialog = dialog;
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			addNewProvider(configurationBean);
+			dialog.setVisible(false);
+		}
+	}
+
+	public class CheckAddedCorrectlyObserver implements
+			Observer<ServiceDescriptionRegistryEvent> {
+		private final ConfigurableServiceProvider provider;
+
+		private CheckAddedCorrectlyObserver(ConfigurableServiceProvider provider) {
+			this.provider = provider;
+		}
+
+		@Override
+		public void notify(Observable<ServiceDescriptionRegistryEvent> sender,
+				ServiceDescriptionRegistryEvent message) throws Exception {
+			if (message instanceof ProviderErrorNotification)
+				notify((ProviderErrorNotification) message);
+			else if (message instanceof ServiceDescriptionProvidedEvent)
+				notify((ServiceDescriptionProvidedEvent) message);
+		}
+
+		private void notify(ServiceDescriptionProvidedEvent providedMsg) {
+			if (providedMsg.getProvider() == provider)
+				getServiceDescriptionRegistry().removeObserver(this);
+		}
+
+		private void notify(ProviderErrorNotification errorMsg) {
+			if (errorMsg.getProvider() != provider)
+				return;
+			getServiceDescriptionRegistry().removeObserver(this);
+			getServiceDescriptionRegistry().removeServiceDescriptionProvider(
+					provider);
+//			showMessageDialog(owner, errorMsg.getMessage(),
+//					"Can't add provider " + provider, ERROR_MESSAGE);
+		}
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ExportServiceDescriptionsAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ExportServiceDescriptionsAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ExportServiceDescriptionsAction.java
new file mode 100644
index 0000000..990e429
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ExportServiceDescriptionsAction.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.actions;
+
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.NO_OPTION;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Action to export the current service descritpions from the Service
+ * Registry to an xml file.
+ *
+ * @author Alex Nenadic
+ */
+//FIXME this file assumes we're writing out as XML
+@SuppressWarnings("serial")
+public class ExportServiceDescriptionsAction extends AbstractAction {
+	private static final String EXTENSION = ".xml";
+	private static final String EXPORT_SERVICES = "Export services to file";
+	private static final String SERVICE_EXPORT_DIR_PROPERTY = "serviceExportDir";
+	private Logger logger = Logger.getLogger(ExportServiceDescriptionsAction.class);
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public ExportServiceDescriptionsAction(ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super(EXPORT_SERVICES);
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	public static final boolean INHIBIT = true;
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		JComponent parentComponent = null;
+		if (e.getSource() instanceof JComponent)
+			parentComponent = (JComponent) e.getSource();
+
+		if (INHIBIT) {
+			showMessageDialog(parentComponent,
+					"Operation not currently working correctly",
+					"Not Implemented", ERROR_MESSAGE);
+			return;
+		}
+
+		JFileChooser fileChooser = new JFileChooser();
+		Preferences prefs = Preferences.userNodeForPackage(getClass());
+		String curDir = prefs.get(SERVICE_EXPORT_DIR_PROPERTY,
+				System.getProperty("user.home"));
+		fileChooser.setDialogTitle("Select file to export services to");
+
+		fileChooser.setFileFilter(new FileFilter() {
+			@Override
+			public boolean accept(File f) {
+				return f.isDirectory()
+						|| f.getName().toLowerCase().endsWith(EXTENSION);
+			}
+
+			@Override
+			public String getDescription() {
+				return ".xml files";
+			}
+		});
+
+		fileChooser.setCurrentDirectory(new File(curDir));
+
+		boolean tryAgain = true;
+		while (tryAgain) {
+			tryAgain = false;
+			int returnVal = fileChooser.showSaveDialog(parentComponent);
+			if (returnVal == APPROVE_OPTION) {
+				prefs.put(SERVICE_EXPORT_DIR_PROPERTY, fileChooser.getCurrentDirectory()
+						.toString());
+				File file = fileChooser.getSelectedFile();
+				if (!file.getName().toLowerCase().endsWith(EXTENSION)) {
+					String newName = file.getName() + EXTENSION;
+					file = new File(file.getParentFile(), newName);
+				}
+
+				try {
+					if (file.exists()) {
+						String msg = "Are you sure you want to overwrite existing file "
+								+ file + "?";
+						int ret = showConfirmDialog(parentComponent, msg,
+								"File already exists", YES_NO_CANCEL_OPTION);
+						if (ret == NO_OPTION) {
+							tryAgain = true;
+							continue;
+						} else if (ret != YES_OPTION) {
+							logger.info("Service descriptions export: aborted overwrite of "
+									+ file.getAbsolutePath());
+							break;
+						}
+					}
+					exportServiceDescriptions(file);
+					break;
+				} catch (Exception ex) {
+					logger.error("Service descriptions export: failed to export services to "
+							+ file.getAbsolutePath(), ex);
+					showMessageDialog(
+							parentComponent,
+							"Failed to export services to "
+									+ file.getAbsolutePath(), "Error",
+							ERROR_MESSAGE);
+					break;
+				}
+			}
+		}
+
+		if (parentComponent instanceof JButton)
+			// lose the focus from the button after performing the action
+			parentComponent.requestFocusInWindow();
+	}
+
+	private void exportServiceDescriptions(File file) {
+		// TODO: Open in separate thread to avoid hanging UI
+		serviceDescriptionRegistry.exportCurrentServiceDescriptions(file);
+		logger.info("Service descriptions export: saved to file "
+				+ file.getAbsolutePath());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromFileAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromFileAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromFileAction.java
new file mode 100644
index 0000000..1542583
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromFileAction.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.actions;
+
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.QUESTION_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.JOptionPane.showOptionDialog;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.util.HashSet;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import org.apache.log4j.Logger;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+/**
+ * Action to import a list of service descriptions from an xml file
+ * into the Service Registry. Users have an option to completely
+ * replace the current services or just add the ones from the file
+ * to the current services.
+ *
+ * @author Alex Nenadic
+ */
+//FIXME this file assumes we're writing out as XML
+@SuppressWarnings("serial")
+public class ImportServiceDescriptionsFromFileAction extends AbstractAction{
+	private static final String EXTENSION = ".xml";
+	private static final String IMPORT_SERVICES = "Import services from file";
+	private static final String SERVICE_IMPORT_DIR_PROPERTY = "serviceImportDir";
+	private static final Logger logger = Logger.getLogger(ExportServiceDescriptionsAction.class);
+
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public ImportServiceDescriptionsFromFileAction(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super(IMPORT_SERVICES);
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	private static final Object[] CHOICES = { "Add to current services",
+			"Replace current services", "Cancel" };
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		JComponent parentComponent = null;
+		if (e.getSource() instanceof JComponent)
+			parentComponent = (JComponent) e.getSource();
+
+		if (ExportServiceDescriptionsAction.INHIBIT) {
+			showMessageDialog(parentComponent,
+					"Operation not currently working correctly",
+					"Not Implemented", ERROR_MESSAGE);
+			return;
+		}
+
+		int choice = showOptionDialog(
+				parentComponent,
+				"Do you want to add the imported services to the current ones or replace the current ones?",
+				"Import services", YES_NO_CANCEL_OPTION, QUESTION_MESSAGE,
+				null, CHOICES, CHOICES[0]);
+
+		if (choice != CANCEL_OPTION) {
+			JFileChooser fileChooser = new JFileChooser();
+			Preferences prefs = Preferences.userNodeForPackage(getClass());
+			String curDir = prefs.get(SERVICE_IMPORT_DIR_PROPERTY, System.getProperty("user.home"));
+
+			fileChooser.setDialogTitle("Select file to import services from");
+
+			fileChooser.setFileFilter(new FileFilter() {
+				@Override
+				public boolean accept(File f) {
+					return f.isDirectory()
+							|| f.getName().toLowerCase().endsWith(EXTENSION);
+				}
+
+				@Override
+				public String getDescription() {
+					return EXTENSION + " files";
+				}
+			});
+
+			fileChooser.setCurrentDirectory(new File(curDir));
+
+			if (fileChooser.showOpenDialog(parentComponent) == APPROVE_OPTION) {
+				prefs.put(SERVICE_IMPORT_DIR_PROPERTY, fileChooser
+						.getCurrentDirectory().toString());
+				File file = fileChooser.getSelectedFile();
+
+				try {
+					// Did user want to replace or add services?
+					importServices(file, choice == YES_OPTION);
+				} catch (Exception ex) {
+					logger.error(
+							"Service descriptions import: failed to import services from "
+									+ file.getAbsolutePath(), ex);
+					showMessageDialog(parentComponent,
+							"Failed to import services from " + file.getAbsolutePath(), "Error",
+							ERROR_MESSAGE);
+				}
+			}
+		}
+
+		if (parentComponent instanceof JButton)
+			// lose the focus from the button after performing the action
+			parentComponent.requestFocusInWindow();
+	}
+
+	private void importServices(final File file, final boolean addToCurrent)
+			throws Exception {
+		// TODO: Open in separate thread to avoid hanging UI
+
+		if (!addToCurrent)
+			for (ServiceDescriptionProvider provider : new HashSet<>(
+					serviceDescriptionRegistry.getServiceDescriptionProviders()))
+				// remove all configurable service providers
+				if (provider instanceof ConfigurableServiceProvider)
+					serviceDescriptionRegistry
+							.removeServiceDescriptionProvider(provider);
+
+		// import all providers from the file
+		serviceDescriptionRegistry.loadServiceProviders(file);
+		serviceDescriptionRegistry.saveServiceDescriptions();
+	}
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromURLAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromURLAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromURLAction.java
new file mode 100644
index 0000000..0dbbe25
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromURLAction.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.ui.servicepanel.actions;
+
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.QUESTION_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showInputDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.JOptionPane.showOptionDialog;
+
+import java.awt.event.ActionEvent;
+import java.net.URL;
+import java.util.HashSet;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Action to import a list of service descriptions from an URL pointing
+ * to an xml file into the Service Registry. Users have an option to
+ * completely replace the current services or just add the ones from the
+ * file to the current services.
+ *
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class ImportServiceDescriptionsFromURLAction extends AbstractAction{
+	private static final String IMPORT_SERVICES_FROM_URL = "Import services from URL";
+	private static final Logger logger = Logger.getLogger(ExportServiceDescriptionsAction.class);
+
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public ImportServiceDescriptionsFromURLAction(ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super(IMPORT_SERVICES_FROM_URL);
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	private static final Object[] CHOICES = { "Add to current services",
+			"Replace current services", "Cancel" };
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		JComponent parentComponent = null;
+		if (e.getSource() instanceof JComponent)
+			parentComponent = (JComponent) e.getSource();
+
+		if (ExportServiceDescriptionsAction.INHIBIT) {
+			showMessageDialog(parentComponent,
+					"Operation not currently working correctly",
+					"Not Implemented", ERROR_MESSAGE);
+			return;
+		}
+
+		int choice = showOptionDialog(
+				parentComponent,
+				"Do you want to add the imported services to the current ones or replace the current ones?",
+				"Import services", YES_NO_CANCEL_OPTION, QUESTION_MESSAGE,
+				null, CHOICES, CHOICES[0]);
+
+		if (choice != CANCEL_OPTION) {
+			final String urlString = (String) showInputDialog(parentComponent,
+					"Enter the URL of the service descriptions file to import",
+					"Service Descriptions URL", QUESTION_MESSAGE, null, null,
+					"http://");
+			try {
+				if (urlString != null && !urlString.isEmpty())
+					// Did user want to replace or add services?
+					importServices(urlString, choice == YES_OPTION);
+			} catch (Exception ex) {
+				logger.error(
+						"Service descriptions import: failed to import services from "
+								+ urlString, ex);
+				showMessageDialog(parentComponent,
+						"Failed to import services from " + urlString, "Error",
+						ERROR_MESSAGE);
+			}
+		}
+
+		if (parentComponent instanceof JButton)
+			// lose the focus from the button after performing the action
+			parentComponent.requestFocusInWindow();
+	}
+
+	private void importServices(final String urlString, final boolean addToCurrent)
+			throws Exception {
+		// TODO: Open in separate thread to avoid hanging UI
+		URL url = new URL(urlString);
+
+		if (!addToCurrent)
+			for (ServiceDescriptionProvider provider : new HashSet<>(
+					serviceDescriptionRegistry.getServiceDescriptionProviders()))
+				// remove all configurable service providers
+				if (provider instanceof ConfigurableServiceProvider)
+					serviceDescriptionRegistry
+							.removeServiceDescriptionProvider(provider);
+
+		// import all providers from the URL
+		serviceDescriptionRegistry.loadServiceProviders(url);
+		serviceDescriptionRegistry.saveServiceDescriptions();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RefreshProviderRegistryAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RefreshProviderRegistryAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RefreshProviderRegistryAction.java
new file mode 100644
index 0000000..9c4c84b
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RefreshProviderRegistryAction.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.actions;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+/**
+ * Action for refreshing the service provider registry.
+ * <p>
+ * This would typically re-parse WSDLs, etc.
+ * 
+ * @see ServiceDescriptionRegistry#refresh()
+ * @author Stian Soiland-Reyes
+ */
+@SuppressWarnings("serial")
+public class RefreshProviderRegistryAction extends AbstractAction {
+	private static final String REFRESH = "Reload services";
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public RefreshProviderRegistryAction(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super(REFRESH);
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		serviceDescriptionRegistry.refresh();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveDefaultServicesAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveDefaultServicesAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveDefaultServicesAction.java
new file mode 100644
index 0000000..b6ba606
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveDefaultServicesAction.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.actions;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+@SuppressWarnings("serial")
+public class RemoveDefaultServicesAction extends AbstractAction {
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public RemoveDefaultServicesAction(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super("Remove default service providers");
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		for (ServiceDescriptionProvider provider : serviceDescriptionRegistry
+				.getDefaultServiceDescriptionProviders()) {
+			if (!(provider instanceof ConfigurableServiceProvider))
+				continue;
+			serviceDescriptionRegistry
+					.removeServiceDescriptionProvider(provider);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveUserServicesAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveUserServicesAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveUserServicesAction.java
new file mode 100644
index 0000000..bf0a771
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveUserServicesAction.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.actions;
+
+import static javax.swing.JOptionPane.YES_NO_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.JLabel;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+@SuppressWarnings("serial")
+public class RemoveUserServicesAction extends AbstractAction {
+	private static final String CONFIRM_MESSAGE = "You are about to remove all services you have added. <br>"
+			+ "Are you <b>really</b> sure you want to do this?";
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public RemoveUserServicesAction(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super("Remove all user added service providers");
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		int option = showConfirmDialog(null, new JLabel("<html><body>"
+				+ CONFIRM_MESSAGE + "</body></html>"),
+				"Confirm service deletion", YES_NO_OPTION);
+
+		if (option == YES_OPTION)
+			for (ServiceDescriptionProvider provider : serviceDescriptionRegistry
+					.getUserAddedServiceProviders())
+				serviceDescriptionRegistry
+						.removeServiceDescriptionProvider(provider);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RestoreDefaultServicesAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RestoreDefaultServicesAction.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RestoreDefaultServicesAction.java
new file mode 100644
index 0000000..c7071ed
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RestoreDefaultServicesAction.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.actions;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+@SuppressWarnings("serial")
+public class RestoreDefaultServicesAction extends AbstractAction {
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public RestoreDefaultServicesAction(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super("Restore default service providers");
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		for (ServiceDescriptionProvider provider : serviceDescriptionRegistry
+				.getDefaultServiceDescriptionProviders()) {
+			if (!(provider instanceof ConfigurableServiceProvider))
+				continue;
+			serviceDescriptionRegistry.addServiceDescriptionProvider(provider);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigPanel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigPanel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigPanel.java
new file mode 100644
index 0000000..f666877
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigPanel.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.config;
+
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.WEST;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration;
+import net.sf.taverna.t2.workbench.helper.Helper;
+
+@SuppressWarnings("serial")
+public class ServiceDescriptionConfigPanel extends JPanel {
+	private static final String REMOVE_PERMANENTLY = "Allow permanent removal of default service providers";
+	private static final String INCLUDE_DEFAULTS = "Include default service providers";
+
+	private final ServiceDescriptionsConfiguration config;
+	private JCheckBox includeDefaults;
+	private JCheckBox removePermanently;
+	private final ServiceDescriptionRegistry serviceDescRegistry;
+
+	public ServiceDescriptionConfigPanel(ServiceDescriptionsConfiguration config,
+			ServiceDescriptionRegistry serviceDescRegistry) {
+		super(new GridBagLayout());
+		this.config = config;
+		this.serviceDescRegistry = serviceDescRegistry;
+		initialize();
+	}
+
+	private void initialize() {
+		removeAll();
+
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		// Title describing what kind of settings we are configuring here
+		JTextArea descriptionText = new JTextArea(
+				"Configure behaviour of default service providers in Service Panel");
+        descriptionText.setLineWrap(true);
+        descriptionText.setWrapStyleWord(true);
+        descriptionText.setEditable(false);
+        descriptionText.setFocusable(false);
+        descriptionText.setBorder(new EmptyBorder(10, 10, 10, 10));
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.anchor = WEST;
+		gbc.fill = HORIZONTAL;
+		add(descriptionText, gbc);
+
+		includeDefaults = new JCheckBox(INCLUDE_DEFAULTS);
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.anchor = WEST;
+		gbc.fill = NONE;
+        gbc.insets = new Insets(10, 0, 0, 0);
+		add(includeDefaults, gbc);
+
+		removePermanently = new JCheckBox(REMOVE_PERMANENTLY);
+		gbc.gridx = 0;
+		gbc.gridy = 2;
+        gbc.insets = new Insets(0, 0, 0, 0);
+		add(removePermanently, gbc);
+
+		// Filler
+		gbc.gridx = 0;
+		gbc.gridy = 3;
+		gbc.weighty = 1;
+		gbc.weightx = 1;
+		gbc.fill = GridBagConstraints.BOTH;
+        gbc.insets = new Insets(10, 0, 0, 0);
+		add(createButtonPanel(), gbc);
+
+		setFields(config);
+	}
+
+	/**
+	 * Create the panel to contain the buttons
+	 *
+	 * @return
+	 */
+	private JPanel createButtonPanel() {
+		final JPanel panel = new JPanel();
+
+		/**
+		 * The helpButton shows help about the current component
+		 */
+		JButton helpButton = new JButton(new AbstractAction("Help") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				Helper.showHelp(panel);
+			}
+		});
+		panel.add(helpButton);
+
+		/**
+		 * The resetButton changes the property values shown to those
+		 * corresponding to the configuration currently applied.
+		 */
+		JButton resetButton = new JButton(new AbstractAction("Reset") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				setFields(config);
+			}
+		});
+		panel.add(resetButton);
+
+		/**
+		 * The applyButton applies the shown field values to the
+		 * {@link HttpProxyConfiguration} and saves them for future.
+		 */
+		JButton applyButton = new JButton(new AbstractAction("Apply") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				applySettings();
+				setFields(config);
+			}
+		});
+		panel.add(applyButton);
+
+		return panel;
+	}
+
+	protected void applySettings() {
+		// Include default service providers
+		config.setIncludeDefaults(includeDefaults.isSelected());
+		for (ServiceDescriptionProvider provider : serviceDescRegistry
+				.getDefaultServiceDescriptionProviders()) {
+			if (! (provider instanceof ConfigurableServiceProvider))
+				continue;
+			if (config.isIncludeDefaults())
+				serviceDescRegistry.addServiceDescriptionProvider(provider);
+			else
+				serviceDescRegistry.removeServiceDescriptionProvider(provider);
+		}
+
+		// Allow permanent removal of default service providers
+		config.setRemovePermanently(removePermanently.isSelected());
+	}
+
+	/**
+	 * Set the shown configuration field values to those currently in use
+	 * (i.e. last saved configuration).
+	 *
+	 */
+	private void setFields(ServiceDescriptionsConfiguration configurable) {
+		includeDefaults.setSelected(configurable.isIncludeDefaults());
+		removePermanently.setSelected(configurable.isRemovePermanently());
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/ExpandableOnDemandLoadedListCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/ExpandableOnDemandLoadedListCellRenderer.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/ExpandableOnDemandLoadedListCellRenderer.java
new file mode 100644
index 0000000..a223fc8
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/ExpandableOnDemandLoadedListCellRenderer.java
@@ -0,0 +1,220 @@
+package net.sf.taverna.biocatalogue.ui.search_results;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.ListCellRenderer;
+import javax.swing.SwingUtilities;
+
+import net.sf.taverna.biocatalogue.model.LoadingResource;
+import net.sf.taverna.biocatalogue.model.Resource;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+
+
+/**
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public abstract class ExpandableOnDemandLoadedListCellRenderer extends JPanel implements ListCellRenderer
+{
+  protected static final int DESCRIPTION_MAX_LENGTH_COLLAPSED = 90;
+  protected static final int DESCRIPTION_MAX_LENGTH_EXPANDED = 500;
+  
+  protected static final int LINE_LENGTH = 90;
+  
+  
+  protected static final int TOOLTIP_DESCRIPTION_LENGTH = 150;
+  protected static final int TOOLTIP_LINE_LENGTH = 60;
+  
+  // list cells are not repainted by Swing by default - hence to use animated GIFs inside cells,
+  // need to have a special class that takes care of changing the frames as necessary
+  protected JLabel loaderBarAnimationOrange = new JLabel(ResourceManager.getImageIcon(ResourceManager.BAR_LOADER_ORANGE), JLabel.CENTER);
+  protected JLabel loaderBarAnimationGrey = new JLabel(ResourceManager.getImageIcon(ResourceManager.BAR_LOADER_GREY), JLabel.CENTER);
+  protected JLabel loaderBarAnimationGreyStill = new JLabel (ResourceManager.getImageIcon(ResourceManager.BAR_LOADER_GREY_STILL), JLabel.CENTER);
+  
+  
+  protected JPanel thisPanel;
+  private List<Class<? extends ResourceLink>> resourceClasses;
+  
+  
+  protected JLabel jlExpand;
+  protected static Rectangle expandRect;
+    
+  public ExpandableOnDemandLoadedListCellRenderer()
+  {
+    this.thisPanel = this;
+    
+    resourceClasses = new ArrayList<Class<? extends ResourceLink>>();
+    try {
+      for (Resource.TYPE resourceType : Resource.TYPE.values()) {
+        resourceClasses.add(resourceType.getXmlBeansGeneratedClass());
+      }
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+    }
+      
+  }
+  
+  
+  public static Rectangle getExpandRect() {
+    return (expandRect == null ? new Rectangle() : expandRect);
+  }
+  
+  
+  public Component getListCellRendererComponent(JList list, Object itemToRender, int itemIndex, boolean isSelected, boolean cellHasFocus)
+  {
+    // the same instance of the cell renderer is used for all cells, so
+    // need to remove everything from the current panel to ensure clean
+    // painting of the current cell
+    this.removeAll();
+    
+    // GET THE DATA
+    
+    // LoadingResource is a placeholder for the detailed data on the resource --
+    // it is being quickly fetched from the API and contanins just the name and the URL
+    // of the actual resource;
+    // 
+    // these entries will be placed into the list when the initial part of the search
+    // is complete, further details will be loaded asynchronously and inserted into
+    // the same area
+    if (itemToRender instanceof LoadingResource) {
+      prepareInitiallyLoadingEntry(itemToRender);
+    }
+    
+    // real data about some resource: details, but in the collapsed form
+    else if (isInstanceOfResourceType(itemToRender)) {
+      prepareLoadedEntry(itemToRender, isSelected);
+    }
+       
+    // error case - unknown resource...
+    else {
+      prepareUnknownResourceTypeEntry();
+    }
+    
+    
+    // MAKE SURE CELL SELECTION WORKS AS DESIRED
+    if (shouldBeHidden(itemToRender)) {
+        this.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(3, 4, 3, 4, list.getBackground()),
+                BorderFactory.createLineBorder(Color.DARK_GRAY)));
+        setBackground(list.getBackground());
+        setForeground(list.getBackground());
+    }
+    else if (isSelected) {
+      this.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(3, 4, 3, 4, list.getBackground()),
+                                                        BorderFactory.createLineBorder(Color.DARK_GRAY)));
+        setBackground(Color.decode("#BAE8FF"));         // very light blue colour
+        setForeground(list.getSelectionForeground());
+    } else {
+        this.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(3, 4, 3, 4, list.getBackground()),
+                                                          BorderFactory.createLineBorder(Color.DARK_GRAY)));
+        setBackground(Color.WHITE);
+        setForeground(list.getForeground());
+    }
+    
+    this.revalidate();
+    
+    if (expandRect == null && jlExpand != null) {
+      SwingUtilities.invokeLater(new Runnable() {
+        public void run() {
+          expandRect = jlExpand.getBounds();
+          expandRect.x -= Math.abs(thisPanel.getBounds().x);
+        }
+      });
+    }
+    
+    return (this);
+  }
+  
+  
+  /**
+   * This entry can be in one of two states:
+   * -- containing only the name of the resource and NOT loading further details;
+   * -- containing only the name of the resource and LOADING further details.
+   * 
+   * @param itemToRender
+   * @return
+   */
+  protected abstract GridBagConstraints prepareInitiallyLoadingEntry(Object itemToRender);
+  
+  
+  /**
+   * 
+   * @param itemToRender
+ * @param isSelected 
+   * @param expandedView <code>true</code> to indicate that this method generates the top
+   *                     fragment of the expanded list entry for this SOAP operation / REST method.
+   * @return
+   */
+  protected abstract GridBagConstraints prepareLoadedEntry(Object itemToRender, boolean isSelected);
+  
+  
+  private void prepareUnknownResourceTypeEntry()
+  {
+    this.setLayout(new GridBagLayout());
+    GridBagConstraints c = new GridBagConstraints();
+    c.anchor = GridBagConstraints.NORTHWEST;
+    c.fill = GridBagConstraints.HORIZONTAL;
+    
+    c.gridx = 0;
+    c.gridy = 0;
+    c.weightx = 0;
+    c.insets = new Insets(8, 6, 6, 3);
+    this.add(new JLabel(ResourceManager.getImageIcon(ResourceManager.UNKNOWN_RESOURCE_TYPE_ICON)), c);
+    
+    c.gridx++;
+    c.weightx = 1.0;
+    c.insets = new Insets(8, 3, 6, 3);
+    this.add(new JLabel("<html><font color=\"#FF0000\">ERROR: This item shoulnd't have been here...</font></html>"), c);
+    
+    c.gridx = 1;
+    c.gridy++;
+    c.gridheight = 1;
+    c.weightx = 1.0;
+    c.weighty = 0;
+    c.insets = new Insets(3, 3, 3, 3);
+    this.add(new JLabel(" "), c);
+    
+    c.gridy++;
+    c.insets = new Insets(3, 3, 8, 3);
+    this.add(new JLabel(" "), c);
+  }
+  
+  
+  private boolean isInstanceOfResourceType(Object itemToRender)
+  {
+    for (Class<? extends ResourceLink> resourceClass : resourceClasses) {
+      if (resourceClass.isInstance(itemToRender)) {
+        return (true);
+      }
+    }
+    
+    return (false);
+  }
+  
+  protected TYPE determineResourceType(Object itemToRender) {
+    if (itemToRender instanceof ResourceLink) {
+      return (Resource.getResourceTypeFromResourceURL(((ResourceLink)itemToRender).getHref()));
+    }
+    else {
+      return (null);
+    }
+  }
+  
+  abstract boolean shouldBeHidden(Object itemToRender);
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/RESTMethodListCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/RESTMethodListCellRenderer.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/RESTMethodListCellRenderer.java
new file mode 100644
index 0000000..64bdfbf
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/RESTMethodListCellRenderer.java
@@ -0,0 +1,248 @@
+package net.sf.taverna.biocatalogue.ui.search_results;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JLabel;
+
+import net.sf.taverna.biocatalogue.model.LoadingResource;
+import net.sf.taverna.biocatalogue.model.Resource;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.t2.lang.ui.ReadOnlyTextArea;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check.ServiceMonitoringStatusInterpreter;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.biocatalogue.x2009.xml.rest.RestMethod;
+import org.biocatalogue.x2009.xml.rest.RestParameter;
+import org.biocatalogue.x2009.xml.rest.RestRepresentation;
+import org.biocatalogue.x2009.xml.rest.Service;
+import org.biocatalogue.x2009.xml.rest.RestMethod.Ancestors;
+
+
+/**
+ * 
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class RESTMethodListCellRenderer extends ExpandableOnDemandLoadedListCellRenderer
+{
+  private JLabel jlTypeIcon = new JLabel();
+  private JLabel jlItemStatus = new JLabel();
+  private JLabel jlItemTitle = new JLabel("X");
+  private JLabel jlPartOf = new JLabel("X");
+  private ReadOnlyTextArea jtDescription = new ReadOnlyTextArea(5, 80);
+  private JLabel jlMethodType = new JLabel("X");
+  private JLabel jlUrlTemplate = new JLabel("X");
+  private JLabel jlMethodParameters = new JLabel("X");
+  private JLabel jlInputRepresentations = new JLabel("X");
+  private JLabel jlOutputRepresentations = new JLabel("X");
+  
+  private GridBagConstraints c;
+  
+  private static Resource.TYPE resourceType = Resource.TYPE.RESTMethod;
+
+  
+  
+  public RESTMethodListCellRenderer() {
+	   jlItemTitle.setFont(jlItemTitle.getFont().deriveFont(Font.PLAIN, jlItemTitle.getFont().getSize() + 2));
+	    jtDescription.setOpaque(false);
+	    jtDescription.setLineWrap(true);
+	    jtDescription.setWrapStyleWord(true);
+  }
+  
+  
+  
+  /**
+   * This entry can be in one of two states:
+   * -- containing only the name of the resource and NOT loading further details;
+   * -- containing only the name of the resource and LOADING further details.
+   * 
+   * @param itemToRender
+   * @return
+   */
+  protected GridBagConstraints prepareInitiallyLoadingEntry(Object itemToRender)
+  {
+    LoadingResource resource = (LoadingResource)itemToRender;
+    
+    jlTypeIcon.setIcon(resourceType.getIcon());
+    jlItemStatus.setIcon(ResourceManager.getImageIcon(ResourceManager.SERVICE_STATUS_UNCHECKED_ICON_LARGE));
+    
+    jlItemTitle.setText("<html>" + StringEscapeUtils.escapeHtml(Resource.getDisplayNameForResource(resource)) + "<font color=\"gray\"><i>- fetching more information</i></font></html>");
+    
+    jlPartOf.setText("");
+    jtDescription.setText(" ");
+    jlMethodType.setText(" ");
+    jlUrlTemplate.setText(" ");
+    jlMethodParameters.setText(" ");
+    jlInputRepresentations.setText(" ");
+    jlOutputRepresentations.setText(" ");
+    
+    return (arrangeLayout());
+  }
+  
+  
+  /**
+   * 
+   * @param itemToRender
+   * @param expandedView <code>true</code> to indicate that this method generates the top
+   *                     fragment of the expanded list entry for this SOAP operation / REST method.
+   * @return
+   */
+  protected GridBagConstraints prepareLoadedEntry(Object itemToRender, boolean selected)
+  {
+    RestMethod restMethod = (RestMethod)itemToRender;;
+    
+    Ancestors ancestors = restMethod.getAncestors();
+    Service service = ancestors.getService();
+    String title = "<html>" + StringEscapeUtils.escapeHtml(Resource.getDisplayNameForResource(restMethod));
+
+    if (restMethod.isSetArchived() || service.isSetArchived()) {
+    	jlTypeIcon.setIcon(ResourceManager.getImageIcon(ResourceManager.WARNING_ICON));
+    	title = title + "<i> - this operation is archived and probably cannot be used</i></html>";
+    }
+    else {
+    	jlTypeIcon.setIcon(resourceType.getIcon());
+    	title = title + "</html>";
+    }
+    
+    // service status
+    jlItemStatus.setIcon(ServiceMonitoringStatusInterpreter.getStatusIcon(service, false));
+    jlItemTitle.setText(title);
+     
+    jlPartOf.setText("<html><b>Part of: </b>" + restMethod.getAncestors().getRestService().getResourceName() + "</html>");
+    
+    String strDescription = (restMethod.getDescription() == null || restMethod.getDescription().length() == 0 ?
+                             "No description" :
+                            	 Util.stripAllHTML(restMethod.getDescription()));
+    jtDescription.setText(strDescription);
+    
+    jlMethodType.setText("<html><b>HTTP Method: </b>" + StringEscapeUtils.escapeHtml(restMethod.getHttpMethodType().toString()) + "</html>");
+    jlUrlTemplate.setText("<html><b>URL Template: </b>" + StringEscapeUtils.escapeHtml(restMethod.getUrlTemplate()) + "</html>");
+    
+    List<String> names = new ArrayList<String>();
+    for (RestParameter restParameter : restMethod.getInputs().getParameters().getRestParameterList()) {
+      names.add(restParameter.getName() + (restParameter.getIsOptional() ? " (optional)" : ""));
+    }
+    
+    String methodParameters = "<b>" + names.size() + " " + Util.pluraliseNoun("Parameter", names.size()) + "</b>";
+    if(names.size() > 0) {
+      methodParameters += ": " + StringEscapeUtils.escapeHtml(Util.ensureLineLengthWithinString(Util.join(names, ", "), LINE_LENGTH, false));
+    }
+    methodParameters = "<html>" + methodParameters + "</html>";
+    jlMethodParameters.setText(methodParameters);
+    
+       names.clear();
+      for (RestRepresentation restRepresentation : restMethod.getInputs().getRepresentations().getRestRepresentationList()) {
+        names.add(restRepresentation.getContentType());
+      }
+      
+      String inputRepresentations = "<b>" + names.size() + " " + Util.pluraliseNoun("Input representation", names.size()) + "</b>";
+      if(names.size() > 0) {
+        inputRepresentations += ": " + StringEscapeUtils.escapeHtml(Util.ensureLineLengthWithinString(Util.join(names, ", "), LINE_LENGTH, false));
+      }
+      inputRepresentations = "<html>" + inputRepresentations + "</html>";
+      
+      jlInputRepresentations.setText(inputRepresentations);
+
+      // output representations
+      names.clear();
+      for (RestRepresentation restRepresentation : restMethod.getOutputs().getRepresentations().getRestRepresentationList()) {
+        names.add(restRepresentation.getContentType());
+      }
+      
+      String outputRepresentations = "<b>" + names.size() + " " + Util.pluraliseNoun("Output representation", names.size()) + "</b>";
+      if(names.size() > 0) {
+        outputRepresentations += ": " + StringEscapeUtils.escapeHtml(Util.ensureLineLengthWithinString(Util.join(names, ", "), LINE_LENGTH, false));
+      }
+      outputRepresentations = "<html>" + outputRepresentations + "</html>";
+      
+      jlOutputRepresentations.setText(outputRepresentations);
+    
+    return (arrangeLayout());
+  }
+  
+  
+  /**
+   * @return Final state of the {@link GridBagConstraints} instance
+   *         that was used to lay out components in the panel.
+   */
+  private GridBagConstraints arrangeLayout()
+  {
+    // POPULATE PANEL WITH PREPARED COMPONENTS
+    this.setLayout(new GridBagLayout());
+    c = new GridBagConstraints();
+    c.anchor = GridBagConstraints.NORTHWEST;
+    c.fill = GridBagConstraints.HORIZONTAL;
+    
+    c.gridx = 0;
+    c.gridy = 0;
+    c.weightx = 0;
+    c.insets = new Insets(8, 6, 6, 3);
+    this.add(jlTypeIcon, c);
+    
+    c.gridx++;
+    c.weightx = 1.0;
+    c.insets = new Insets(8, 3, 6, 3);
+    this.add(jlItemTitle, c);
+    
+    c.gridx++;
+    c.gridheight = 8;
+    c.weightx = 0;
+    c.weighty = 1.0;
+    this.add(jlItemStatus, c);
+    
+    c.gridx = 1;
+    c.gridy++;
+    c.gridheight = 1;
+    c.weightx = 1.0;
+    c.weighty = 0;
+    this.add(jlPartOf, c);
+    
+    c.fill = GridBagConstraints.NONE;
+    c.gridy++;
+    this.add(jtDescription, c);
+    
+    c.fill = GridBagConstraints.HORIZONTAL;
+    c.gridy++;
+    this.add(jlMethodType, c);
+    
+    c.gridy++;
+    this.add(jlUrlTemplate, c);
+    
+    c.gridy++;
+    this.add(jlMethodParameters, c);
+    
+    c.gridy++;
+    this.add(jlInputRepresentations, c);
+    
+    c.gridy++;
+    this.add(jlOutputRepresentations, c);
+    return (c);
+  }
+  
+@Override
+boolean shouldBeHidden(Object itemToRender) {
+	if (!(itemToRender instanceof RestMethod)) {
+		return false;
+	}
+    RestMethod restMethod = (RestMethod)itemToRender;;
+    
+    Ancestors ancestors = restMethod.getAncestors();
+    Service service = ancestors.getService();
+    String title = Resource.getDisplayNameForResource(restMethod);
+
+    if (restMethod.isSetArchived() || service.isSetArchived()) {
+    	return true;
+    }
+    else {
+    	return false;
+    }
+
+}
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SOAPOperationListCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SOAPOperationListCellRenderer.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SOAPOperationListCellRenderer.java
new file mode 100644
index 0000000..77939c7
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SOAPOperationListCellRenderer.java
@@ -0,0 +1,257 @@
+package net.sf.taverna.biocatalogue.ui.search_results;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+
+import net.sf.taverna.biocatalogue.model.LoadingExpandedResource;
+import net.sf.taverna.biocatalogue.model.LoadingResource;
+import net.sf.taverna.biocatalogue.model.Resource;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.t2.lang.ui.ReadOnlyTextArea;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check.ServiceMonitoringStatusInterpreter;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
+import org.biocatalogue.x2009.xml.rest.Service;
+import org.biocatalogue.x2009.xml.rest.ServiceTechnologyType;
+import org.biocatalogue.x2009.xml.rest.SoapInput;
+import org.biocatalogue.x2009.xml.rest.SoapOperation;
+import org.biocatalogue.x2009.xml.rest.SoapOutput;
+import org.biocatalogue.x2009.xml.rest.SoapService;
+import org.biocatalogue.x2009.xml.rest.Service.ServiceTechnologyTypes;
+import org.biocatalogue.x2009.xml.rest.ServiceTechnologyType.Enum;
+import org.biocatalogue.x2009.xml.rest.SoapOperation.Ancestors;
+
+
+/**
+ * 
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class SOAPOperationListCellRenderer extends ExpandableOnDemandLoadedListCellRenderer
+{
+	
+	private JLabel jlTypeIcon = new JLabel();
+  private JLabel jlItemStatus = new JLabel();
+  private JLabel jlItemTitle = new JLabel("X");
+  private JLabel jlPartOf = new JLabel("X");
+  private JLabel jlWsdlLocation = new JLabel("X");
+  private ReadOnlyTextArea jtDescription = new ReadOnlyTextArea(5,80);
+  private JLabel jlSoapInputs = new JLabel("X");
+  private JLabel jlSoapOutputs = new JLabel("X");
+  
+  private GridBagConstraints c;
+  
+  private static Resource.TYPE resourceType = Resource.TYPE.SOAPOperation;
+  
+  
+  public SOAPOperationListCellRenderer() {
+    jlItemTitle.setFont(jlItemTitle.getFont().deriveFont(Font.PLAIN, jlItemTitle.getFont().getSize() + 2));
+    jtDescription.setOpaque(false);
+    jtDescription.setLineWrap(true);
+    jtDescription.setWrapStyleWord(true);
+  }
+  
+  
+  /**
+   * This entry can be in one of two states:
+   * -- containing only the name of the resource and NOT loading further details;
+   * -- containing only the name of the resource and LOADING further details.
+   * 
+   * @param itemToRender
+   * @return
+   */
+  protected GridBagConstraints prepareInitiallyLoadingEntry(Object itemToRender)
+  {
+    LoadingResource resource = (LoadingResource)itemToRender;
+    
+    jlTypeIcon.setIcon(resourceType.getIcon());
+    jlItemStatus.setIcon(ResourceManager.getImageIcon(ResourceManager.SERVICE_STATUS_UNCHECKED_ICON_LARGE));
+       
+    jlItemTitle.setText("<html>" + StringEscapeUtils.escapeHtml(Resource.getDisplayNameForResource(resource)) + "<font color=\"gray\"><i>- fetching more information</i></font></html>");
+   
+    jlPartOf.setText(" ");
+    jlWsdlLocation.setText(" ");
+    jtDescription.setText("");
+    jlSoapInputs.setText(" ");
+    jlSoapOutputs.setText(" ");
+   
+    return (arrangeLayout());
+  }
+  
+  
+  /**
+   * 
+   * @param itemToRender
+ * @param selected 
+   * @param expandedView <code>true</code> to indicate that this method generates the top
+   *                     fragment of the expanded list entry for this SOAP operation / REST method.
+   * @return
+   */
+  protected GridBagConstraints prepareLoadedEntry(Object itemToRender, boolean selected)
+  {
+    SoapOperation soapOp = (SoapOperation)itemToRender;
+    
+    Ancestors ancestors = soapOp.getAncestors();
+    SoapService soapService = ancestors.getSoapService();
+    Service service = ancestors.getService();
+    String title = StringEscapeUtils.escapeHtml(Resource.getDisplayNameForResource(soapOp));
+    
+    if (soapOp.isSetArchived() || service.isSetArchived()) {
+    	jlTypeIcon.setIcon(ResourceManager.getImageIcon(ResourceManager.WARNING_ICON));
+    	title = "<html>" + title + "<i> - this operation is archived and probably cannot be used</i></html>";
+    } else if (isSoapLab(service)) {
+       	jlTypeIcon.setIcon(ResourceManager.getImageIcon(ResourceManager.WARNING_ICON));
+    	title = "<html>" + title + "<i> - this operation can only be used as part of a SoapLab service</i></html>";
+    }
+    else {
+    	jlTypeIcon.setIcon(resourceType.getIcon());
+    	title = "<html>" + title + "</html>";
+   }
+    
+    // service status
+    jlItemStatus.setIcon(ServiceMonitoringStatusInterpreter.getStatusIcon(service, false));
+    jlItemTitle.setText(title);
+    
+    jlPartOf.setText("<html><b>Part of: </b>" + StringEscapeUtils.escapeHtml(soapOp.getAncestors().getSoapService().getResourceName()) + "</html>");
+    
+    jlWsdlLocation.setText("<html><b>WSDL location: </b>" + soapService.getWsdlLocation() + "</html>");
+    
+        String strDescription = (soapOp.getDescription() == null || soapOp.getDescription().length() == 0 ?
+                             "No description" :
+                            	 Util.stripAllHTML(soapOp.getDescription()));
+    
+            jtDescription.setText(strDescription);
+    
+    // add SOAP inputs
+    List<String> names = new ArrayList<String>();
+    for (SoapInput soapInput : soapOp.getInputs().getSoapInputList()) {
+      names.add(soapInput.getName());
+    }
+    
+    String soapInputs = "<b>" + names.size() + " " + Util.pluraliseNoun("Input", names.size()) + "</b>";
+    if(names.size() > 0) {
+      soapInputs += ": " + StringEscapeUtils.escapeHtml(Util.ensureLineLengthWithinString(Util.join(names, ", "), LINE_LENGTH, false));
+    }
+    soapInputs = "<html>" + soapInputs + "</html>";
+    jlSoapInputs.setText(soapInputs);
+    
+    c.gridy++;
+    this.add(jlSoapInputs, c);
+    
+    
+    // add SOAP outputs
+    names.clear();
+    for (SoapOutput soapOutput : soapOp.getOutputs().getSoapOutputList()) {
+      names.add(soapOutput.getName());
+    }
+    
+    String soapOutputs = "<b>" + names.size() + " " + Util.pluraliseNoun("Output", names.size()) + "</b>";
+    if(names.size() > 0) {
+      soapOutputs += ": " + StringEscapeUtils.escapeHtml(Util.ensureLineLengthWithinString(Util.join(names, ", "), LINE_LENGTH, false));
+    }
+    soapOutputs = "<html>" + soapOutputs + "</html>";
+    jlSoapOutputs.setText(soapOutputs);
+   
+    return (arrangeLayout());
+  }
+
+
+private boolean isSoapLab(Service service) {
+	boolean result = false;
+	ServiceTechnologyTypes serviceTechnologyTypes = service.getServiceTechnologyTypes();
+	if (serviceTechnologyTypes == null) {
+		return result;
+	}
+	List<Enum> typeList = serviceTechnologyTypes.getTypeList();
+	if (typeList == null) {
+		return result;
+	}
+	result = typeList.contains(ServiceTechnologyType.SOAPLAB);
+	return result;
+}
+  
+  
+  /**
+   * @return Final state of the {@link GridBagConstraints} instance
+   *         that was used to lay out components in the panel.
+   */
+  private GridBagConstraints arrangeLayout()
+  {
+	   // POPULATE PANEL WITH PREPARED COMPONENTS
+	    this.setLayout(new GridBagLayout());
+	    c = new GridBagConstraints();
+	    c.anchor = GridBagConstraints.NORTHWEST;
+	    c.fill = GridBagConstraints.HORIZONTAL;
+	    
+	    c.gridx = 0;
+	    c.gridy = 0;
+	    c.weightx = 0;
+	    c.insets = new Insets(8, 6, 6, 3);
+	    this.add(jlTypeIcon, c);
+	    
+	    c.gridx++;
+	    c.weightx = 1.0;
+	    c.insets = new Insets(8, 3, 6, 3);
+	    this.add(jlItemTitle, c);
+	    
+	    c.gridx++;
+	    c.gridheight = 7;
+	    c.weightx = 0;
+	    c.weighty = 1.0;
+	    this.add(jlItemStatus, c);
+	    
+	    c.gridx = 1;
+	    c.gridy++;
+	    c.gridheight = 1;
+	    c.weightx = 0;
+	    c.weighty = 0;
+	    this.add(jlPartOf, c);
+	    
+	    c.gridy++;
+	    this.add(jlWsdlLocation, c);
+	    
+	    c.fill = GridBagConstraints.NONE;
+	    c.gridy++;
+	    this.add(jtDescription, c);
+	    
+	    c.fill = GridBagConstraints.HORIZONTAL;
+	    c.gridy++;
+	    this.add(jlSoapInputs, c);
+	    
+	    c.fill = GridBagConstraints.HORIZONTAL;
+	    c.gridy++;
+	    this.add(jlSoapOutputs, c);
+	    
+	    return (c);
+  }
+  
+@Override
+boolean shouldBeHidden(Object itemToRender) {
+	if (!(itemToRender instanceof SoapOperation)) {
+		return false;
+	}
+	SoapOperation soapOp = (SoapOperation) itemToRender;
+	   Ancestors ancestors = soapOp.getAncestors();
+	    Service service = ancestors.getService();
+	    if (soapOp.isSetArchived() || service.isSetArchived()) {
+	    	return true;
+	    } else if (isSoapLab(service)) {
+	       	return true;
+	    }
+	    else {
+	    	return false;
+	   }
+
+}
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsListingPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsListingPanel.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsListingPanel.java
new file mode 100644
index 0000000..c88de40
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsListingPanel.java
@@ -0,0 +1,870 @@
+package net.sf.taverna.biocatalogue.ui.search_results;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.AdjustmentEvent;
+import java.awt.event.AdjustmentListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.BorderFactory;
+import javax.swing.DefaultListModel;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JToolBar;
+import javax.swing.ListCellRenderer;
+import javax.swing.ListModel;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import net.sf.taverna.biocatalogue.model.BioCataloguePluginConstants;
+import net.sf.taverna.biocatalogue.model.LoadingExpandedResource;
+import net.sf.taverna.biocatalogue.model.LoadingResource;
+import net.sf.taverna.biocatalogue.model.Resource;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+import net.sf.taverna.biocatalogue.model.search.SearchInstance;
+import net.sf.taverna.biocatalogue.ui.JWaitDialog;
+import net.sf.taverna.t2.lang.ui.ModelMap;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponent;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.Integration;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check.ServiceHealthChecker;
+import net.sf.taverna.t2.workbench.MainWindow;
+import net.sf.taverna.t2.workbench.ModelMapConstants;
+import net.sf.taverna.t2.workbench.ui.Workbench;
+import net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI;
+
+import org.apache.log4j.Logger;
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+import org.biocatalogue.x2009.xml.rest.RestMethod;
+import org.biocatalogue.x2009.xml.rest.Service;
+import org.biocatalogue.x2009.xml.rest.ServiceTechnologyType;
+
+/**
+ * This class is responsible for producing search results listing panel. It only
+ * shows a single listing for a specified type. Multiple types are handled by
+ * having different tabs in {@link SearchResultsMainPanel} with instances of
+ * this class in each.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class SearchResultsListingPanel extends JPanel implements MouseListener,
+		SearchResultsRenderer, MouseMotionListener {
+	public static final int SEARCH_STATUS_TOOLTIP_LINE_LENGTH = 65;
+
+	private static final Logger logger = Logger.getLogger(SearchResultsListingPanel.class);
+	private final SearchResultsMainPanel parentMainSearchResultsPanel;
+
+	// currently displayed search results
+	SearchInstance searchInstance;
+
+	// main UI components
+	private SearchResultsListingPanel thisPanel;
+	private DefaultListModel resultsListingModel;
+	private JList jlResultsListing;
+	private JScrollPane spResultsListing;
+
+	// contextual menu
+	private JPopupMenu contextualMenu;
+	private Action addToServicePanelAction;
+	private Action addToWorkflowDiagramAction;
+	private Action openInBioCatalogueAction;
+	private Action doHealthCheckAction;
+	private Action addAllOperationsToServicePanelAction;
+
+	// search status and actions on selected items in the list
+	private JToolBar tbSelectedItemActions;
+	protected JPanel jpSearchStatus;
+	private JLabel jlSearchStatus;
+
+	// this is used for previewing items from the result listing through
+	// contextual menu -
+	// value will be updated by mouse event accordingly
+	private ResourceLink potentialObjectToPreview;
+	private final TYPE typeToPreview;
+
+	// Design perspective - some actions require switching to it
+	PerspectiveSPI designPerspective;
+
+	private ListCellRenderer listingCellRenderer;
+	
+	/**
+	 * @param typeToPreview
+	 *            Resource type that will be previewed in this panel.
+	 * @param parentMainSearchResultsPanel
+	 *            Reference to a "parent" of this panel - this is needed to
+	 *            notify the main results panel with the
+	 */
+	public SearchResultsListingPanel(TYPE typeToPreview,
+			SearchResultsMainPanel parentMainSearchResultsPanel) {
+		this.thisPanel = this;
+
+		this.typeToPreview = typeToPreview;
+		listingCellRenderer = this.typeToPreview
+		.getResultListingCellRenderer();
+		this.parentMainSearchResultsPanel = parentMainSearchResultsPanel;
+		MainComponentFactory
+				.getSharedInstance();
+
+		initialiseUI();
+
+		this.setPreferredSize(new Dimension(800, 400));
+	}
+
+	private void initialiseUI() {
+
+		this.addToServicePanelAction = new AbstractAction(
+				"Add to Service Panel",
+				ResourceManager
+						.getImageIcon(ResourceManager.ADD_PROCESSOR_AS_FAVOURITE_ICON)) {
+			// Tooltip
+			{
+				this.putValue(SHORT_DESCRIPTION, "Add selected "
+						+ typeToPreview.getTypeName()
+						+ " to the Service Panel");
+			}
+
+			public void actionPerformed(ActionEvent e) {
+				final JWaitDialog jwd = new JWaitDialog(
+						MainComponent.dummyOwnerJFrame,
+						"Service Catalogue Plugin - Adding "
+								+ typeToPreview.getTypeName(),
+						"<html><center>Please wait for selected "
+								+ typeToPreview.getTypeName()
+								+ " details to be fetched from the Service Catalogue<br>"
+								+ "and to be added into the Service Panel.</center></html>");
+
+				new Thread("Adding " + typeToPreview.getTypeName()
+						+ " into Service Panel") {
+					public void run() {
+						// if it is the expanded that we are looking at, need to extract
+						// the 'associated' object
+						ResourceLink processorResourceToAdd = (potentialObjectToPreview instanceof LoadingExpandedResource ? ((LoadingExpandedResource) potentialObjectToPreview)
+								.getAssociatedObj()
+								: potentialObjectToPreview);
+
+						JComponent insertionOutcome = Integration
+								.insertProcesorIntoServicePanel(processorResourceToAdd);
+						jwd.waitFinished(insertionOutcome);
+						
+						// Switch to Design Perspective
+						switchToDesignPerspective();
+					}
+				}.start();
+
+				// NB! The modal dialog window needs to be made visible after
+				// the background
+				// process (i.e. adding a processor) has already been started!
+				jwd.setVisible(true);
+			}
+		};
+
+		// For a parent Web service, action to add all operations to the Service Panel.
+		// Works for SOAP services at the moment.
+		this.addAllOperationsToServicePanelAction = new AbstractAction(
+				"Add all operations to Service Panel",
+				ResourceManager
+						.getImageIcon(ResourceManager.ADD_ALL_SERVICES_AS_FAVOURITE_ICON)) {
+			// Tooltip
+			{
+				this.putValue(SHORT_DESCRIPTION, "Add all associated services to the Service Panel");
+			}
+
+			public void actionPerformed(ActionEvent e) {
+				final JWaitDialog jwd = new JWaitDialog(
+						MainComponent.dummyOwnerJFrame,
+						"Service Catalogue Plugin - Adding "
+								+ typeToPreview.getTypeName(),
+						"<html><center>Please wait for selected "
+								+ typeToPreview.getTypeName()
+								+ " details to be fetched from the Service Catalogue<br>"
+								+ "and to be added into the Service Panel.</center></html>");
+
+				new Thread("Adding all operations of " + typeToPreview.getTypeName()
+						+ " to the Service Panel") {
+					public void run() {
+						// if it is the expanded that we are looking at, need to extract
+						// the 'associated' object
+						ResourceLink resourceLink = (potentialObjectToPreview instanceof LoadingExpandedResource ? ((LoadingExpandedResource) potentialObjectToPreview)
+								.getAssociatedObj()
+								: potentialObjectToPreview);						
+
+						JComponent insertionOutcome = Integration
+								.insertAllOperationsIntoServicePanel(resourceLink);
+						jwd.waitFinished(insertionOutcome);
+						
+						// Switch to Design Perspective
+						switchToDesignPerspective();
+					}
+				}.start();
+
+				// NB! The modal dialog window needs to be made visible after
+				// the background
+				// process (i.e. adding a processor) has already been started!
+				jwd.setVisible(true);
+			}
+		};
+
+		this.addToWorkflowDiagramAction = new AbstractAction(
+				"Add to workflow",
+				ResourceManager
+						.getImageIcon(ResourceManager.ADD_PROCESSOR_TO_WORKFLOW_ICON)) {
+			// Tooltip
+			{
+				this.putValue(SHORT_DESCRIPTION, "<html>Insert selected "
+						+ typeToPreview.getTypeName()
+						+ " into the current workflow</html>");
+			}
+
+			public void actionPerformed(ActionEvent e) {
+				final JWaitDialog jwd = new JWaitDialog(
+						MainComponent.dummyOwnerJFrame,
+						"Service Catalogue Plugin - Adding "
+								+ typeToPreview.getTypeName(),
+						"<html><center>Please wait for selected "
+								+ typeToPreview.getTypeName()
+								+ " details to be fetched from the Service Catalogue<br>"
+								+ "and to be added into the current workflow.</center></html>");
+
+				new Thread("Adding " + typeToPreview.getTypeName()
+						+ " into workflow") {
+					public void run() {
+						// if it is the expanded that we are looking at, need to extract
+						// the 'associated' object
+						ResourceLink processorResourceToAdd = (potentialObjectToPreview instanceof LoadingExpandedResource ? ((LoadingExpandedResource) potentialObjectToPreview)
+								.getAssociatedObj()
+								: potentialObjectToPreview);
+
+						JComponent insertionOutcome = Integration
+								.insertProcessorIntoCurrentWorkflow(processorResourceToAdd);
+						jwd.waitFinished(insertionOutcome);
+
+						// Switch to Design Perspective
+						switchToDesignPerspective();
+					}
+				}.start();
+
+				// NB! The modal dialog window needs to be made visible after
+				// the background
+				// process (i.e. adding a processor) has already been started!
+				jwd.setVisible(true);
+			}
+		};
+
+		this.openInBioCatalogueAction = new AbstractAction(
+				"Open in the Service Catalogue",
+				ResourceManager
+						.getImageIcon(ResourceManager.OPEN_IN_BIOCATALOGUE_ICON)) {
+			// Tooltip
+			{
+				this.putValue(SHORT_DESCRIPTION, "<html>View selected "
+						+ typeToPreview.getTypeName()
+						+ " on the Service Catalogue Web site.<br>"
+						+ "This will open your standard Web browser.</html>");
+			}
+
+			public void actionPerformed(ActionEvent e) {
+				String hrefString = potentialObjectToPreview.getHref();
+				   try {
+						Desktop.getDesktop().browse(new URI(hrefString));
+					    }
+					    catch (Exception ex) {
+					      logger.error("Failed while trying to open the URL in a standard browser; URL was: " +
+					           hrefString + "\nException was: " + ex + "\n" + ex.getStackTrace());
+					    };
+			}
+		};
+
+		this.doHealthCheckAction = new AbstractAction(
+				"Check monitoring status",
+				ResourceManager
+						.getImageIcon(ResourceManager.EXECUTE_HEALTH_CHECK_ICON)) {
+			// Tooltip
+			{
+				this
+						.putValue(
+								SHORT_DESCRIPTION,
+								"<html>Fetch the latest monitoring data for selected "
+										+ typeToPreview.getTypeName()
+										+ ".<br>"
+										+ "Data will be obtained from the Service Catalogue and displayed in a popup window.</html>");
+			}
+
+			public void actionPerformed(ActionEvent e) {
+				// if it is the expanded that we are looking at, need to extract
+				// the 'associated' object
+				ResourceLink resourceForHealthCheck = (potentialObjectToPreview instanceof LoadingExpandedResource ? ((LoadingExpandedResource) potentialObjectToPreview)
+						.getAssociatedObj()
+						: potentialObjectToPreview);
+
+				ServiceHealthChecker.checkResource(resourceForHealthCheck);
+			}
+		};
+
+		tbSelectedItemActions = new JToolBar(JToolBar.HORIZONTAL);
+		tbSelectedItemActions.setBorderPainted(true);
+		tbSelectedItemActions.setBorder(BorderFactory.createEmptyBorder(5, 5,
+				5, 3));
+		tbSelectedItemActions.setFloatable(false);
+		if (typeToPreview.isSuitableForAddingToServicePanel()) {
+			tbSelectedItemActions.add(addToServicePanelAction);
+		}
+		if (typeToPreview.isSuitableForAddingAllToServicePanel()) {
+			tbSelectedItemActions.add(addAllOperationsToServicePanelAction);
+		}
+		if (typeToPreview.isSuitableForAddingToWorkflowDiagram()) {
+			tbSelectedItemActions.add(addToWorkflowDiagramAction);
+		}
+		if (typeToPreview.isSuitableForHealthCheck()) {
+			tbSelectedItemActions.add(doHealthCheckAction);
+		}
+		tbSelectedItemActions.add(openInBioCatalogueAction);
+
+		// *** Prepare search results status panel ***
+
+		GridBagConstraints c = new GridBagConstraints();
+		jpSearchStatus = new JPanel(new GridBagLayout());
+		c.anchor = GridBagConstraints.WEST;
+		c.weightx = 0;
+		jpSearchStatus.add(tbSelectedItemActions, c);
+
+		jlSearchStatus = new JLabel();
+		jlSearchStatus.setIconTextGap(20);
+		c.weightx = 1.0;
+		c.insets = new Insets(0, 20, 0, 0);
+		jpSearchStatus.add(jlSearchStatus, c);
+
+		if (parentMainSearchResultsPanel.getFilterTreePaneFor(typeToPreview) != null) {
+			Dimension preferredSize = new Dimension(200,
+					parentMainSearchResultsPanel.getFilterTreePaneFor(
+							typeToPreview).getTreeToolbarPreferredSize().height);
+
+			// HACK: due to concurrency issues, sometimes this doesn't work
+			// correctly -
+			// to rectify the problem using the hard-coded value that was
+			// correct at
+			// the time of coding...
+			if (preferredSize.height < 30) {
+				preferredSize.height = 33;
+			}
+
+			jpSearchStatus.setPreferredSize(preferredSize);
+		}
+
+		// *** Create list to hold search results and wrap it into a scroll pane
+		// ***
+		resultsListingModel = new DefaultListModel();
+		jlResultsListing = new JList(resultsListingModel);
+		jlResultsListing.setDoubleBuffered(true);
+		jlResultsListing.setCellRenderer(listingCellRenderer);
+		jlResultsListing.addMouseListener(this);
+		jlResultsListing.addMouseMotionListener(this);
+		jlResultsListing.setBackground(thisPanel.getBackground());
+
+		jlResultsListing.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+		jlResultsListing.addListSelectionListener(new ListSelectionListener() {
+			public void valueChanged(ListSelectionEvent e) {
+				if (!e.getValueIsAdjusting()) {
+					// update value to be used in contextual menu click handler
+					// to act on the just-selected entry
+					potentialObjectToPreview = getResourceSelectedInJList();
+
+					if (potentialObjectToPreview != null) {
+
+						// only enable actions in the menu if the list entry
+						// that is being
+						// clicked on is beyond the initial 'loading' state
+						boolean shown = !isListEntryOnlyWithInitialDetails(potentialObjectToPreview);
+						boolean shownAndNotArchived = shown && !isArchived(potentialObjectToPreview);
+						addToServicePanelAction
+								.setEnabled(shownAndNotArchived);
+						addAllOperationsToServicePanelAction
+						.setEnabled(shownAndNotArchived && !(potentialObjectToPreview instanceof RestMethod));
+						addToWorkflowDiagramAction
+								.setEnabled(shownAndNotArchived);
+						openInBioCatalogueAction
+								.setEnabled(shown);
+						doHealthCheckAction
+								.setEnabled(shown);
+					    
+						return;
+					}
+				}
+
+				// disable actions if nothing is selected in the list or if
+				// selection is still "adjusting"
+				addToServicePanelAction.setEnabled(false);
+				addAllOperationsToServicePanelAction.setEnabled(false);
+				addToWorkflowDiagramAction.setEnabled(false);
+				openInBioCatalogueAction.setEnabled(false);
+				doHealthCheckAction.setEnabled(false);
+			}
+		});
+
+		spResultsListing = new JScrollPane(jlResultsListing);
+		spResultsListing.getVerticalScrollBar().addAdjustmentListener(
+				new AdjustmentListener() {
+					public void adjustmentValueChanged(AdjustmentEvent e) {
+						if (!e.getValueIsAdjusting()) {
+							// load missing details on adjusting the scroll bar
+							//
+							// only start loading more results in case if the
+							// value is "not adjusting" -
+							// this means that the mouse has been released and
+							// is not dragging the scroll bar
+							// any more, so effectively the user has stopped
+							// scrolling
+							checkAllEntriesInTheVisiblePartOfJListAreLoaded();
+						}
+					}
+				});
+
+		// tie components to the class panel itself
+		this.resetSearchResultsListing(true);
+
+		// *** Create CONTEXTUAL MENU ***
+
+		contextualMenu = new JPopupMenu();
+		if (typeToPreview.isSuitableForAddingToServicePanel()) {
+			contextualMenu.add(addToServicePanelAction);
+			contextualMenu.add(addAllOperationsToServicePanelAction);
+		}
+		if (typeToPreview.isSuitableForAddingToWorkflowDiagram()) {
+			contextualMenu.add(addToWorkflowDiagramAction);
+		}
+		if (typeToPreview.isSuitableForHealthCheck()) {
+			contextualMenu.add(doHealthCheckAction);
+		}
+		contextualMenu.add(openInBioCatalogueAction);
+	}
+
+	/**
+	 * Allows to set the search status by supplying the message to display.
+	 */
+	protected void setSearchStatusText(final String statusString,
+			final boolean spinnerActive) {
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				jlSearchStatus
+						.setIcon(spinnerActive ? ResourceManager
+								.getImageIcon(ResourceManager.BAR_LOADER_ORANGE)
+								: null);
+
+				jlSearchStatus.setText(statusString);
+				jlSearchStatus.setToolTipText("<html>"
+						+ Util.ensureLineLengthWithinString(statusString,
+								SEARCH_STATUS_TOOLTIP_LINE_LENGTH, false)
+						+ "</html>");
+			}
+		});
+	}
+
+	/**
+	 * This helper method is used to initialise this panel. Also invoked when
+	 * search results need to be cleared.
+	 * 
+	 * @param showSuggestion
+	 *            <code>true</code> should be used on first load of the panel -
+	 *            in that case a suggestion would be displayed to perform a
+	 *            search, tag search or start directly with filtering;<br/>
+	 *            <code>false</code> to be used when resetting the panel after
+	 *            perfoming the search, but not finding any results.
+	 */
+	public void resetSearchResultsListing(boolean showSuggestion) {
+		setSearchStatusText("No searches were made yet", false);
+
+		String labelText = "<html><center>"
+				+ (showSuggestion ? "You can find "
+						+ this.typeToPreview.getCollectionName()
+						+ " by typing a search query."
+						+ (this.typeToPreview.isSuitableForFiltering() ? "<br><br>Alternatively, you can select some filters from the tree on the left."
+								: "")
+						: "There are no "
+								+ this.typeToPreview.getCollectionName()
+								+ " that match your search criteria<br><br>"
+								+ "Please try making the search query shorter or selecting fewer filters")
+				+ "</center></html>";
+
+		JLabel jlMainLabel = new JLabel(labelText, JLabel.CENTER);
+		jlMainLabel.setFont(jlMainLabel.getFont().deriveFont(Font.PLAIN, 16));
+		jlMainLabel.setBorder(BorderFactory.createEtchedBorder());
+
+		this.removeAll();
+		this.setLayout(new BorderLayout(0, 0));
+		this.add(jpSearchStatus, BorderLayout.NORTH);
+		this.add(jlMainLabel, BorderLayout.CENTER);
+		this.validate();
+
+		// disable the toolbar actions
+		this.addToServicePanelAction.setEnabled(false);
+		this.addToWorkflowDiagramAction.setEnabled(false);
+		this.openInBioCatalogueAction.setEnabled(false);
+		this.doHealthCheckAction.setEnabled(false);
+		this.addAllOperationsToServicePanelAction.setEnabled(false);
+	}
+
+	/**
+	 * Statistics will be rendered along with the collection of found items.
+	 * 
+	 * @param searchInstance
+	 *            SearchInstance containing search results to render.
+	 */
+	public void renderResults(SearchInstance searchInstance) {
+		// make the current search instance available globally within this class
+		this.searchInstance = searchInstance;
+
+		// stop spinner icon on the tab that is populated and add number of
+		// results
+		parentMainSearchResultsPanel.setDefaultIconForTab(typeToPreview);
+		parentMainSearchResultsPanel.setDefaultTitleForTabWithSuffix(
+				typeToPreview, " ("
+						+ searchInstance.getSearchResults()
+								.getTotalMatchingItemCount() + ")");
+
+		// if nothing was found - display notification and finish result
+		// processing
+		if (searchInstance.getSearchResults().getTotalMatchingItemCount() == 0) {
+			resetSearchResultsListing(false);
+
+			// must happen after resetting the listing, as it replaces the
+			// default status text
+			setSearchStatusText("No results found for "
+					+ searchInstance.getDescriptionStringForSearchStatus(),
+					false);
+			return;
+		}
+
+		// populate results
+		if (searchInstance.getSearchResults().getTotalMatchingItemCount() > 0) {
+			// populate the list box with users
+
+			List<? extends ResourceLink> foundItems = searchInstance
+					.getSearchResults().getFoundItems();
+			for (ResourceLink item : foundItems) {
+				resultsListingModel.addElement(item);
+			}
+		}
+
+		// update the UI once contents are ready
+		thisPanel.removeAll();
+		thisPanel.setLayout(new BorderLayout(0, 0));
+		thisPanel.add(jpSearchStatus, BorderLayout.NORTH);
+		thisPanel.add(spResultsListing, BorderLayout.CENTER);
+		thisPanel.repaint();
+
+		// automatically start loading details for the first section of result
+		// listing
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				checkAllEntriesInTheVisiblePartOfJListAreLoaded();
+			}
+		});
+
+		// *** Also update status text ***
+
+		setSearchStatusText("Search results for "
+				+ searchInstance.getDescriptionStringForSearchStatus(), false);
+	}
+
+	/**
+	 * Check if details are fetched for all result entries that are currently
+	 * visible in the JList.
+	 * 
+	 * If some are not yet loaded, identifies the page in the index of
+	 * corresponding resources to fetch details.
+	 * 
+	 * When done, recursively calls itself again to verify that no more entries
+	 * need further details loaded.
+	 */
+	private void checkAllEntriesInTheVisiblePartOfJListAreLoaded() {
+		int firstVisibleIndex = jlResultsListing.getFirstVisibleIndex();
+
+		if (firstVisibleIndex >= 0) {
+			int lastVisibleIndex = jlResultsListing.getLastVisibleIndex();
+
+			final int firstNotFetchedMatchingItemIndex = searchInstance
+					.getSearchResults().getFirstMatchingItemIndexNotYetFetched(
+							firstVisibleIndex, lastVisibleIndex);
+			final int pageToFetchNumber = searchInstance.getSearchResults()
+					.getMatchingItemPageNumberFor(
+							firstNotFetchedMatchingItemIndex);
+
+			// check if found a valid page to load
+			if (pageToFetchNumber != -1) {
+				int numberOfResourcesPerPageForThisResourceType = searchInstance
+						.getSearchResults().getTypeOfResourcesInTheResultSet()
+						.getApiResourceCountPerIndexPage();
+
+				int firstListIndexToLoad = searchInstance.getSearchResults()
+						.getFirstItemIndexOn(pageToFetchNumber); // first
+																	// element
+																	// on the
+																	// page that
+																	// is about
+																	// to be
+																	// loaded
+				int countToLoad = Math.min(
+						numberOfResourcesPerPageForThisResourceType, // if the
+																		// last
+																		// page
+																		// isn't
+																		// full,
+																		// need
+																		// to
+																		// mark
+																		// less
+																		// items
+																		// than
+																		// the
+																		// full
+																		// page
+						resultsListingModel.getSize() - firstListIndexToLoad);
+
+				// mark the next "page" of items in the JList as "loading" -
+				// but also mark them in the SearchResults backing list, so
+				// that next calls to this listener are aware of the previous
+				// items that were marked as "loading"
+				for (int i = firstListIndexToLoad; i < firstListIndexToLoad
+						+ countToLoad; i++) {
+					((LoadingResource) searchInstance.getSearchResults()
+							.getFoundItems().get(i)).setLoading(true);
+				}
+
+				// update the UI to show 'loading' state on relevant entries
+				renderFurtherResults(searchInstance, firstListIndexToLoad,
+						countToLoad);
+
+				// now start loading data for the 'loading' entries
+				final CountDownLatch latch = new CountDownLatch(1);
+				new Thread("Search via the API") {
+					public void run() {
+						try {
+							searchInstance.fetchMoreResults(
+									parentMainSearchResultsPanel, latch,
+									thisPanel, pageToFetchNumber);
+						} catch (Exception e) {
+							logger.error("Error while searching via the Service Catalogue API", e);
+
+						}
+					}
+				}.start();
+
+				// wait for the previous portion of results to load, then fetch
+				// the next portion
+				new Thread(
+						"Fetch more another page of details for search results") {
+					public void run() {
+						try {
+							latch.await();
+							checkAllEntriesInTheVisiblePartOfJListAreLoaded();
+						} catch (InterruptedException e) {
+							logger
+									.error(
+											"Failed to wait for the previous page of results to load to check if "
+													+ "another one needs loading as well. Details in the attache exception.",
+											e);
+						}
+					}
+				}.start();
+
+			}
+		}
+	}
+
+	/**
+	 * Tests whether {@link ResourceLink} object corresponding to an entry in
+	 * the search results list is in the state where only the first (initial)
+	 * fragment of data was loaded (through BioCatalogue LITE JSON API) that
+	 * contains just the title + URL of the resource.
+	 * 
+	 * @param resource
+	 * @return
+	 */
+	private boolean isListEntryOnlyWithInitialDetails(ResourceLink resource) {
+		return (resource instanceof LoadingResource);
+	}
+	
+	private boolean isArchived(ResourceLink resource) {
+		if (listingCellRenderer instanceof ExpandableOnDemandLoadedListCellRenderer) {
+			ExpandableOnDemandLoadedListCellRenderer r = (ExpandableOnDemandLoadedListCellRenderer) listingCellRenderer;
+			return r.shouldBeHidden(resource);
+		}
+		return false;
+	}
+
+
+	// ***** Callbacks for MouseListener *****
+
+	public void mouseClicked(MouseEvent e) {
+	}
+
+	public void mouseEntered(MouseEvent e) { /* NOT IN USE */
+	}
+
+	public void mouseExited(MouseEvent e) { /* NOT IN USE */
+	}
+
+	public void mousePressed(MouseEvent e) {
+		// checked in both mousePressed() & mouseReleased() for cross-platform
+		// operation
+		maybeShowPopupMenu(e);
+	}
+
+	public void mouseReleased(MouseEvent e) {
+		// checked in both mousePressed() & mouseReleased() for cross-platform
+		// operation
+		maybeShowPopupMenu(e);
+	}
+
+	// ***** Callbacks for MouseMotionListener *****
+
+	public void mouseMoved(MouseEvent e) {
+	}
+
+	public void mouseDragged(MouseEvent e) { /* do nothing */
+	}
+
+	/**
+	 * Gets the selected object from the specified list. Used for previewing
+	 * items through double-clicks and contextual menu.
+	 * 
+	 * @return <code>null</code> if no selection in the list,
+	 *         <code>ResourceLink</code> object that is currently selected
+	 *         otherwise.
+	 */
+	private ResourceLink getResourceSelectedInJList() {
+		return (jlResultsListing.getSelectedIndex() == -1 ? null
+				: (ResourceLink) jlResultsListing.getSelectedValue());
+	}
+
+	private void maybeShowPopupMenu(MouseEvent e) {
+		if (e.getSource().equals(jlResultsListing) && e.isPopupTrigger()
+				&& jlResultsListing.locationToIndex(e.getPoint()) != -1) {
+			// select the entry in the list that triggered the event to show
+			// this popup menu
+			jlResultsListing.setSelectedIndex(jlResultsListing
+					.locationToIndex(e.getPoint()));
+
+			// update value to be used in contextual menu click handler to act
+			// on the just-selected entry
+			potentialObjectToPreview = getResourceSelectedInJList();
+
+			// show the contextual menu
+			this.contextualMenu.show(e.getComponent(), e.getX(), e.getY());
+		}
+	}
+
+	// *** Callbacks for SearchResultsRenderer ***
+
+	public void renderInitialResults(final SearchInstance si) {
+		// NB! critical to have UI update done within the invokeLater()
+		// method - this is to prevent UI from 'flashing' and to
+		// avoid concurrency-related errors
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				// make sure to remove any old results from the list model!
+				resultsListingModel.clear();
+
+				// display the partial search results
+				logger.debug("Started rendering initial search results for "
+						+ si.getResourceTypeToSearchFor().getCollectionName());
+				renderResults(si);
+				logger.debug("Finished rendering initial search results for "
+						+ si.getResourceTypeToSearchFor().getCollectionName());
+			}
+		});
+	}
+
+	public void renderFurtherResults(SearchInstance si, int startIndex,
+			int count) {
+		renderFurtherResults(si, startIndex, count, false);
+	}
+
+	public void renderFurtherResults(final SearchInstance si,
+			final int startIndex, final int count,
+			final boolean disableListDataListeners) {
+		logger.debug("Started rendering further search results for "
+				+ si.getResourceTypeToSearchFor().getCollectionName());
+
+		// NB! very important to remove all listeners here, so that the JList
+		// won't "freeze"
+		// on updating the components
+		ListDataListener[] listeners = null;
+		if (disableListDataListeners) {
+			listeners = resultsListingModel.getListDataListeners();
+			for (ListDataListener listener : listeners) {
+				resultsListingModel.removeListDataListener(listener);
+			}
+		}
+
+		for (int i = startIndex; i < startIndex + count
+				&& i < resultsListingModel.getSize(); i++) {
+			resultsListingModel.set(i, searchInstance.getSearchResults()
+					.getFoundItems().get(i));
+		}
+
+		// reset all listeners in case they were removed
+		if (disableListDataListeners) {
+			for (ListDataListener listener : listeners) {
+				resultsListingModel.addListDataListener(listener);
+			}
+		}
+
+		// NB! critical to have UI update done within the invokeLater()
+		// method - this is to prevent UI from 'flashing' and to
+		// avoid some weird errors
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				jlResultsListing.validate();
+				jlResultsListing.repaint();
+
+				logger.debug("Finished rendering further search results for "
+						+ si.getResourceTypeToSearchFor().getCollectionName());
+			}
+		});
+	}
+
+	private void switchToDesignPerspective() {
+		if (designPerspective == null) {
+			for (PerspectiveSPI perspective : Workbench.getInstance()
+					.getPerspectives().getPerspectives()) {
+				if (perspective.getText().equalsIgnoreCase("design")) {
+					designPerspective = perspective;
+					break;
+				}
+			}
+		}
+		
+		if (designPerspective != null) {
+			ModelMap.getInstance().setModel(
+					ModelMapConstants.CURRENT_PERSPECTIVE, designPerspective);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsMainPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsMainPanel.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsMainPanel.java
new file mode 100644
index 0000000..a3fca27
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsMainPanel.java
@@ -0,0 +1,498 @@
+package net.sf.taverna.biocatalogue.ui.search_results;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JSplitPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JToggleButton;
+
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.model.search.SearchInstance;
+import net.sf.taverna.biocatalogue.model.search.SearchInstanceTracker;
+import net.sf.taverna.biocatalogue.model.search.SearchOptions;
+import net.sf.taverna.biocatalogue.model.search.ServiceFilteringSettings;
+import net.sf.taverna.biocatalogue.ui.filtertree.FilterTreePane;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This class represents the main panel that deals with the status
+ * and results of the current search.
+ * 
+ * It has a status label, spinner to depict search in progress,
+ * actual search results split into tabs by their type, a toolbar
+ * with search history, favourite searches settings, favourite filters,
+ * ability to restart last search, etc.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class SearchResultsMainPanel extends JPanel implements ActionListener, SearchInstanceTracker
+{
+  private final SearchResultsMainPanel instanceOfSelf;
+  private Logger logger;
+  
+  private LinkedHashMap<TYPE, JComponent> searchResultTabs;
+  private Map<TYPE, SearchResultsListingPanel> searchResultListings;
+  
+  // holds a reference to the instance of the search instances in the current context
+  // that should be active at the moment (will aid early termination of older searches
+  // when new ones are started)
+  private Map<TYPE, SearchInstance> currentSearchInstances;
+  
+  // holds a map of references to the current instances of filter trees per resource type
+  private Map<TYPE, FilterTreePane> currentFilterPanes;
+  
+  
+  // COMPONENTS
+  private JTabbedPane tabbedSearchResultPanel;
+  
+  protected JToggleButton bToggleSearchHistory;
+  protected JButton bRefreshLastSearch;
+  protected JButton bClearSearchResults;
+  
+  
+  public SearchResultsMainPanel()
+  {
+    this.instanceOfSelf = this;
+    MainComponentFactory.getSharedInstance();
+    this.logger = Logger.getLogger(SearchResultsMainPanel.class);
+    
+    this.currentSearchInstances = new HashMap<TYPE,SearchInstance>();
+    
+    this.searchResultListings = new HashMap<TYPE, SearchResultsListingPanel>();
+    this.currentFilterPanes = new HashMap<TYPE,FilterTreePane>();
+    this.searchResultTabs = new LinkedHashMap<TYPE, JComponent>(); // crucial to preserve the order -- so that these tabs always appear in the UI in the same order!
+    initialiseResultTabsMap();
+    
+    initialiseUI();
+  }
+  
+  
+  private void initialiseUI()
+  {
+    // create a panel for tabbed listings of search results
+    this.tabbedSearchResultPanel = new JTabbedPane();
+    reloadResultTabsFromMap();
+       
+    // pack all main components together
+    JPanel jpMainResultsPanel = new JPanel(new BorderLayout());
+    jpMainResultsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 3));
+    
+    jpMainResultsPanel.add(tabbedSearchResultPanel, BorderLayout.CENTER);
+    
+    
+    // --- Put together all parts ---
+    // main components in the middle, toolbar on the right
+    this.setMinimumSize(new Dimension(450, 50));
+    this.setLayout(new BorderLayout());
+    this.add(jpMainResultsPanel, BorderLayout.CENTER);
+    
+    // FIXME - add toolbar to the main window!
+//    this.add(tbSearchActions, BorderLayout.EAST);
+  }
+  
+  
+  
+  // ----- Hiding / Showing tabs for various search result types -----
+  
+  /**
+   * Dynamically populates the map of resource types and components that represent these types
+   * in the tabbed pane -- this is only to be done once during the initialisation.
+   */
+  private void initialiseResultTabsMap()
+  {
+    for (TYPE t : TYPE.values()) {
+      toggleResultTabsInMap(t, t.isDefaultSearchType());
+    }
+  }
+  
+  
+  /**
+   * Adds or removes a tab for a specified type of resource.
+   * 
+   * @param type Resource type for which the tab is to be added / removed.
+   * @param doShowTab Defines whether to add or remove tab for this resource type.
+   */
+  public void toggleResultTabsInMap(TYPE type, boolean doShowTab)
+  {
+    JPanel jpResultTabContent = null;
+    
+    if (doShowTab)
+    {
+      jpResultTabContent = new JPanel(new GridLayout());
+      
+      // decide if this resource type supports filtering
+      if (type.isSuitableForFiltering()) {
+          FilterTreePane filterTreePane = new FilterTreePane(type);
+          this.currentFilterPanes.put(type, filterTreePane);
+      }
+      else {
+        // not suitable for filtering - record this in a map
+        this.currentFilterPanes.put(type, null);
+      }
+      
+      
+      SearchResultsListingPanel resultsListingPanel = new SearchResultsListingPanel(type, this);
+      this.searchResultListings.put(type, resultsListingPanel);
+      
+      if (this.currentFilterPanes.get(type) == null) {
+        jpResultTabContent.add(resultsListingPanel);
+      }
+      else {
+        JSplitPane spFiltersAndResultListing = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
+        spFiltersAndResultListing.setLeftComponent(this.currentFilterPanes.get(type));
+        spFiltersAndResultListing.setRightComponent(resultsListingPanel);
+        jpResultTabContent.add(spFiltersAndResultListing);
+      }
+    }
+    else {
+      // tab for this type is being hidden - just remove the references
+      // to the search result listing and to filter pane 
+      this.searchResultListings.put(type, null);
+      this.currentFilterPanes.put(type, null);
+    }
+    
+    this.searchResultTabs.put(type, jpResultTabContent);
+  }
+  
+  
+  /**
+   * (Re-)loads the user interface from the internal map.
+   */
+  public void reloadResultTabsFromMap()
+  {
+    Component selectedTabsComponent = tabbedSearchResultPanel.getSelectedComponent();
+    tabbedSearchResultPanel.removeAll();
+    for (TYPE type : this.searchResultTabs.keySet()) {
+      JComponent c = this.searchResultTabs.get(type);
+      if (c != null) {
+        tabbedSearchResultPanel.addTab(type.getCollectionName(), type.getIcon(), c, type.getCollectionTabTooltip());
+      }
+    }
+    
+    // attempt to re-select the same tab that was open before reloading
+    try {
+      tabbedSearchResultPanel.setSelectedComponent(selectedTabsComponent);
+    }
+    catch (IllegalArgumentException e) {
+      // failed - probably previously selected tab got removed - select the first one
+      tabbedSearchResultPanel.setSelectedIndex(0);
+    }
+  }
+  
+  
+  /**
+   * @param resourceType Resource type to look for.
+   * @return Current index of the tab in the results tabbed pane view
+   *         that holds a component showing search results for this type.
+   *         Returns <code>-1</code> if requested type is not currently displayed.
+   */
+  protected int getTabIndexForResourceType(TYPE resourceType) {
+    return (tabbedSearchResultPanel.indexOfComponent(searchResultTabs.get(resourceType)));
+  }
+  
+  
+  // ----- ------
+  
+  
+  /**
+   * This method is intended to be called when filter options in one of the tabs change.
+   * It starts the new filtering operation.
+   * 
+   * Effectively it sets the filtering parameters for the SearchInstance
+   * and then starts a new search with that {@link SearchInstance} wrapped into {@link SearchOptions}.
+   * 
+   * @param resourceType Resource type for which the new filtering operation is started
+   * @param filteringSettings Filtering settings for the current filtering operation
+   *                          obtained from the filter tree (or favourite filters).
+   */
+  public void startNewFiltering(TYPE resourceType, ServiceFilteringSettings filteringSettings)
+  {
+    SearchInstance siPreviousSearchForThisType = getCurrentSearchInstance(resourceType);
+    
+    // pass on the filtering parameters to the relevant search instance (this will overwrite the old ones if any were present!)
+    if (siPreviousSearchForThisType == null) {
+      // no filterings have been done earlier for this resource type;
+      // we'll need a new (blank) query search SearchInstance and
+      // wrap it into a service filtering SearchInstance
+      siPreviousSearchForThisType = new SearchInstance(new SearchInstance("", resourceType), filteringSettings);
+    }
+    else {
+      if (!siPreviousSearchForThisType.isServiceFilteringSearch()) {
+        // just wrap existing search instance that was (probably) transferred from the Search tab
+        // into another SearchInstance that explicitly deals with service filtering
+        siPreviousSearchForThisType = new SearchInstance(siPreviousSearchForThisType, filteringSettings);
+      }
+      else {
+        // previous search instance dealt with filtering -
+        // simply update the filtering settings (but before that
+        // run a 'deep copy' of the original search instance, so
+        // that the new one gets a new reference; this will aid
+        // in early termination of older filterings)
+        siPreviousSearchForThisType = siPreviousSearchForThisType.deepCopy();
+        siPreviousSearchForThisType.setFilteringSettings(filteringSettings);
+      }
+    }
+    
+    // proceed with "search" as usual - it will treat this search instance differently
+    // from "ordinary" search
+    startNewSearch(new SearchOptions(siPreviousSearchForThisType));
+  }
+  
+  
+  /**
+   * Worker method responsible for starting a new search via the API.
+   * 
+   * This method is to be used when a *new* search is started. It will
+   * mainly make updates to the UI and store the new search in the history.
+   */
+  public void startNewSearch(final SearchOptions searchOptions)
+  {
+    try
+    {
+      for (final TYPE resourceType : searchOptions.getResourceTypesToSearchFor())
+      {
+        SearchInstance si = null;
+        switch (searchOptions.getSearchType()) {
+          case QuerySearch: si = new SearchInstance(searchOptions.getSearchString(), resourceType);
+                            resetAllFilterPanes(searchOptions.getSearchString());
+                            break;
+                            
+          case TagSearch:   if (resourceType.isSuitableForTagSearch()) {
+                              si = new SearchInstance(searchOptions.getSearchTags(), resourceType);
+                              resetAllFilterPanes(searchOptions.getSearchString());
+                            }
+                            else {
+                              // FIXME implement this... - show "no results" in the appropriate tab
+                              JOptionPane.showMessageDialog(null, "'" + resourceType.getTypeName() + "' resource type is not suitable for tag search");
+                            }
+                            break;
+                            
+          case Filtering:   if (resourceType.isSuitableForFiltering()) {
+                              si = searchOptions.getPreconfiguredSearchInstance();
+                            }
+                            else {
+                              // FIXME implement this... - show "no results" in the appropriate tab
+                              JOptionPane.showMessageDialog(null, "'" + resourceType.getTypeName() + "' resource type is not suitable for filtering");
+                            }
+                            break;
+        }
+        
+        if (si.isEmptySearch()) {
+        	clearListingPanels();
+        	return;
+        }
+        
+        // Record 'this' search instance and set it as the new "primary" one for
+        // this resource type;
+        // (this way it if a new search thread starts afterwards, it is possible to
+        //  detect this and stop the 'older' search, because it is no longer relevant)
+        registerSearchInstance(resourceType, si);
+        
+        // start spinner icon for this tab to indicate search in progress - also show status message
+        setSpinnerIconForTab(resourceType);
+        setDefaultTitleForTab(resourceType);
+        searchResultListings.get(resourceType).setSearchStatusText("Searching for " + si.getDescriptionStringForSearchStatus() + "...", true);
+        
+        
+        // start the actual search
+        final SearchInstance siToStart = si;
+        new Thread(searchOptions.getSearchType() + " of " + resourceType.getCollectionName() + " via the API") {
+          public void run() {
+            siToStart.startNewSearch(instanceOfSelf, new CountDownLatch(1), searchResultListings.get(resourceType));  // FIXME - the new countdown latch is never used...
+          }
+        }.start();
+      }
+    }
+    catch (Exception e) {
+      logger.error("Error while searching via the Service Catalogue API. Error details attached.", e);
+    }
+    
+}
+  
+  
+  
+  /**
+   * Clears selection of filtering criteria and collapses any expanded nodes
+   * in all filter tree panes.<br/><br/>
+   * 
+   * To be used for resetting all filter panes when the new query / tag
+   * search starts.
+ * @param queryString 
+   */
+  private void resetAllFilterPanes(String queryString) {
+    for (FilterTreePane filterTreePane : this.currentFilterPanes.values()) {
+      if (filterTreePane != null) {
+        filterTreePane.clearSelection();
+        filterTreePane.collapseAll();
+        if ((queryString != null) && !queryString.isEmpty()) {
+        	filterTreePane.applyQueryString(queryString);
+        }
+      }
+    }
+  }
+  
+  
+  protected void setSpinnerIconForTab(TYPE resourceType) {
+    tabbedSearchResultPanel.setIconAt(getTabIndexForResourceType(resourceType), ResourceManager.getImageIcon(ResourceManager.SPINNER));
+  }
+  
+  protected void setDefaultIconForTab(TYPE resourceType) {
+    this.tabbedSearchResultPanel.setIconAt(getTabIndexForResourceType(resourceType), resourceType.getIcon());
+  }
+  
+  
+  /**
+   * Same as {@link SearchResultsMainPanel#setDefaultTitleForTab(TYPE)},
+   * but allows to append a specified string at the end of the default title.
+   * 
+   * @param resourceType
+   * @param suffix
+   */
+  protected void setDefaultTitleForTabWithSuffix(TYPE resourceType, String suffix) {
+    tabbedSearchResultPanel.setTitleAt(getTabIndexForResourceType(resourceType),
+        resourceType.getCollectionName() + (suffix == null ? "" : suffix) );
+  }
+  
+  
+  /**
+   * Sets default title for a tab that contains panel representing 
+   * search results of the specified resource type. Default title
+   * is just a name of the collections of resources in that tab. 
+   * 
+   * @param resourceType 
+   */
+  protected void setDefaultTitleForTab(TYPE resourceType) {
+    setDefaultTitleForTabWithSuffix(resourceType, null);
+  }
+  
+  
+  /**
+   * @param resourceType Resource type for which the search result listing panel is requested.
+   * @return Reference to the requested panel or <code>null</code> if a tab for the specified
+   *         <code>resourceType</code> does not exist.
+   */
+  protected SearchResultsListingPanel getResultsListingFor(TYPE resourceType) {
+    return (this.searchResultListings.get(resourceType));
+  }
+  
+  
+  /**
+   * @param resourceType Resource type for which filter tree pane is to be returned.
+   * @return Reference to the requested filter tree pane or <code>null</code> if
+   *         there is no search result tab for the specified <code>resourceType</code>
+   *         (or if that <code>resourceType</code> does not support filtering).
+   */
+  protected FilterTreePane getFilterTreePaneFor(TYPE resourceType) {
+    return (this.currentFilterPanes.get(resourceType));
+  }
+    
+  
+  // *** Callback for ActionListener interface ***
+  
+  public void actionPerformed(ActionEvent e)
+  {
+    // FIXME -- remove this...
+//    if (e.getSource().equals(bRefreshLastSearch))
+//    {
+//      // restore state of the search options panel
+//      pluginPerspectiveMainComponent.getSearchTab().restoreSearchOptions(siPreviousSearch);
+//      
+//      // completely re-run the previous search
+//      startNewSearch(siPreviousSearch);
+//    }
+//    else if (e.getSource().equals(bClearSearchResults))
+//    {
+//      // manual request to clear results of previous search
+//      
+//      // if any search thread was running, deactivate it as well
+//      if (isSearchThreadRunning()) {
+//        vCurrentSearchThreadID.set(0, null);
+//      }
+//      
+//      // changing both - spinner image and the status text simultaneously
+//      setSearchStatusText("No searches were made yet", false);
+//      
+//      // removed the previous search, hence makes no sense to allow to clear "previous" results again
+//      bClearSearchResults.setEnabled(false);
+//      
+//      // only remove data about previous search and disable refresh button 
+//      // if no search thread is currently running - otherwise keep the button
+//      // enabled in case there is a need to re-start the search if it's frozen
+//      if (!isSearchThreadRunning()) {
+//        siPreviousSearch = null;
+//        bRefreshLastSearch.setEnabled(false);
+//      }
+//      
+//      // also notify tabbed results panel, so that it removes the actual search results 
+//      searchResultsPanel.clearPreviousSearchResults();
+//    }
+//    else if (e.getSource().equals(this.jclPreviewCurrentFilteringCriteria))
+//    {
+//      // open a preview window showing current filtering settings
+//      SwingUtilities.invokeLater(new Runnable()
+//      {
+//        public void run() {
+//          ServiceFilteringSettingsPreview p = new ServiceFilteringSettingsPreview(siPreviousSearch.getFilteringSettings());
+//          p.setVisible(true);
+//        }
+//      });
+//      
+//    }
+  }
+  
+  
+  // *** Callbacks for SearchInstanceTracker interface ***
+  
+  public synchronized void clearPreviousSearchInstances() {
+    this.currentSearchInstances.clear();
+  }
+  
+  public synchronized boolean isCurrentSearchInstance(TYPE searchType, SearchInstance searchInstance) {
+    // NB! it is crucial to perform test by reference here (hence the use of "==", not equals()!)
+    return (this.currentSearchInstances.get(searchType) == searchInstance);
+  }
+  
+  public synchronized void registerSearchInstance(TYPE searchType, SearchInstance searchInstance) {
+    this.currentSearchInstances.put(searchType, searchInstance);
+  }
+  
+  public synchronized SearchInstance getCurrentSearchInstance(TYPE searchType) {
+    return this.currentSearchInstances.get(searchType);
+  }
+
+public void clearListingPanels() {
+    for (SearchResultsListingPanel listingPanel : searchResultListings.values()) {
+    	listingPanel.resetSearchResultsListing(true);
+    }
+    for (TYPE t : searchResultListings.keySet()) {
+    	setDefaultTitleForTab(t);
+    }
+	
+}
+
+public void clearSearch() {
+	clearPreviousSearchInstances();
+	clearListingPanels();
+    for (FilterTreePane treePanel : currentFilterPanes.values()) {
+    	treePanel.reset();
+    }
+}
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsRenderer.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsRenderer.java
new file mode 100644
index 0000000..58ee5fd
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/SearchResultsRenderer.java
@@ -0,0 +1,47 @@
+package net.sf.taverna.biocatalogue.ui.search_results;
+
+import net.sf.taverna.biocatalogue.model.search.SearchInstance;
+
+/**
+ * This interfaces avoids coupling of search engine classes
+ * directly with the UI classes.
+ * 
+ * Search engines would send new chunks of search results
+ * to the <code>SearchResultsRenderer</code> as soon as
+ * they become available.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public interface SearchResultsRenderer
+{
+  /**
+   * Render initial fragment of search results. This includes
+   * creating a new <code>ListModel</code> in the results listing
+   * and populating it with the first chunk of results and placeholders
+   * for those results that haven't been fetched yet.
+   * 
+//   * @param searchThreadID This is the ID of the thread that initiated search  // FIXME
+//   *                       from within the UI component, rather than the ID of
+//   *                       the real worker search engine's search thread.
+//   *                       It is used to test whether that thread is still active -
+//   *                       to determine whether the partial results need to be rendered.
+   * @param si The search instance containing partial search results to be rendered. 
+   */
+  void renderInitialResults(SearchInstance si);
+  
+  
+  /**
+   * Update the results listing with a specific fragment of the collection
+   * of search results.
+   * 
+   * @param si The search instance containing partial search results to be rendered.
+   * @param startIndex First index in the result collection to update.
+   * @param count Number of result listing entries to update.
+   *              <br/>
+   *              At most <code>count</code> results will be rendered - less can be rendered
+   *              if end of result list is reached earlier. <code>count</code> is normally
+   *              just a page size for a specific resource type.
+   */
+  void renderFurtherResults(SearchInstance si, int startIndex, int count);
+  
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerImpl.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerImpl.java b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerImpl.java
new file mode 100644
index 0000000..4c03dbe
--- /dev/null
+++ b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerImpl.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * 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.ui.impl.configuration.colour;
+
+import static java.awt.Color.WHITE;
+import static java.awt.Color.decode;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
+
+import uk.org.taverna.configuration.AbstractConfigurable;
+import uk.org.taverna.configuration.ConfigurationManager;
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+
+/**
+ * A factory class that determines the colour that a Colourable UI component
+ * should be displayed as, according to a schema configured by the user.
+ * 
+ * @author Stuart Owen
+ * @author Ian Dunlop
+ * @see Colourable
+ */
+public class ColourManagerImpl extends AbstractConfigurable implements
+		ColourManager {
+	// Names of things that may be coloured
+	private static final String WORKFLOW_PORT_OBJECT = "uk.org.taverna.scufl2.api.port.WorkflowPort";
+	private static final String PROCESSOR_PORT_OBJECT = "uk.org.taverna.scufl2.api.port.ProcessorPort";
+	private static final String PROCESSOR_OBJECT = "uk.org.taverna.scufl2.api.core.Processor";
+	private static final String MERGE_OBJECT = "net.sf.taverna.t2.workflowmodel.Merge";
+	private static final String NONEXECUTABLE_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/nonExecutable";
+	private static final String XML_SPLITTER_OUT_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/xml-splitter/out";
+	private static final String XML_SPLITTER_IN_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/xml-splitter/in";
+	private static final String LOCALWORKER_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/localworker";
+	private static final String WSDL_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/wsdl";
+	private static final String CONSTANT_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/constant";
+	private static final String SOAPLAB_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/soaplab";
+	private static final String RSHELL_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/rshell";
+	private static final String NESTED_WORKFLOW = "http://ns.taverna.org.uk/2010/activity/nested-workflow";
+	private static final String MOBY_PARSER_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/biomoby/parser";
+	private static final String MOBY_OBJECT_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/biomoby/object";
+	private static final String MOBY_SERVICE_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/biomoby/service";
+	private static final String BIOMART_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/biomart";
+	private static final String BEANSHELL_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/beanshell";
+	private static final String APICONSUMER_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/apiconsumer";
+
+	// Names of colours used
+	private static final String burlywood2 = "#deb887";
+	private static final String darkgoldenrod1 = "#ffb90f";
+	private static final String darkolivegreen3 = "#a2cd5a";
+	private static final String gold = "#ffd700";
+	private static final String grey = "#777777";
+	private static final String lightcyan2 = "#d1eeee";
+	private static final String lightgoldenrodyellow = "#fafad2";
+	// light purple non standard
+	private static final String lightpurple = "#ab92ea";
+	private static final String lightsteelblue = "#b0c4de";
+	private static final String mediumorchid2 = "#d15fee";
+	private static final String palegreen = "#98fb98";
+	private static final String pink = "#ffc0cb";
+	private static final String purplish = "#8070ff";
+	// ShadedLabel.Orange
+	private static final String shadedorange = "#eece8f";
+	// ShadedLabel.Green
+	private static final String shadedgreen = "#a1c69d";
+	// slightly lighter than the real steelblue4
+	private static final String steelblue4 = "#648faa";
+	private static final String turquoise = "#77aadd";
+	private static final String white = "#ffffff";
+
+	private Map<String, String> defaultPropertyMap;
+	private Map<Object, Color> cachedColours;
+
+	public ColourManagerImpl(ConfigurationManager configurationManager) {
+		super(configurationManager);
+		initialiseDefaults();
+	}
+
+	@Override
+	public String getCategory() {
+		return "colour";
+	}
+
+	@Override
+	public Map<String, String> getDefaultPropertyMap() {
+		if (defaultPropertyMap == null)
+			initialiseDefaults();
+		return defaultPropertyMap;
+	}
+
+	@Override
+	public String getDisplayName() {
+		return "Colour Management";
+	}
+
+	@Override
+	public String getFilePrefix() {
+		return "ColourManagement";
+	}
+
+	/**
+	 * Unique identifier for this ColourManager
+	 */
+	@Override
+	public String getUUID() {
+		return "a2148420-5967-11dd-ae16-0800200c9a66";
+	}
+
+	private void initialiseDefaults() {
+		defaultPropertyMap = new HashMap<>();
+		cachedColours = new HashMap<>();
+
+		defaultPropertyMap.put(APICONSUMER_ACTIVITY, palegreen);
+		defaultPropertyMap.put(BEANSHELL_ACTIVITY, burlywood2);
+		defaultPropertyMap.put(BIOMART_ACTIVITY, lightcyan2);
+		defaultPropertyMap.put(CONSTANT_ACTIVITY, lightsteelblue);
+		defaultPropertyMap.put(LOCALWORKER_ACTIVITY, mediumorchid2);
+		defaultPropertyMap.put(MOBY_SERVICE_ACTIVITY, darkgoldenrod1);
+		defaultPropertyMap.put(MOBY_OBJECT_ACTIVITY, gold);
+		defaultPropertyMap.put(MOBY_PARSER_ACTIVITY, white);
+		defaultPropertyMap.put(NESTED_WORKFLOW, pink);
+		defaultPropertyMap.put(RSHELL_ACTIVITY, steelblue4);
+		defaultPropertyMap.put(SOAPLAB_ACTIVITY, lightgoldenrodyellow);
+		defaultPropertyMap.put(WSDL_ACTIVITY, darkolivegreen3);
+		defaultPropertyMap.put(XML_SPLITTER_IN_ACTIVITY, lightpurple);
+		defaultPropertyMap.put(XML_SPLITTER_OUT_ACTIVITY, lightpurple);
+
+		defaultPropertyMap.put(NONEXECUTABLE_ACTIVITY, grey);
+
+		defaultPropertyMap.put(MERGE_OBJECT, turquoise);
+		defaultPropertyMap.put(PROCESSOR_OBJECT, shadedgreen);
+		defaultPropertyMap.put(PROCESSOR_PORT_OBJECT, purplish);
+		defaultPropertyMap.put(WORKFLOW_PORT_OBJECT, shadedorange);
+	}
+
+	@Override
+	public Color getPreferredColour(String itemKey) {
+		Color colour = cachedColours.get(itemKey);
+		if (colour == null) {
+			String colourString = (String) getProperty(itemKey);
+			colour = colourString == null ? WHITE : decode(colourString);
+			cachedColours.put(itemKey, colour);
+		}
+		return colour;
+	}
+
+	@Override
+	public void setPreferredColour(String itemKey, Color colour) {
+		cachedColours.put(itemKey, colour);
+	}
+
+	@Override
+	public void restoreDefaults() {
+		super.restoreDefaults();
+		if (cachedColours == null)
+			cachedColours = new HashMap<>();
+		else
+			cachedColours.clear();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/mimetype/MimeTypeManagerImpl.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/mimetype/MimeTypeManagerImpl.java b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/mimetype/MimeTypeManagerImpl.java
new file mode 100644
index 0000000..0ff6c65
--- /dev/null
+++ b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/mimetype/MimeTypeManagerImpl.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * 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.ui.impl.configuration.mimetype;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sf.taverna.t2.workbench.configuration.mimetype.MimeTypeManager;
+import uk.org.taverna.configuration.AbstractConfigurable;
+import uk.org.taverna.configuration.ConfigurationManager;
+
+public class MimeTypeManagerImpl extends AbstractConfigurable implements
+		MimeTypeManager {
+	/**
+	 * Constructs a new <code>MimeTypeManagerImpl</code>.
+	 * 
+	 * @param configurationManager
+	 */
+	public MimeTypeManagerImpl(ConfigurationManager configurationManager) {
+		super(configurationManager);
+	}
+
+	@Override
+	public String getCategory() {
+		return "Mime Type";
+	}
+
+	@Override
+	public Map<String, String> getDefaultPropertyMap() {
+		HashMap<String, String> map = new HashMap<>();
+		map.put("text/plain", "Plain Text");
+		map.put("text/xml", "XML Text");
+		map.put("text/html", "HTML Text");
+		map.put("text/rtf", "Rich Text Format");
+		map.put("text/x-graphviz", "Graphviz Dot File");
+		map.put("image/png", "PNG Image");
+		map.put("image/jpeg", "JPEG Image");
+		map.put("image/gif", "GIF Image");
+		map.put("application/octet-stream", "Binary Data");
+		map.put("application/zip", "Zip File");
+		map.put("chemical/x-swissprot", "SWISSPROT Flat File");
+		map.put("chemical/x-embl-dl-nucleotide", "EMBL Flat File");
+		map.put("chemical/x-ppd", "PPD File");
+		map.put("chemical/seq-aa-genpept", "Genpept Protein");
+		map.put("chemical/seq-na-genbank", "Genbank Nucleotide");
+		map.put("chemical/x-pdb", "PDB 3D Structure File");
+		return map;
+	}
+
+	@Override
+	public String getUUID() {
+		return "b9277fa0-5967-11dd-ae16-0800200c9a66";
+	}
+
+	@Override
+	public String getDisplayName() {
+		return "Mime Type Manager";
+	}
+
+	@Override
+	public String getFilePrefix() {
+		return "MimeTypeManagerImpl";
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/T2ConfigurationFrameImpl.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/T2ConfigurationFrameImpl.java b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/T2ConfigurationFrameImpl.java
new file mode 100644
index 0000000..4910f78
--- /dev/null
+++ b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/T2ConfigurationFrameImpl.java
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * 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.ui.impl.configuration.ui;
+
+import static javax.swing.JSplitPane.HORIZONTAL_SPLIT;
+import static net.sf.taverna.t2.workbench.helper.HelpCollator.registerComponent;
+import static net.sf.taverna.t2.workbench.helper.Helper.setKeyCatcher;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JFrame;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.ListModel;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import net.sf.taverna.t2.workbench.configuration.workbench.ui.T2ConfigurationFrame;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.ConfigurationUIFactory;
+
+public class T2ConfigurationFrameImpl implements T2ConfigurationFrame {
+	private static Logger logger = Logger.getLogger(T2ConfigurationFrameImpl.class);
+	private static final int FRAME_WIDTH = 700;
+	private static final int FRAME_HEIGHT = 450;
+
+	private List<ConfigurationUIFactory> configurationUIFactories = new ArrayList<>();
+
+	private JFrame frame;
+	private JSplitPane splitPane;
+	private JList<ConfigurableItem> list;
+
+	public T2ConfigurationFrameImpl() {
+	}
+
+	@Override
+	public void showFrame() {
+		getFrame().setVisible(true);
+	}
+
+	@Override
+	public void showConfiguration(String name) {
+		showFrame();
+		ListModel<ConfigurableItem> lm = list.getModel();
+		for (int i = 0; i < lm.getSize(); i++)
+			if (lm.getElementAt(i).toString().equals(name)) {
+				list.setSelectedIndex(i);
+				break;
+			}
+	}
+
+	private JFrame getFrame() {
+		if (frame != null)
+			return frame;
+
+		frame = new JFrame();
+		setKeyCatcher(frame);
+		registerComponent(frame);
+		frame.setLayout(new BorderLayout());
+
+		/*
+		 * Split pane to hold list of properties (on the left) and their
+		 * configurable options (on the right)
+		 */
+		splitPane = new JSplitPane(HORIZONTAL_SPLIT);
+		splitPane.setBorder(null);
+
+		list = getConfigurationList();
+		JScrollPane jspList = new JScrollPane(list);
+		jspList.setBorder(new EmptyBorder(5, 5, 5, 5));
+		jspList.setMinimumSize(new Dimension(150,
+				jspList.getPreferredSize().height));
+
+		splitPane.setLeftComponent(jspList);
+		splitPane.setRightComponent(new JPanel());
+		splitPane.setDividerSize(1);
+
+		// select first item if one exists
+		if (list.getModel().getSize() > 0)
+			list.setSelectedValue(list.getModel().getElementAt(0), true);
+
+		frame.add(splitPane);
+		frame.setSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
+		return frame;
+	}
+
+	private JList<ConfigurableItem> getConfigurationList() {
+		if (list != null)
+			return list;
+
+		list = new JList<>();
+		list.addListSelectionListener(new ListSelectionListener() {
+			@Override
+			public void valueChanged(ListSelectionEvent e) {
+				if (list.getSelectedValue() instanceof ConfigurableItem) {
+					ConfigurableItem item = (ConfigurableItem) list
+							.getSelectedValue();
+					setMainPanel(item.getPanel());
+				}
+
+				/*
+				 * Keep the split pane's divider at its current position - but
+				 * looks ugly. The problem with divider moving from its current
+				 * position after selecting an item from the list on the left is
+				 * that the right hand side panels are loaded dynamically and it
+				 * seems there is nothing we can do about it - it's just the
+				 * JSplitPane's behaviour
+				 */
+				// splitPane.setDividerLocation(splitPane.getLastDividerLocation());
+			}
+		});
+		list.setListData(getListItems());
+		return list;
+	}
+
+	private void setMainPanel(JPanel panel) {
+		panel.setBorder(new EmptyBorder(15, 15, 15, 15));
+		splitPane.setRightComponent(panel);
+	}
+
+	public void setConfigurationUIFactories(
+			List<ConfigurationUIFactory> configurationUIFactories) {
+		this.configurationUIFactories = configurationUIFactories;
+	}
+
+	private ConfigurableItem[] getListItems() {
+		List<ConfigurableItem> arrayList = new ArrayList<>();
+		for (ConfigurationUIFactory fac : configurationUIFactories) {
+			String name = fac.getConfigurable().getDisplayName();
+			if (name != null) {
+				logger.info("Adding configurable for name: " + name);
+				arrayList.add(new ConfigurableItem(fac));
+			} else {
+				logger.warn("The configurable " + fac.getConfigurable().getClass()
+						+ " has a null name");
+			}
+		}
+		// Sort the list alphabetically
+		ConfigurableItem[] array = arrayList.toArray(new ConfigurableItem[0]);
+		Arrays.sort(array, new Comparator<ConfigurableItem>() {
+			@Override
+			public int compare(ConfigurableItem item1, ConfigurableItem item2) {
+				return item1.toString().compareToIgnoreCase(item2.toString());
+			}
+		});
+		return array;
+	}
+
+	public void update(Object service, Map<?, ?> properties) {
+		getConfigurationList().setListData(getListItems());
+		if (frame != null) {
+			frame.revalidate();
+			frame.repaint();
+			// select first item if one exists
+			if (list.getModel().getSize() > 0)
+				list.setSelectedValue(list.getModel().getElementAt(0), true);
+		}
+	}
+
+	class ConfigurableItem {
+		private final ConfigurationUIFactory factory;
+
+		public ConfigurableItem(ConfigurationUIFactory factory) {
+			this.factory = factory;
+		}
+
+		public JPanel getPanel() {
+			return factory.getConfigurationPanel();
+		}
+
+		@Override
+		public String toString() {
+			return factory.getConfigurable().getDisplayName();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchConfigurationMenu.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchConfigurationMenu.java b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchConfigurationMenu.java
new file mode 100644
index 0000000..453f0c0
--- /dev/null
+++ b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchConfigurationMenu.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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.ui.impl.configuration.ui;
+
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.configuration.workbench.ui.T2ConfigurationFrame;
+
+public class WorkbenchConfigurationMenu extends AbstractMenuAction {
+	private static final String MAC_OS_X = "Mac OS X";
+
+	private T2ConfigurationFrame t2ConfigurationFrame;
+
+	public WorkbenchConfigurationMenu() {
+		super(URI.create("http://taverna.sf.net/2008/t2workbench/menu#preferences"),
+				100);
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		return new AbstractAction("Preferences") {
+			@Override
+			public void actionPerformed(ActionEvent event) {
+				t2ConfigurationFrame.showFrame();
+			}
+		};
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return !MAC_OS_X.equalsIgnoreCase(System.getProperty("os.name"));
+	}
+
+	public void setT2ConfigurationFrame(T2ConfigurationFrame t2ConfigurationFrame) {
+		this.t2ConfigurationFrame = t2ConfigurationFrame;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchPreferencesSection.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchPreferencesSection.java b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchPreferencesSection.java
new file mode 100644
index 0000000..d131ac3
--- /dev/null
+++ b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchPreferencesSection.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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.ui.impl.configuration.ui;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+public class WorkbenchPreferencesSection extends AbstractMenuSection {
+	private static final URI FILE_MENU = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#file");
+	private static final URI PREFERENCES_MENU_ITEM = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#preferences");
+
+	public WorkbenchPreferencesSection() {
+		super(FILE_MENU, 100, PREFERENCES_MENU_ITEM);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..3b51dd4
--- /dev/null
+++ b/taverna-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,2 @@
+net.sf.taverna.t2.workbench.ui.impl.configuration.ui.WorkbenchPreferencesSection
+net.sf.taverna.t2.workbench.ui.impl.configuration.ui.WorkbenchConfigurationMenu

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
new file mode 100644
index 0000000..4af55ec
--- /dev/null
+++ b/taverna-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.ui.impl.configuration.WorkbenchConfigurationUIFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context-osgi.xml b/taverna-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context-osgi.xml
new file mode 100644
index 0000000..29aea44
--- /dev/null
+++ b/taverna-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context-osgi.xml
@@ -0,0 +1,27 @@
+<?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="t2ConfigurationFrame" interface="net.sf.taverna.t2.workbench.configuration.workbench.ui.T2ConfigurationFrame" />
+
+	<service ref="WorkbenchConfigurationUIFactory" interface="uk.org.taverna.configuration.ConfigurationUIFactory" />
+
+	<service ref="WorkbenchPreferencesSection" auto-export="interfaces" />
+	<service ref="WorkbenchConfigurationMenu" auto-export="interfaces" />
+
+	<service ref="ColourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" />
+	<service ref="WorkbenchConfiguration" interface="net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration" />
+	<service ref="MimeTypeManager" interface="net.sf.taverna.t2.workbench.configuration.mimetype.MimeTypeManager" />
+
+	<reference id="configurationManager" interface="uk.org.taverna.configuration.ConfigurationManager" />
+	<reference id="applicationConfiguration" interface="uk.org.taverna.configuration.app.ApplicationConfiguration" />
+
+	<list id="configurationUIFactories" interface="uk.org.taverna.configuration.ConfigurationUIFactory" cardinality="0..N">
+		<listener ref="t2ConfigurationFrame" bind-method="update" unbind-method="update"/>
+	</list>
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context.xml
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context.xml b/taverna-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context.xml
new file mode 100644
index 0000000..40da7fd
--- /dev/null
+++ b/taverna-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context.xml
@@ -0,0 +1,34 @@
+<?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="t2ConfigurationFrame" class="net.sf.taverna.t2.workbench.ui.impl.configuration.ui.T2ConfigurationFrameImpl">
+		<property name="configurationUIFactories" ref="configurationUIFactories" />
+	</bean>
+
+	<bean id="WorkbenchConfigurationUIFactory" class="net.sf.taverna.t2.workbench.ui.impl.configuration.WorkbenchConfigurationUIFactory">
+		<property name="workbenchConfiguration">
+			<ref local="WorkbenchConfiguration"/>
+		</property>
+	</bean>
+
+	<bean id="WorkbenchPreferencesSection" class="net.sf.taverna.t2.workbench.ui.impl.configuration.ui.WorkbenchPreferencesSection" />
+	<bean id="WorkbenchConfigurationMenu" class="net.sf.taverna.t2.workbench.ui.impl.configuration.ui.WorkbenchConfigurationMenu">
+		<property name="t2ConfigurationFrame">
+			<ref local="t2ConfigurationFrame"/>
+		</property>
+	</bean>
+
+	<bean id="ColourManager" class="net.sf.taverna.t2.workbench.ui.impl.configuration.colour.ColourManagerImpl">
+		<constructor-arg ref="configurationManager"/>
+	</bean>
+	<bean id="WorkbenchConfiguration" class="net.sf.taverna.t2.workbench.ui.impl.configuration.WorkbenchConfigurationImpl">
+		<constructor-arg ref="configurationManager"/>
+		<property name="applicationConfiguration" ref="applicationConfiguration" />
+	</bean>
+	<bean id="MimeTypeManager" class="net.sf.taverna.t2.workbench.ui.impl.configuration.mimetype.MimeTypeManagerImpl">
+		<constructor-arg ref="configurationManager"/>
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ConfigurationManagerTest.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ConfigurationManagerTest.java b/taverna-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ConfigurationManagerTest.java
new file mode 100644
index 0000000..1202c11
--- /dev/null
+++ b/taverna-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ConfigurationManagerTest.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * 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.ui.impl.configuration;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.File;
+import java.util.UUID;
+
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+import net.sf.taverna.t2.workbench.ui.impl.configuration.colour.ColourManagerImpl;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import uk.org.taverna.configuration.app.impl.ApplicationConfigurationImpl;
+import uk.org.taverna.configuration.impl.ConfigurationManagerImpl;
+
+public class ConfigurationManagerTest {
+
+	ConfigurationManagerImpl configurationManager;
+
+	@Before
+	public void setup() {
+		configurationManager = new ConfigurationManagerImpl(new ApplicationConfigurationImpl());
+	}
+
+	@Test
+	public void createConfigManager() {
+		assertNotNull("Config Manager should not be null", configurationManager);
+	}
+
+	@Ignore("Hardcoded /Users/Ian") //FIXME: update test to work using File.createTempFile(...)
+	@Test
+	public void populateConfigOfColourmanager() {
+		ColourManager manager= new ColourManagerImpl(null);
+
+		manager.setProperty("colour.first", "25");
+		manager.setProperty("colour.second", "223");
+
+		configurationManager.setBaseConfigLocation(new File("/Users/Ian/scratch"));
+		try {
+			configurationManager.store(manager);
+		} catch (Exception e1) {
+			e1.printStackTrace();
+		}
+
+		ColourManager manager2 = new ColourManagerImpl(configurationManager);
+
+		try {
+			configurationManager.populate(manager2);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+
+
+		assertEquals("Properties do not match", manager2.getProperty("colour.first"), manager.getProperty("colour.first"));
+		assertEquals("Properties do not match", manager2.getProperty("colour.second"), manager.getProperty("colour.second"));
+
+
+	}
+
+	@Test
+	public void saveColoursForDummyColourable() {
+		String dummy = "";
+		ColourManager manager=new ColourManagerImpl(configurationManager);
+		manager.setProperty(dummy.getClass().getCanonicalName(), "#000000");
+
+		File f = new File(System.getProperty("java.io.tmpdir"));
+		File d = new File(f, UUID.randomUUID().toString());
+		d.mkdir();
+		configurationManager.setBaseConfigLocation(d);
+		try {
+			configurationManager.store(manager);
+		} catch (Exception e1) {
+			e1.printStackTrace();
+		}
+
+		try {
+			configurationManager.populate(manager);
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+
+
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerTest.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerTest.java b/taverna-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerTest.java
new file mode 100644
index 0000000..0239ea8
--- /dev/null
+++ b/taverna-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerTest.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * 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.ui.impl.configuration.colour;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.awt.Color;
+import java.io.File;
+import java.util.UUID;
+
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import uk.org.taverna.configuration.Configurable;
+import uk.org.taverna.configuration.app.impl.ApplicationConfigurationImpl;
+import uk.org.taverna.configuration.impl.ConfigurationManagerImpl;
+
+public class ColourManagerTest {
+
+	private ConfigurationManagerImpl configurationManager;
+
+	@Before
+	public void setup() {
+		configurationManager = new ConfigurationManagerImpl(new ApplicationConfigurationImpl());
+
+		File f = new File(System.getProperty("java.io.tmpdir"));
+		File d = new File(f, UUID.randomUUID().toString());
+		d.mkdir();
+		configurationManager.setBaseConfigLocation(d);
+	}
+
+	@Test
+	public void testGetPreferredColourEqualsWhite() throws Exception {
+		String dummy = new String();
+
+		Color c = new ColourManagerImpl(configurationManager).getPreferredColour(dummy);
+		assertEquals("The default colour should be WHITE", Color.WHITE, c);
+	}
+
+	@Test
+	public void testConfigurableness() throws Exception {
+		ColourManager manager = new ColourManagerImpl(configurationManager);
+		assertTrue(manager instanceof Configurable);
+
+		assertEquals("wrong category", "colour", manager.getCategory());
+		assertEquals("wrong name", "Colour Management", manager.getDisplayName());
+		assertEquals("wrong UUID", "a2148420-5967-11dd-ae16-0800200c9a66",
+				manager.getUUID());
+		assertNotNull("there is no default property map", manager
+				.getDefaultPropertyMap());
+	}
+
+	@Test
+	public void saveAsWrongArrayType() throws Exception {
+		String dummy = "";
+		ColourManager manager = new ColourManagerImpl(configurationManager);
+		manager.setProperty(dummy.getClass().getCanonicalName(), "#ffffff");
+
+		File baseLoc = File.createTempFile("test", "scratch");
+		baseLoc.delete();
+		assertTrue("Could not make directory " + baseLoc, baseLoc.mkdir());
+		configurationManager.setBaseConfigLocation(baseLoc);
+		configurationManager.store(manager);
+		configurationManager.populate(manager);
+		manager.getPreferredColour(dummy);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/pom.xml b/taverna-contextual-views-api/pom.xml
new file mode 100644
index 0000000..d0f10a1
--- /dev/null
+++ b/taverna-contextual-views-api/pom.xml
@@ -0,0 +1,99 @@
+<?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>taverna-contextual-views-api</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Contextual Views API</name>
+	<description>Contextual views for the activities</description>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-activity-icons-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-activity-palette-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-edits-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-file-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-helper-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-ui</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-observer</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+			<version>${taverna.language.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-services-api</artifactId>
+			<version>${taverna.osgi.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>${jackson.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>${log4j.version}</version>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityConfigurationAction.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityConfigurationAction.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityConfigurationAction.java
new file mode 100644
index 0000000..4d2fcff
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityConfigurationAction.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * 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.ui.actions.activity;
+
+import java.util.List;
+import java.util.WeakHashMap;
+
+import javax.swing.AbstractAction;
+import javax.swing.JDialog;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.events.ClosingDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ActivityConfigurationDialog;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+@SuppressWarnings("serial")
+public abstract class ActivityConfigurationAction extends AbstractAction {
+	private static WeakHashMap<Activity, ActivityConfigurationDialog> configurationDialogs = new WeakHashMap<>();
+	private static DataflowCloseListener listener;
+
+	protected Activity activity;
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public ActivityConfigurationAction(Activity activity,
+			ActivityIconManager activityIconManager,
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		this.activity = activity;
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+		putValue(SMALL_ICON,
+				activityIconManager.iconForActivity(activity.getType()));
+	}
+
+	protected Activity getActivity() {
+		return activity;
+	}
+
+	protected ServiceDescription getServiceDescription() {
+		return serviceDescriptionRegistry.getServiceDescription(activity
+				.getType());
+	}
+
+	protected static void setDialog(Activity activity,
+			ActivityConfigurationDialog dialog, FileManager fileManager) {
+		if (listener == null) {
+			listener = new DataflowCloseListener();
+			/*
+			 * Ensure that the DataflowCloseListener is the first notified
+			 * listener. Otherwise you cannot save the configurations.
+			 */
+			List<Observer<FileManagerEvent>> existingListeners = fileManager
+					.getObservers();
+			fileManager.addObserver(listener);
+			for (Observer<FileManagerEvent> observer : existingListeners)
+				if (!observer.equals(listener)) {
+					fileManager.removeObserver(observer);
+					fileManager.addObserver(observer);
+				}
+		}
+		if (configurationDialogs.containsKey(activity)) {
+			ActivityConfigurationDialog currentDialog = configurationDialogs
+					.get(activity);
+			if (!currentDialog.equals(dialog) && currentDialog.isVisible())
+				currentDialog.setVisible(false);
+		}
+		configurationDialogs.put(activity, dialog);
+		dialog.setVisible(true);
+	}
+
+	public static void clearDialog(Activity activity) {
+		if (configurationDialogs.containsKey(activity)) {
+			ActivityConfigurationDialog currentDialog = configurationDialogs
+					.get(activity);
+			if (currentDialog.isVisible())
+				currentDialog.setVisible(false);
+			configurationDialogs.remove(activity);
+			currentDialog.dispose();
+		}
+	}
+
+	protected static void clearDialog(JDialog dialog) {
+		if (configurationDialogs.containsValue(dialog)) {
+			if (dialog.isVisible())
+				dialog.setVisible(false);
+			for (Activity activity : configurationDialogs.keySet())
+				if (configurationDialogs.get(activity).equals(dialog))
+					configurationDialogs.remove(activity);
+			dialog.dispose();
+		}
+	}
+
+	public static boolean closeDialog(Activity activity) {
+		boolean closeIt = true;
+		if (configurationDialogs.containsKey(activity)) {
+			ActivityConfigurationDialog currentDialog = configurationDialogs
+					.get(activity);
+			if (currentDialog.isVisible())
+				closeIt = currentDialog.closeDialog();
+			if (closeIt)
+				configurationDialogs.remove(activity);
+		}
+		return closeIt;
+	}
+
+	public static ActivityConfigurationDialog getDialog(Activity activity) {
+		return configurationDialogs.get(activity);
+	}
+
+	private static class DataflowCloseListener implements
+			Observer<FileManagerEvent> {
+		private Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+		@Override
+		public void notify(Observable<FileManagerEvent> sender,
+				FileManagerEvent message) throws Exception {
+			if (message instanceof ClosingDataflowEvent) {
+				ClosingDataflowEvent closingDataflowEvent = (ClosingDataflowEvent) message;
+				if (closingDataflowEvent.isAbortClose())
+					return;
+				closingDataflow(closingDataflowEvent,
+						((ClosingDataflowEvent) message).getDataflow());
+			}
+		}
+
+		private void closingDataflow(ClosingDataflowEvent event,
+				WorkflowBundle bundle) {
+			Profile profile = bundle.getMainProfile();
+			for (Workflow workflow : bundle.getWorkflows())
+				for (Processor p : workflow.getProcessors()) {
+					ProcessorBinding processorBinding = scufl2Tools
+							.processorBindingForProcessor(p, profile);
+					Activity activity = processorBinding.getBoundActivity();
+					if (!closeDialog(activity))
+						event.setAbortClose(true);
+				}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityContextualView.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityContextualView.java
new file mode 100644
index 0000000..f063c02
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityContextualView.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * 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.ui.actions.activity;
+
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+
+/**
+ * A contextual view specific to an Activity. Concrete subclasses must
+ * initialise the view by calling {@link #initView()}.
+ * <p>
+ * The implementation provides a view based upon the properties set in the
+ * Configuration
+ * 
+ * @author Stuart Owen
+ * @author Ian Dunlop
+ * 
+ * @see Activity
+ * @see ContextualView
+ */
+@SuppressWarnings("serial")
+public abstract class ActivityContextualView extends ContextualView {
+	private Activity activity;
+	private Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	/**
+	 * Constructs an instance of the view.
+	 * <p>
+	 * The constructor parameter for the implementation of this class should
+	 * define the specific Activity type itself.
+	 * 
+	 * @param activity
+	 */
+	protected ActivityContextualView(Activity activity) {
+		super();
+		this.activity = activity;
+	}
+
+	public Activity getActivity() {
+		return activity;
+	}
+
+	public Configuration getConfigBean() {
+		return scufl2Tools.configurationFor(activity, activity.getParent());
+	}
+
+	@Override
+	public abstract void refreshView();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/HTMLBasedActivityContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/HTMLBasedActivityContextualView.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/HTMLBasedActivityContextualView.java
new file mode 100644
index 0000000..de3df33
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/HTMLBasedActivityContextualView.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.ui.actions.activity;
+
+import static net.sf.taverna.t2.lang.ui.HtmlUtils.buildTableOpeningTag;
+import static net.sf.taverna.t2.lang.ui.HtmlUtils.createEditorPane;
+import static net.sf.taverna.t2.lang.ui.HtmlUtils.getHtmlHead;
+import static net.sf.taverna.t2.lang.ui.HtmlUtils.panelForHtml;
+
+import javax.swing.JComponent;
+import javax.swing.JEditorPane;
+
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+import uk.org.taverna.scufl2.api.activity.Activity;
+
+@SuppressWarnings("serial")
+public abstract class HTMLBasedActivityContextualView extends ActivityContextualView {
+	private static final String BEANSHELL_URI = "http://ns.taverna.org.uk/2010/activity/beanshell";
+	private static final String LOCALWORKER_URI = "http://ns.taverna.org.uk/2010/activity/localworker";
+	private JEditorPane editorPane;
+	private final ColourManager colourManager;
+
+	public HTMLBasedActivityContextualView(Activity activity, ColourManager colourManager) {
+		super(activity);
+		this.colourManager = colourManager;
+		initView();
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		editorPane = createEditorPane(buildHtml());
+		return panelForHtml(editorPane);
+	}
+
+	private String buildHtml() {
+		StringBuilder html = new StringBuilder(getHtmlHead(getBackgroundColour()));
+		html.append(buildTableOpeningTag());
+		html.append("<tr><th colspan=\"2\">").append(getViewTitle()).append("</th></tr>");
+		html.append(getRawTableRowsHtml()).append("</table>");
+		html.append("</body></html>");
+		return html.toString();
+	}
+
+	protected abstract String getRawTableRowsHtml();
+
+	public String getBackgroundColour() {
+		String activityType = getActivity().getType().toString();
+		if (LOCALWORKER_URI.equals(activityType))
+			if (getConfigBean().getJson().get("isAltered").booleanValue())
+				return (String) colourManager.getProperty(BEANSHELL_URI);
+		String colour = (String) colourManager.getProperty(activityType);
+		return colour == null ? "#ffffff" : colour;
+	}
+
+	/**
+	 * Update the html view with the latest information in the configuration
+	 * bean
+	 */
+	@Override
+	public void refreshView() {
+		editorPane.setText(buildHtml());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/AddLayerFactorySPI.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/AddLayerFactorySPI.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/AddLayerFactorySPI.java
new file mode 100644
index 0000000..6b0245c
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/AddLayerFactorySPI.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+
+/**
+ * SPI for adding dispatch stack layers to a processor, such as
+ * {@link net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Loop}.
+ * <p>
+ * Buttons or similar will be added in the processor contextual view.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public interface AddLayerFactorySPI {
+	boolean canAddLayerFor(Processor proc);
+
+	Action getAddLayerActionFor(Processor proc);
+
+	boolean canCreateLayerClass(URI dispatchLayerType);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/ContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/ContextualView.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/ContextualView.java
new file mode 100644
index 0000000..45efaab
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/ContextualView.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews;
+
+import static java.awt.BorderLayout.CENTER;
+
+import java.awt.BorderLayout;
+import java.awt.Frame;
+
+import javax.swing.Action;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+
+/**
+ * An abstract class defining the base container to hold a contextual view over
+ * Dataflow element.
+ * <p>
+ * The specific implementation of this class to support a given dataflow element
+ * needs to implement the {@link #getMainFrame()} and {@link #getViewTitle()}.
+ * <p>
+ * If a view is associated with an action handler to configure this component,
+ * then the {@link #getConfigureAction(Frame) getConfigureAction} handler must
+ * be over-ridden. If this returns null then the configure button is left
+ * disabled and it is not possible to configure the element.
+ * 
+ * @author Stuart Owen
+ * @author Ian Dunlop
+ * @author Alan R Williams
+ */
+@SuppressWarnings("serial")
+public abstract class ContextualView extends JPanel {
+	/**
+	 * When implemented, this method should define the main frame that is placed
+	 * in this container, and provides a static view of the Dataflow element.
+	 * 
+	 * @return a JComponent that represents the dataflow element.
+	 */
+	public abstract JComponent getMainFrame();
+
+	/**
+	 * @return a String providing a title for the view
+	 */
+	public abstract String getViewTitle();
+
+	/**
+	 * Allows the item to be configured, but returning an action handler that
+	 * will be invoked when selecting to configure. By default this is provided
+	 * by a button.
+	 * <p>
+	 * If there is no ability to configure the given item, then this should
+	 * return null.
+	 * 
+	 * @param owner
+	 *            the owning dialog to be used when displaying dialogues for
+	 *            configuration options
+	 * @return an action that allows the element being viewed to be configured.
+	 */
+	public Action getConfigureAction(Frame owner) {
+		return null;
+	}
+
+	/**
+	 * This <i>must</i> be called by any sub-classes after they have initialised
+	 * their own view since it gets their main panel and adds it to the main
+	 * contextual view. If you don't do this you will get a very empty frame
+	 * popping up!
+	 */
+	public void initView() {
+		setLayout(new BorderLayout());
+		add(getMainFrame(), CENTER);
+		setName(getViewTitle());
+	}
+
+	public abstract void refreshView();
+
+	public abstract int getPreferredPosition();
+
+	public static String getTextFromDepth(String kind, Integer depth) {
+		String labelText = "The last prediction said the " + kind;
+		if (depth == null) {
+			labelText += " would not transmit a value";
+		} else if (depth == -1) {
+			labelText += " was invalid/unpredicted";
+		} else if (depth == 0) {
+			labelText += " would carry a single value";
+		} else {
+			labelText += " would carry a list of depth " + depth;
+		}
+		return labelText;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationDialog.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationDialog.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationDialog.java
new file mode 100644
index 0000000..c4c77b7
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationDialog.java
@@ -0,0 +1,474 @@
+package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity;
+
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.Cursor.DEFAULT_CURSOR;
+import static java.awt.Cursor.WAIT_CURSOR;
+import static java.awt.Cursor.getPredefinedCursor;
+import static java.lang.Math.max;
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.NO_OPTION;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_NO_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow;
+import static net.sf.taverna.t2.workbench.helper.Helper.showHelp;
+import static net.sf.taverna.t2.workbench.ui.actions.activity.ActivityConfigurationAction.clearDialog;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.lang.ui.DeselectingButton;
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.EditManager.DataFlowRedoEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.DataFlowUndoEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+import net.sf.taverna.t2.workflow.edits.AddChildEdit;
+import net.sf.taverna.t2.workflow.edits.AddProcessorInputPortEdit;
+import net.sf.taverna.t2.workflow.edits.AddProcessorOutputPortEdit;
+import net.sf.taverna.t2.workflow.edits.ChangeDepthEdit;
+import net.sf.taverna.t2.workflow.edits.ChangeGranularDepthEdit;
+import net.sf.taverna.t2.workflow.edits.ChangeJsonEdit;
+import net.sf.taverna.t2.workflow.edits.RemoveChildEdit;
+import net.sf.taverna.t2.workflow.edits.RemoveProcessorInputPortEdit;
+import net.sf.taverna.t2.workflow.edits.RemoveProcessorOutputPortEdit;
+import net.sf.taverna.t2.workflow.edits.RenameEdit;
+
+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.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.ActivityPort;
+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.OutputProcessorPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+@SuppressWarnings("serial")
+public class ActivityConfigurationDialog extends HelpEnabledDialog {
+	private enum PortType {
+		INPUT, OUTPUT
+	}
+	
+	protected static Logger logger = Logger.getLogger(ActivityConfigurationDialog.class);
+	private static final Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	private final EditManager editManager;
+
+	private Activity activity;
+	private ActivityConfigurationPanel panel;
+	protected WorkflowBundle owningWorkflowBundle;
+	protected Processor owningProcessor;
+	private Observer<EditManagerEvent> observer;
+	Dimension minimalSize = null;
+	Dimension buttonPanelSize = null;
+	JPanel buttonPanel;
+	protected JButton applyButton;
+
+	public ActivityConfigurationDialog(Activity a, ActivityConfigurationPanel p,
+			EditManager editManager) {
+		super(getMainWindow(), "Configuring " + a.getClass().getSimpleName(),
+				false, null);
+		this.activity = a;
+		this.panel = p;
+		this.editManager = editManager;
+
+		owningWorkflowBundle = activity.getParent().getParent();
+		owningProcessor = findProcessor(a);
+
+		setTitle(getRelativeName(owningWorkflowBundle, activity));
+		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+		setLayout(new BorderLayout());
+
+		add(panel, BorderLayout.CENTER);
+
+		buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+		buttonPanel.setBorder(new EmptyBorder(5, 20, 5, 5));
+
+		JButton helpButton = new DeselectingButton("Help", new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				showHelp(panel);
+			}
+		});
+		buttonPanel.add(helpButton);
+
+		applyButton = new DeselectingButton("Apply", new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				/*
+				 * For the moment it always does an apply as what should be
+				 * happening is that the apply button only becomes available
+				 * when the configuration has changed. However, many
+				 * configuration panels are not set up to detected changes
+				 */
+				// if (panel.isConfigurationChanged()) {
+				if (checkPanelValues())
+					applyConfiguration();
+				// } else {
+				// logger.info("Ignoring apply");
+				// }
+			}
+		});
+		buttonPanel.add(applyButton);
+
+		JButton closeButton = new DeselectingButton("Close", new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				closeDialog();
+			}
+		});
+		buttonPanel.add(closeButton);
+
+		add(buttonPanel, SOUTH);
+
+		this.addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowOpened(WindowEvent e) {
+				requestFocusInWindow();
+				panel.whenOpened();
+			}
+
+			@Override
+			public void windowClosing(WindowEvent e) {
+				closeDialog();
+			}
+		});
+		pack();
+		minimalSize = getSize();
+		setLocationRelativeTo(null);
+		setResizable(true);
+		addComponentListener(new ComponentAdapter() {
+			@Override
+			public void componentResized(ComponentEvent e) {
+				int newWidth = max(getWidth(), minimalSize.width);
+				int newHeight = max(getHeight(), minimalSize.height);
+				setSize(new Dimension(newWidth, newHeight));
+			}
+		});
+
+		observer = new Observer<EditManagerEvent>() {
+			@Override
+			public void notify(Observable<EditManagerEvent> sender, EditManagerEvent message)
+					throws Exception {
+				logger.info("sender is a " + sender.getClass().getCanonicalName());
+				logger.info("message is a " + message.getClass().getCanonicalName());
+				Edit<?> edit = message.getEdit();
+				logger.info(edit.getClass().getCanonicalName());
+				considerEdit(message, edit);
+			}
+		};
+		editManager.addObserver(observer);
+	}
+
+	private boolean checkPanelValues() {
+		boolean result = false;
+		try {
+			setCursor(getPredefinedCursor(WAIT_CURSOR));
+			result = panel.checkValues();
+		} finally {
+			setCursor(getPredefinedCursor(DEFAULT_CURSOR));
+		}
+		return result;
+	}
+
+	private void considerEdit(EditManagerEvent message, Edit<?> edit) {
+		// boolean result = false;
+		if (edit instanceof CompoundEdit) {
+			for (Edit<?> subEdit : ((CompoundEdit) edit).getChildEdits())
+				considerEdit(message, subEdit);
+			return;
+		}
+
+		Object subject = edit.getSubject();
+		if (subject == owningProcessor) {
+			// panel.reevaluate();
+			setTitle(getRelativeName(owningWorkflowBundle, activity));
+		} else if (subject == owningWorkflowBundle) {
+			for (Workflow workflow : owningWorkflowBundle.getWorkflows())
+				if (!workflow.getProcessors().contains(owningProcessor))
+					clearDialog(activity);
+		} else if (subject == activity) {
+			if (message instanceof DataFlowUndoEvent) {
+				logger.info("undo of activity edit found");
+				panel.refreshConfiguration();
+			} else if (message instanceof DataFlowRedoEvent) {
+				logger.info("redo of activity edit found");
+				panel.refreshConfiguration();
+			}
+		}
+	}
+
+	protected void configureActivity(ObjectNode json, List<ActivityPortConfiguration> inputPorts,
+			List<ActivityPortConfiguration> outputPorts) {
+		configureActivity(owningWorkflowBundle, activity, json, inputPorts, outputPorts);
+	}
+
+	public void configureActivity(WorkflowBundle workflowBundle, Activity activity,
+			ObjectNode json, List<ActivityPortConfiguration> inputPorts,
+			List<ActivityPortConfiguration> outputPorts) {
+		try {
+			List<Edit<?>> editList = new ArrayList<Edit<?>>();
+			Profile profile = activity.getParent();
+			List<ProcessorBinding> processorBindings = scufl2Tools
+					.processorBindingsToActivity(activity);
+			Configuration configuration = scufl2Tools.configurationFor(activity, profile);
+			editList.add(new ChangeJsonEdit(configuration, json));
+
+			configurePorts(activity, editList, processorBindings, inputPorts, PortType.INPUT);
+			configurePorts(activity, editList, processorBindings, outputPorts, PortType.OUTPUT);
+			editManager.doDataflowEdit(workflowBundle, new CompoundEdit(editList));
+		} catch (IllegalStateException | EditException e) {
+			logger.error(e);
+		}
+	}
+
+	private void configurePorts(Activity activity, List<Edit<?>> editList,
+			List<ProcessorBinding> processorBindings,
+			List<ActivityPortConfiguration> portDefinitions, PortType portType) {
+		Set<ActivityPort> ports = new HashSet<>();
+		for (ActivityPort activityPort : portType == PortType.INPUT ? activity
+				.getInputPorts() : activity.getOutputPorts())
+			ports.add(activityPort);
+		for (ActivityPortConfiguration portDefinition : portDefinitions) {
+			String portName = portDefinition.getName();
+			int portDepth = portDefinition.getDepth();
+			int granularPortDepth = portDefinition.getGranularDepth();
+			ActivityPort activityPort = portDefinition.getActivityPort();
+			if (activityPort == null) {
+				// no activity port so add a new one
+				if (portType == PortType.INPUT)
+					createInputPort(activity, editList, processorBindings, portDefinition);
+				else
+					createOutputPort(activity, editList, processorBindings, portDefinition);
+			} else {
+				ports.remove(activityPort);
+				// check if port has changed
+				for (ProcessorBinding processorBinding : processorBindings)
+					if (portType == PortType.INPUT)
+						for (ProcessorInputPortBinding portBinding : processorBinding
+								.getInputPortBindings()) {
+							if (!portBinding.getBoundActivityPort().equals(
+									activityPort))
+								continue;
+							InputProcessorPort processorPort = portBinding
+									.getBoundProcessorPort();
+							if (!activityPort.getName().equals(portName))
+								// port name changed
+								if (processorPort.getName().equals(activityPort.getName()))
+									// default mapping so change processor port
+									editList.add(new RenameEdit<>(processorPort, portName));
+							if (!processorPort.getDepth().equals(portDepth))
+								// port depth changed
+								editList.add(new ChangeDepthEdit<>(
+										processorPort, portDepth));
+						}
+					else
+						for (ProcessorOutputPortBinding portBinding : processorBinding
+								.getOutputPortBindings()) {
+							if (!portBinding.getBoundActivityPort().equals(
+									activityPort))
+								continue;
+							OutputProcessorPort processorPort = portBinding
+									.getBoundProcessorPort();
+							if (!activityPort.getName().equals(portName))
+								// port name changed
+								if (processorPort.getName().equals(
+										activityPort.getName()))
+									// default mapping so change processor port
+									editList.add(new RenameEdit<>(
+											processorPort, portName));
+							if (!processorPort.getDepth().equals(portDepth))
+								// port depth changed
+								editList.add(new ChangeDepthEdit<>(
+										processorPort, portDepth));
+							if (!processorPort.getGranularDepth().equals(
+									granularPortDepth))
+								// port granular depth changed
+								editList.add(new ChangeGranularDepthEdit<>(
+										processorPort, granularPortDepth));
+						}
+				if (!activityPort.getName().equals(portName))
+					// port name changed
+					editList.add(new RenameEdit<>(activityPort, portName));
+				if (!activityPort.getDepth().equals(portDepth))
+					// port depth changed
+					editList.add(new ChangeDepthEdit<>(activityPort, portDepth));
+				if (activityPort instanceof OutputActivityPort) {
+					OutputActivityPort outputActivityPort = (OutputActivityPort) activityPort;
+					Integer granularDepth = outputActivityPort
+							.getGranularDepth();
+					if (granularDepth == null
+							|| !granularDepth.equals(granularPortDepth))
+						// granular port depth changed
+						editList.add(new ChangeGranularDepthEdit<>(
+								outputActivityPort, granularPortDepth));
+				}
+			}
+		}
+
+		// remove any unconfigured ports
+		for (ActivityPort activityPort : ports) {
+			// remove processor ports and bindings
+			for (ProcessorBinding processorBinding : processorBindings)
+				if (portType.equals(PortType.INPUT))
+					for (ProcessorInputPortBinding portBinding : processorBinding
+							.getInputPortBindings()) {
+						if (portBinding.getBoundActivityPort().equals(activityPort)) {
+							editList.add(new RemoveProcessorInputPortEdit(processorBinding
+									.getBoundProcessor(), portBinding.getBoundProcessorPort()));
+							editList.add(new RemoveChildEdit<>(processorBinding,
+									portBinding));
+						}
+					}
+				else
+					for (ProcessorOutputPortBinding portBinding : processorBinding
+							.getOutputPortBindings())
+						if (portBinding.getBoundActivityPort().equals(activityPort)) {
+							editList.add(new RemoveProcessorOutputPortEdit(processorBinding
+									.getBoundProcessor(), portBinding.getBoundProcessorPort()));
+							editList.add(new RemoveChildEdit<>(processorBinding,
+									portBinding));
+						}
+			// remove activity port
+			editList.add(new RemoveChildEdit<Activity>(activity, activityPort));
+		}
+	}
+
+	private void createInputPort(Activity activity, List<Edit<?>> editList,
+			List<ProcessorBinding> processorBindings,
+			ActivityPortConfiguration portDefinition) {
+		InputActivityPort actPort = new InputActivityPort(null,
+				portDefinition.getName());
+		actPort.setDepth(portDefinition.getDepth());
+		// add port to activity
+		editList.add(new AddChildEdit<>(activity, actPort));
+		for (ProcessorBinding processorBinding : processorBindings) {
+			Processor processor = processorBinding.getBoundProcessor();
+			// add a new processor port
+			InputProcessorPort procPort = new InputProcessorPort();
+			procPort.setName(portDefinition.getName());
+			procPort.setDepth(portDefinition.getDepth());
+			editList.add(new AddProcessorInputPortEdit(processor, procPort));
+			// add a new port binding
+			ProcessorInputPortBinding binding = new ProcessorInputPortBinding();
+			binding.setBoundProcessorPort(procPort);
+			binding.setBoundActivityPort(actPort);
+			editList.add(new AddChildEdit<>(processorBinding, binding));
+		}
+	}
+
+	private void createOutputPort(Activity activity, List<Edit<?>> editList,
+			List<ProcessorBinding> processorBindings,
+			ActivityPortConfiguration portDefinition) {
+		OutputActivityPort actPort = new OutputActivityPort(null,
+				portDefinition.getName());
+		actPort.setDepth(portDefinition.getDepth());
+		actPort.setGranularDepth(portDefinition.getGranularDepth());
+		// add port to activity
+		editList.add(new AddChildEdit<Activity>(activity, actPort));
+		for (ProcessorBinding processorBinding : processorBindings) {
+			Processor processor = processorBinding.getBoundProcessor();
+			// add a new processor port
+			OutputProcessorPort procPort = new OutputProcessorPort();
+			procPort.setName(portDefinition.getName());
+			procPort.setDepth(portDefinition.getDepth());
+			procPort.setGranularDepth(portDefinition.getGranularDepth());
+			editList.add(new AddProcessorOutputPortEdit(processor, procPort));
+			// add a new port binding
+			ProcessorOutputPortBinding binding = new ProcessorOutputPortBinding();
+			binding.setBoundProcessorPort(procPort);
+			binding.setBoundActivityPort(actPort);
+			editList.add(new AddChildEdit<>(processorBinding, binding));
+		}
+	}
+
+	protected static Processor findProcessor(Activity activity) {
+		for (ProcessorBinding processorBinding : scufl2Tools
+				.processorBindingsToActivity(activity))
+			return processorBinding.getBoundProcessor();
+		return null;
+	}
+
+	public static String getRelativeName(WorkflowBundle workflowBundle, Activity activity) {
+		StringBuilder relativeName = new StringBuilder("");
+		if (workflowBundle != null) {
+			Workflow workflow = workflowBundle.getMainWorkflow();
+			if (workflow != null) {
+				relativeName.append(workflow.getName());
+				relativeName.append(":");
+			}
+		}
+		Processor processor = findProcessor(activity);
+		if (processor != null)
+			relativeName.append(processor.getName());
+		return relativeName.toString();
+	}
+
+	public boolean closeDialog() {
+		if (panel.isConfigurationChanged()) {
+			String relativeName = getRelativeName(owningWorkflowBundle, activity);
+			if (checkPanelValues()) {
+				int answer = showConfirmDialog(this,
+						"Do you want to save the configuration of " + relativeName + "?",
+						relativeName, YES_NO_CANCEL_OPTION);
+				if (answer == YES_OPTION) {
+					applyConfiguration();
+				} else if (answer == CANCEL_OPTION) {
+					return false;
+				}
+			} else if (showConfirmDialog(
+					this,
+					"New configuration could not be saved. Do you still want to close?",
+					relativeName, YES_NO_OPTION) == NO_OPTION)
+				return false;
+		}
+		panel.whenClosed();
+		clearDialog(activity);
+		return true;
+	}
+
+	private void applyConfiguration() {
+		panel.noteConfiguration();
+		configureActivity(panel.getJson(), panel.getInputPorts(),
+				panel.getOutputPorts());
+		panel.refreshConfiguration();
+	}
+
+	@Override
+	public void dispose() {
+		super.dispose();
+		editManager.removeObserver(observer);
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionRegistryImpl.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionRegistryImpl.java b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionRegistryImpl.java
new file mode 100644
index 0000000..9dc3f00
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionRegistryImpl.java
@@ -0,0 +1,652 @@
+/*******************************************************************************
+ * 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.servicedescriptions.impl;
+
+import static java.lang.System.currentTimeMillis;
+import static java.lang.Thread.MIN_PRIORITY;
+import static java.lang.Thread.currentThread;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.sf.taverna.t2.lang.observer.MultiCaster;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider.FindServiceDescriptionsCallBack;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.servicedescriptions.events.AddedProviderEvent;
+import net.sf.taverna.t2.servicedescriptions.events.PartialServiceDescriptionsNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderErrorNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderStatusNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderUpdatingNotification;
+import net.sf.taverna.t2.servicedescriptions.events.ProviderWarningNotification;
+import net.sf.taverna.t2.servicedescriptions.events.RemovedProviderEvent;
+import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionProvidedEvent;
+import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionRegistryEvent;
+import net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionDeserializer.DeserializationException;
+
+import org.apache.commons.beanutils.BeanUtils;
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+public class ServiceDescriptionRegistryImpl implements ServiceDescriptionRegistry {
+	/**
+	 * If a writable property of this name on a provider exists (ie. the provider has a method
+	 * setServiceDescriptionRegistry(ServiceDescriptionRegistry registry) - then this property will
+	 * be set to the current registry.
+	 */
+	public static final String SERVICE_DESCRIPTION_REGISTRY = "serviceDescriptionRegistry";
+	public static Logger logger = Logger.getLogger(ServiceDescriptionRegistryImpl.class);
+	public static final ThreadGroup threadGroup = new ThreadGroup("Service description providers");
+	/**
+	 * Total maximum timeout while waiting for description threads to finish
+	 */
+	private static final long DESCRIPTION_THREAD_TIMEOUT_MS = 3000;
+	protected static final String CONF_DIR = "conf";
+	protected static final String SERVICE_PROVIDERS_FILENAME = "service_providers.xml";
+	private static final String DEFAULT_CONFIGURABLE_SERVICE_PROVIDERS_FILENAME = "default_service_providers.xml";
+
+	private ServiceDescriptionsConfiguration serviceDescriptionsConfig;
+	private ApplicationConfiguration applicationConfiguration;
+	/**
+	 * <code>false</code> until first call to {@link #loadServiceProviders()} - which is done by
+	 * first call to {@link #getServiceDescriptionProviders()}.
+	 */
+	private boolean hasLoadedProviders = false;
+	/**
+	 * <code>true</code> while {@link #loadServiceProviders(File)},
+	 * {@link #loadServiceProviders(URL)} or {@link #loadServiceProviders()} is in progress, avoids
+	 * triggering {@link #saveServiceDescriptions()} on
+	 * {@link #addServiceDescriptionProvider(ServiceDescriptionProvider)} calls.
+	 */
+	private boolean loading = false;
+	private MultiCaster<ServiceDescriptionRegistryEvent> observers = new MultiCaster<>(this);
+	private List<ServiceDescriptionProvider> serviceDescriptionProviders;
+	private Set<ServiceDescriptionProvider> allServiceProviders;
+	private Map<ServiceDescriptionProvider, Set<ServiceDescription>> providerDescriptions = new HashMap<>();
+	private Map<ServiceDescriptionProvider, Thread> serviceDescriptionThreads = new HashMap<>();
+	/**
+	 * Service providers added by the user, should be saved
+	 */
+	private Set<ServiceDescriptionProvider> userAddedProviders = new HashSet<>();
+	private Set<ServiceDescriptionProvider> userRemovedProviders = new HashSet<>();
+	private Set<ServiceDescriptionProvider> defaultServiceDescriptionProviders;
+	/**
+	 * File containing a list of configured ConfigurableServiceProviders which is used to get the
+	 * default set of service descriptions together with those provided by AbstractTemplateServiceS.
+	 * This file is located in the conf directory of the Taverna startup directory.
+	 */
+	private File defaultConfigurableServiceProvidersFile;
+	private boolean defaultSystemConfigurableProvidersLoaded = false;
+
+	static {
+		threadGroup.setMaxPriority(MIN_PRIORITY);
+	}
+
+	public ServiceDescriptionRegistryImpl(
+			ApplicationConfiguration applicationConfiguration) {
+		this.applicationConfiguration = applicationConfiguration;
+		defaultConfigurableServiceProvidersFile = new File(
+				getTavernaStartupConfigurationDirectory(),
+				DEFAULT_CONFIGURABLE_SERVICE_PROVIDERS_FILENAME);
+	}
+
+	/**
+	 * Get the Taverna distribution (startup) configuration directory.
+	 */
+	private File getTavernaStartupConfigurationDirectory() {
+		File distroHome = null;
+		File configDirectory = null;
+		distroHome = applicationConfiguration.getStartupDir();
+		configDirectory = new File(distroHome, "conf");
+		if (!configDirectory.exists())
+			configDirectory.mkdir();
+		return configDirectory;
+	}
+
+	private static void joinThreads(Collection<? extends Thread> threads,
+			long descriptionThreadTimeoutMs) {
+		long finishJoinBy = currentTimeMillis() + descriptionThreadTimeoutMs;
+		for (Thread thread : threads) {
+			// No shorter timeout than 1 ms (thread.join(0) waits forever!)
+			long timeout = Math.max(1, finishJoinBy - currentTimeMillis());
+			try {
+				thread.join(timeout);
+			} catch (InterruptedException e) {
+				currentThread().interrupt();
+				return;
+			}
+			if (thread.isAlive())
+				logger.debug("Thread did not finish " + thread);
+		}
+	}
+
+
+	@Override
+	public void addObserver(Observer<ServiceDescriptionRegistryEvent> observer) {
+		observers.addObserver(observer);
+	}
+
+	@Override
+	public void addServiceDescriptionProvider(ServiceDescriptionProvider provider) {
+		synchronized (this) {
+			userRemovedProviders.remove(provider);
+			if (!getDefaultServiceDescriptionProviders().contains(provider))
+				userAddedProviders.add(provider);
+			allServiceProviders.add(provider);
+		}
+
+		// Spring-like auto-config
+		try {
+			// BeanUtils should ignore this if provider does not have that property
+			BeanUtils.setProperty(provider, SERVICE_DESCRIPTION_REGISTRY, this);
+		} catch (IllegalAccessException | InvocationTargetException e) {
+			logger.warn("Could not set serviceDescriptionRegistry on "
+					+ provider, e);
+		}
+
+		if (!loading)
+			saveServiceDescriptions();
+		observers.notify(new AddedProviderEvent(provider));
+		updateServiceDescriptions(false, false);
+	}
+
+	private File findServiceDescriptionsFile() {
+		File confDir = new File(
+				applicationConfiguration.getApplicationHomeDir(), CONF_DIR);
+		confDir.mkdirs();
+		if (!confDir.isDirectory())
+			throw new RuntimeException("Invalid directory: " + confDir);
+		File serviceDescriptionsFile = new File(confDir,
+				SERVICE_PROVIDERS_FILENAME);
+		return serviceDescriptionsFile;
+	}
+
+	@Override
+	public List<Observer<ServiceDescriptionRegistryEvent>> getObservers() {
+		return observers.getObservers();
+	}
+
+	// Fallback to this method that uses hardcoded default services if you cannot read them from
+	// the file.
+//	@SuppressWarnings("unchecked")
+//	public synchronized Set<ServiceDescriptionProvider> getDefaultServiceDescriptionProvidersFallback() {
+//		/*if (defaultServiceDescriptionProviders != null) {
+//	 return defaultServiceDescriptionProviders;
+//	 }
+//	 defaultServiceDescriptionProviders = new HashSet<ServiceDescriptionProvider>();
+//		 */
+//		for (ServiceDescriptionProvider provider : serviceDescriptionProviders) {
+//
+//			/* We do not need these - already loaded them from getDefaultServiceDescriptionProviders()
+//	 if (!(provider instanceof ConfigurableServiceProvider)) {
+//	 defaultServiceDescriptionProviders.add(provider);
+//	 continue;
+//	 }*/
+//
+//			// Just load the hard coded default configurable service providers
+//			if (provider instanceof ConfigurableServiceProvider){
+//				ConfigurableServiceProvider<Object> template = ((ConfigurableServiceProvider<Object>)
+//						provider);
+//				// Get configurations
+//				List<Object> configurables = template.getDefaultConfigurations();
+//				for (Object config : configurables) {
+//					// Make a copy that we can configure
+//					ConfigurableServiceProvider<Object> configurableProvider = template.clone();
+//					try {
+//						configurableProvider.configure(config);
+//					} catch (ConfigurationException e) {
+//						logger.warn("Can't configure provider "
+//								+ configurableProvider + " with " + config);
+//						continue;
+//					}
+//					defaultServiceDescriptionProviders.add(configurableProvider);
+//				}
+//			}
+//		}
+//		return defaultServiceDescriptionProviders;
+//	}
+
+	// Get the default services.
+	@Override
+	public synchronized Set<ServiceDescriptionProvider> getDefaultServiceDescriptionProviders() {
+		if (defaultServiceDescriptionProviders != null)
+			return defaultServiceDescriptionProviders;
+		defaultServiceDescriptionProviders = new HashSet<>();
+
+		/*
+		 * Add default configurable service description providers from the
+		 * default_service_providers.xml file
+		 */
+		if (defaultConfigurableServiceProvidersFile.exists()) {
+			try {
+				ServiceDescriptionDeserializer deserializer = new ServiceDescriptionDeserializer(
+						serviceDescriptionProviders);
+				defaultServiceDescriptionProviders.addAll(deserializer
+						.deserializeDefaults(this,
+								defaultConfigurableServiceProvidersFile));
+				/*
+				 * We have successfully loaded the defaults for system
+				 * configurable providers. Note that there are still defaults
+				 * for third party configurable providers, which will be loaded
+				 * below using getDefaultConfigurations().
+				 */
+				defaultSystemConfigurableProvidersLoaded = true;
+			} catch (Exception e) {
+				logger.error("Could not load default service providers from "
+						+ defaultConfigurableServiceProvidersFile.getAbsolutePath(), e);
+
+				/*
+				 * Fallback on the old hardcoded method of loading default
+				 * system configurable service providers using
+				 * getDefaultConfigurations().
+				 */
+				defaultSystemConfigurableProvidersLoaded = false;
+			}
+		} else {
+			logger.warn("Could not find the file "
+					+ defaultConfigurableServiceProvidersFile.getAbsolutePath()
+					+ " containing default system service providers. "
+					+ "Using the hardcoded list of default system providers.");
+
+			/*
+			 * Fallback on the old hardcoded method of loading default system
+			 * configurable service providers using getDefaultConfigurations().
+			 */
+			defaultSystemConfigurableProvidersLoaded = false;
+		}
+
+		/*
+		 * Load other default service description providers - template, local
+		 * workers and third party configurable service providers
+		 */
+		for (ServiceDescriptionProvider provider : serviceDescriptionProviders) {
+			/*
+			 * Template service providers (beanshell, string constant, etc. )
+			 * and providers of local workers.
+			 */
+			if (!(provider instanceof ConfigurableServiceProvider)) {
+				defaultServiceDescriptionProviders.add(provider);
+				continue;
+			}
+
+			/*
+			 * Default system or third party configurable service description
+			 * provider. System ones are read from the
+			 * default_service_providers.xml file so getDefaultConfigurations()
+			 * on them will not have much effect here unless
+			 * defaultSystemConfigurableProvidersLoaded is set to false.
+			 */
+			//FIXME needs to be designed to work using Configuration instances
+			//FIXME needs to get configurations via OSGi discovery
+			/*
+			ConfigurableServiceProvider template = (ConfigurableServiceProvider) provider;
+			// Get configurations
+			for (ObjectNode config : template.getDefaultConfigurations()) {
+				// Make a copy that we can configure
+				ConfigurableServiceProvider configurableProvider = template.clone();
+				try {
+					configurableProvider.configure(config);
+				} catch (ConfigurationException e) {
+					logger.warn("Can't configure provider "
+							+ configurableProvider + " with " + config);
+					continue;
+				}
+				defaultServiceDescriptionProviders.add(configurableProvider);
+			}
+			*/
+		}
+
+		return defaultServiceDescriptionProviders;
+	}
+
+	@Override
+	public synchronized Set<ServiceDescriptionProvider> getServiceDescriptionProviders() {
+		if (allServiceProviders != null)
+			return new HashSet<>(allServiceProviders);
+		allServiceProviders = new HashSet<>(userAddedProviders);
+		synchronized (this) {
+			if (!hasLoadedProviders)
+				try {
+					loadServiceProviders();
+				} catch (Exception e) {
+					logger.error("Could not load service providers", e);
+				} finally {
+					hasLoadedProviders = true;
+				}
+		}
+		for (ServiceDescriptionProvider provider : getDefaultServiceDescriptionProviders()) {
+			if (userRemovedProviders.contains(provider))
+				continue;
+			if (provider instanceof ConfigurableServiceProvider
+					&& !serviceDescriptionsConfig.isIncludeDefaults())
+				// We'll skip the default configurable service provders
+				continue;
+			allServiceProviders.add(provider);
+		}
+		return new HashSet<>(allServiceProviders);
+	}
+
+	@Override
+	public Set<ServiceDescriptionProvider> getServiceDescriptionProviders(
+			ServiceDescription sd) {
+		Set<ServiceDescriptionProvider> result = new HashSet<>();
+		for (ServiceDescriptionProvider sdp : providerDescriptions.keySet())
+			if (providerDescriptions.get(sdp).contains(sd))
+				result.add(sdp);
+		return result;
+	}
+
+	@Override
+	public Set<ServiceDescription> getServiceDescriptions() {
+		updateServiceDescriptions(false, true);
+		Set<ServiceDescription> serviceDescriptions = new HashSet<>();
+		synchronized (providerDescriptions) {
+			for (Set<ServiceDescription> providerDesc : providerDescriptions
+					.values())
+				serviceDescriptions.addAll(providerDesc);
+		}
+		return serviceDescriptions;
+	}
+
+	@Override
+	public ServiceDescription getServiceDescription(URI serviceType) {
+		for (ServiceDescription serviceDescription : getServiceDescriptions())
+			if (serviceDescription.getActivityType().equals(serviceType))
+				return serviceDescription;
+		return null;
+	}
+
+	@Override
+	public List<ConfigurableServiceProvider> getUnconfiguredServiceProviders() {
+		List<ConfigurableServiceProvider> providers = new ArrayList<>();
+		for (ServiceDescriptionProvider provider : serviceDescriptionProviders)
+			if (provider instanceof ConfigurableServiceProvider)
+				providers.add((ConfigurableServiceProvider) provider);
+		return providers;
+	}
+
+	@Override
+	public Set<ServiceDescriptionProvider> getUserAddedServiceProviders() {
+		return new HashSet<>(userAddedProviders);
+	}
+
+	@Override
+	public Set<ServiceDescriptionProvider> getUserRemovedServiceProviders() {
+		return new HashSet<>(userRemovedProviders);
+	}
+
+	@Override
+	public void loadServiceProviders() {
+		File serviceProviderFile = findServiceDescriptionsFile();
+		if (serviceProviderFile.isFile())
+			loadServiceProviders(serviceProviderFile);
+		hasLoadedProviders = true;
+	}
+
+	@Override
+	public void loadServiceProviders(File serviceProvidersFile) {
+		ServiceDescriptionDeserializer deserializer = new ServiceDescriptionDeserializer(
+				serviceDescriptionProviders);
+		loading = true;
+		try {
+			deserializer.deserialize(this, serviceProvidersFile);
+		} catch (DeserializationException e) {
+			logger.error("failed to deserialize configuration", e);
+		}
+		loading = false;
+	}
+
+	@Override
+	public void loadServiceProviders(URL serviceProvidersURL) {
+		ServiceDescriptionDeserializer deserializer = new ServiceDescriptionDeserializer(
+				serviceDescriptionProviders);
+		loading = true;
+		try {
+			deserializer.deserialize(this, serviceProvidersURL);
+		} catch (DeserializationException e) {
+			logger.error("failed to deserialize configuration", e);
+		}
+		loading = false;
+	}
+
+	@Override
+	public void refresh() {
+		updateServiceDescriptions(true, false);
+	}
+
+	@Override
+	public void removeObserver(Observer<ServiceDescriptionRegistryEvent> observer) {
+		observers.removeObserver(observer);
+	}
+
+	@Override
+	public synchronized void removeServiceDescriptionProvider(
+			ServiceDescriptionProvider provider) {
+		if (!userAddedProviders.remove(provider))
+			// Not previously added - must be a default one.. but should we remove it?
+			if (loading || serviceDescriptionsConfig.isRemovePermanently()
+					&& serviceDescriptionsConfig.isIncludeDefaults())
+				userRemovedProviders.add(provider);
+		if (allServiceProviders.remove(provider)) {
+			synchronized (providerDescriptions) {
+				Thread thread = serviceDescriptionThreads.remove(provider);
+				if (thread != null)
+					thread.interrupt();
+				providerDescriptions.remove(provider);
+			}
+			observers.notify(new RemovedProviderEvent(provider));
+		}
+		if (!loading)
+			saveServiceDescriptions();
+	}
+
+	@Override
+	public void saveServiceDescriptions() {
+		File serviceDescriptionsFile = findServiceDescriptionsFile();
+		saveServiceDescriptions(serviceDescriptionsFile);
+	}
+
+	@Override
+	public void saveServiceDescriptions(File serviceDescriptionsFile) {
+		ServiceDescriptionSerializer serializer = new ServiceDescriptionSerializer();
+		try {
+			serializer.serializeRegistry(this, serviceDescriptionsFile);
+		} catch (IOException e) {
+			throw new RuntimeException("Can't save service descriptions to "
+					+ serviceDescriptionsFile);
+		}
+	}
+
+	/**
+	 * Exports all configurable service providers (that give service
+	 * descriptions) currently found in the Service Registry (apart from service
+	 * templates and local services) regardless of who added them (user or
+	 * default system providers).
+	 * <p>
+	 * Unlike {@link #saveServiceDescriptions}, this export does not have the
+	 * "ignored providers" section as this is just a plain export of everything
+	 * in the Service Registry.
+	 * 
+	 * @param serviceDescriptionsFile
+	 */
+	@Override
+	public void exportCurrentServiceDescriptions(File serviceDescriptionsFile) {
+		ServiceDescriptionSerializer serializer = new ServiceDescriptionSerializer();
+		try {
+			serializer.serializeFullRegistry(this, serviceDescriptionsFile);
+		} catch (IOException e) {
+			throw new RuntimeException("Could not save service descriptions to "
+					+ serviceDescriptionsFile);
+		}
+	}
+
+	public void setServiceDescriptionProvidersList(
+			List<ServiceDescriptionProvider> serviceDescriptionProviders) {
+		this.serviceDescriptionProviders = serviceDescriptionProviders;
+	}
+
+	private void updateServiceDescriptions(boolean refreshAll, boolean waitFor) {
+		List<Thread> threads = new ArrayList<>();
+		for (ServiceDescriptionProvider provider : getServiceDescriptionProviders()) {
+			synchronized (providerDescriptions) {
+				if (providerDescriptions.containsKey(provider) && !refreshAll)
+					// We'll used the cached values
+					continue;
+				Thread oldThread = serviceDescriptionThreads.get(provider);
+				if (oldThread != null && oldThread.isAlive()) {
+					if (refreshAll)
+						// New thread will override the old thread
+						oldThread.interrupt();
+					else {
+						// observers.notify(new ProviderStatusNotification(provider, "Waiting for provider"));
+						continue;
+					}
+				}
+				// Not run yet - we'll start a new tread
+				Thread thread = new FindServiceDescriptionsThread(provider);
+				threads.add(thread);
+				serviceDescriptionThreads.put(provider, thread);
+				thread.start();
+			}
+		}
+		if (waitFor)
+			joinThreads(threads, DESCRIPTION_THREAD_TIMEOUT_MS);
+	}
+
+	@Override
+	public boolean isDefaultSystemConfigurableProvidersLoaded() {
+		return defaultSystemConfigurableProvidersLoaded;
+	}
+
+	/**
+	 * Sets the serviceDescriptionsConfig.
+	 * 
+	 * @param serviceDescriptionsConfig
+	 *            the new value of serviceDescriptionsConfig
+	 */
+	public void setServiceDescriptionsConfig(
+			ServiceDescriptionsConfiguration serviceDescriptionsConfig) {
+		this.serviceDescriptionsConfig = serviceDescriptionsConfig;
+	}
+
+	class FindServiceDescriptionsThread extends Thread implements
+			UncaughtExceptionHandler, FindServiceDescriptionsCallBack {
+		private final ServiceDescriptionProvider provider;
+		private boolean aborting = false;
+		private final Set<ServiceDescription> providerDescs = new HashSet<>();
+
+		FindServiceDescriptionsThread(ServiceDescriptionProvider provider) {
+			super(threadGroup, "Find service descriptions from " + provider);
+			this.provider = provider;
+			setUncaughtExceptionHandler(this);
+			setDaemon(true);
+		}
+
+		@Override
+		public void fail(String message, Throwable ex) {
+			logger.warn("Provider " + getProvider() + ": " + message, ex);
+			if (aborting)
+				return;
+			observers.notify(new ProviderErrorNotification(getProvider(),
+					message, ex));
+		}
+
+		@Override
+		public void finished() {
+			if (aborting)
+				return;
+			synchronized (providerDescriptions) {
+				providerDescriptions.put(getProvider(), providerDescs);
+			}
+			observers.notify(new ServiceDescriptionProvidedEvent(getProvider(),
+					providerDescs));
+		}
+
+		@Override
+		public void partialResults(
+				Collection<? extends ServiceDescription> serviceDescriptions) {
+			if (aborting)
+				return;
+			providerDescs.addAll(serviceDescriptions);
+			synchronized (providerDescriptions) {
+				providerDescriptions.put(getProvider(), providerDescs);
+			}
+			observers.notify(new PartialServiceDescriptionsNotification(
+					getProvider(), serviceDescriptions));
+		}
+
+		@Override
+		public void status(String message) {
+			logger.debug("Provider " + getProvider() + ": " + message);
+			if (aborting)
+				return;
+			observers.notify(new ProviderStatusNotification(getProvider(),
+					message));
+		}
+
+		@Override
+		public void warning(String message) {
+			logger.warn("Provider " + getProvider() + ": " + message);
+			if (aborting)
+				return;
+			observers.notify(new ProviderWarningNotification(getProvider(),
+					message));
+		}
+
+		public ServiceDescriptionProvider getProvider() {
+			return provider;
+		}
+
+		@Override
+		public void interrupt() {
+			aborting = true;
+			super.interrupt();
+		}
+
+		@Override
+		public void run() {
+			observers.notify(new ProviderUpdatingNotification(provider));
+			getProvider().findServiceDescriptionsAsync(this);
+		}
+
+		@Override
+		public void uncaughtException(Thread t, Throwable ex) {
+			logger.error("Uncaught exception in " + t, ex);
+			fail("Uncaught exception", ex);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionSerializer.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionSerializer.java b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionSerializer.java
new file mode 100644
index 0000000..8a047a3
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionSerializer.java
@@ -0,0 +1,102 @@
+package net.sf.taverna.t2.servicedescriptions.impl;
+
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.CONFIGURATION;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.IGNORED;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDERS;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDER_ID;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.SERVICE_PANEL_CONFIGURATION;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.TYPE;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Set;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+import org.apache.log4j.Logger;
+import org.jdom.JDOMException;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+class ServiceDescriptionSerializer {
+	private static Logger logger = Logger
+			.getLogger(ServiceDescriptionSerializer.class);
+
+	public void serializeRegistry(ServiceDescriptionRegistry registry, File file)
+			throws IOException {
+		Set<ServiceDescriptionProvider> ignoreProviders = registry
+				.getUserRemovedServiceProviders();
+		JsonNode registryElement = serializeRegistry(registry, ignoreProviders);
+		try (BufferedOutputStream bufferedOutStream = new BufferedOutputStream(
+				new FileOutputStream(file))) {
+			bufferedOutStream.write(registryElement.toString()
+					.getBytes("UTF-8"));
+		}
+	}
+
+	/**
+	 * Export the whole service registry to an xml file, regardless of who added
+	 * the service provider (user or system default). In this case there will be
+	 * no "ignored providers" in the saved file.
+	 */
+	public void serializeFullRegistry(ServiceDescriptionRegistry registry,
+			File file) throws IOException {
+		JsonNode registryElement = serializeRegistry(registry, ALL_PROVIDERS);
+		try (BufferedOutputStream bufferedOutStream = new BufferedOutputStream(
+				new FileOutputStream(file))) {
+			bufferedOutStream.write(registryElement.toString()
+					.getBytes("UTF-8"));
+		}
+	}
+
+	private static final JsonNodeFactory factory = JsonNodeFactory.instance;
+	private static final Set<ServiceDescriptionProvider> ALL_PROVIDERS = null;
+
+	private JsonNode serializeRegistry(ServiceDescriptionRegistry registry,
+			Set<ServiceDescriptionProvider> ignoreProviders) {
+		ObjectNode overallConfiguration = factory.objectNode();
+		overallConfiguration.put(SERVICE_PANEL_CONFIGURATION,
+				ignoreProviders != ALL_PROVIDERS ? "full" : "defaults only");
+		ArrayNode providers = overallConfiguration.putArray(PROVIDERS);
+
+		for (ServiceDescriptionProvider provider : registry
+				.getUserAddedServiceProviders())
+			try {
+				providers.add(serializeProvider(provider));
+			} catch (JDOMException | IOException e) {
+				logger.warn("Could not serialize " + provider, e);
+			}
+
+		if (ignoreProviders != ALL_PROVIDERS) {
+			ArrayNode ignored = overallConfiguration.putArray(IGNORED);
+			for (ServiceDescriptionProvider provider : ignoreProviders)
+				try {
+					ignored.add(serializeProvider(provider));
+				} catch (JDOMException | IOException e) {
+					logger.warn("Could not serialize " + provider, e);
+				}
+		}
+
+		return overallConfiguration;
+	}
+
+	private JsonNode serializeProvider(ServiceDescriptionProvider provider)
+			throws JDOMException, IOException {
+		ObjectNode node = factory.objectNode();
+		node.put(PROVIDER_ID, provider.getId());
+
+		if (provider instanceof ConfigurableServiceProvider) {
+			ConfigurableServiceProvider configurable = (ConfigurableServiceProvider) provider;
+			node.put(TYPE, configurable.getConfiguration().getType().toString());
+			node.put(CONFIGURATION, configurable.getConfiguration().getJson());
+		}
+		return node;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionXMLConstants.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionXMLConstants.java b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionXMLConstants.java
new file mode 100644
index 0000000..ee180a7
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionXMLConstants.java
@@ -0,0 +1,15 @@
+package net.sf.taverna.t2.servicedescriptions.impl;
+
+import org.jdom.Namespace;
+
+public interface ServiceDescriptionXMLConstants {
+
+	public static final Namespace SERVICE_DESCRIPTION_NS = Namespace
+			.getNamespace("http://taverna.sf.net/2009/xml/servicedescription");
+	public static final String PROVIDER = "provider";
+	public static final String PROVIDERS = "providers";
+	public static final String SERVICE_DESCRIPTIONS = "serviceDescriptions";
+	public static final String IGNORED_PROVIDERS = "ignoredProviders";
+	public static final String PROVIDER_IDENTIFIER = "providerId";
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionsConfigurationImpl.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionsConfigurationImpl.java b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionsConfigurationImpl.java
new file mode 100644
index 0000000..ef0295c
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionsConfigurationImpl.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * 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.servicedescriptions.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration;
+
+import uk.org.taverna.configuration.AbstractConfigurable;
+import uk.org.taverna.configuration.ConfigurationManager;
+
+public class ServiceDescriptionsConfigurationImpl extends AbstractConfigurable
+		implements ServiceDescriptionsConfiguration {
+	private static final String INCLUDE_DEFAULTS = "includeDefaults";
+	private static final String SERVICE_PALETTE = "Service providers";
+	private static final String SERVICE_PALETTE_PREFIX = "ServiceProviders";
+	private static final String CATEGORY = "Services";
+	private static final String UUID = "f0d1ef24-9337-412f-b2c3-220a01e2efd0";
+	private static final String REMOVE_PERMANENTLY = "removePermanently";
+
+	public ServiceDescriptionsConfigurationImpl(
+			ConfigurationManager configurationManager) {
+		super(configurationManager);
+	}
+
+	@Override
+	public String getCategory() {
+		return CATEGORY;
+	}
+
+	@Override
+	public Map<String, String> getDefaultPropertyMap() {
+		Map<String, String> defaults = new HashMap<String, String>();
+		defaults.put(INCLUDE_DEFAULTS, "true");
+		defaults.put(REMOVE_PERMANENTLY, "true");
+		return defaults;
+	}
+
+	@Override
+	public String getDisplayName() {
+		return SERVICE_PALETTE;
+	}
+
+	@Override
+	public String getFilePrefix() {
+		return SERVICE_PALETTE_PREFIX;
+	}
+
+	@Override
+	public String getUUID() {
+		return UUID;
+	}
+
+	@Override
+	public boolean isIncludeDefaults() {
+		return Boolean.parseBoolean(getProperty(INCLUDE_DEFAULTS));
+	}
+
+	@Override
+	public void setIncludeDefaults(boolean includeDefaults) {
+		setProperty(INCLUDE_DEFAULTS, Boolean.toString(includeDefaults));
+	}
+
+	@Override
+	public boolean isRemovePermanently() {
+		return Boolean.parseBoolean(getProperty(REMOVE_PERMANENTLY));
+	}
+
+	@Override
+	public void setRemovePermanently(boolean removePermanently) {
+		setProperty(REMOVE_PERMANENTLY, Boolean.toString(removePermanently));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfiguration.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfiguration.java b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfiguration.java
new file mode 100644
index 0000000..46f82c4
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfiguration.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.ui.activitypalette;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import uk.org.taverna.configuration.AbstractConfigurable;
+import uk.org.taverna.configuration.ConfigurationManager;
+
+public class ActivityPaletteConfiguration extends AbstractConfigurable {
+	private Map<String,String> defaultPropertyMap;
+
+	public ActivityPaletteConfiguration(ConfigurationManager configurationManager) {
+		super(configurationManager);
+	}
+
+	@Override
+	public String getCategory() {
+		return "Services";
+	}
+
+	@Override
+	public Map<String, String> getDefaultPropertyMap() {
+		if (defaultPropertyMap == null) {
+			defaultPropertyMap = new HashMap<>();
+
+			// //wsdl
+			//defaultPropertyMap.put("taverna.defaultwsdl", "http://www.ebi.ac.uk/xembl/XEMBL.wsdl,"+
+			//                    "http://soap.genome.jp/KEGG.wsdl,"+
+			//                    "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/soap/eutils.wsdl,"+
+			//                    "http://soap.bind.ca/wsdl/bind.wsdl,"+
+			//                    "http://www.ebi.ac.uk/ws/services/urn:Dbfetch?wsdl");
+
+			// //soaplab
+			//defaultPropertyMap.put("taverna.defaultsoaplab", "http://www.ebi.ac.uk/soaplab/services/");
+
+			// //biomart
+			//defaultPropertyMap.put("taverna.defaultmartregistry","http://www.biomart.org/biomart");
+
+			//add property names
+			//defaultPropertyMap.put("name.taverna.defaultwsdl", "WSDL");
+			//defaultPropertyMap.put("name.taverna.defaultsoaplab","Soaplab");
+			//defaultPropertyMap.put("name.taverna.defaultmartregistry", "Biomart");
+		}
+		return defaultPropertyMap;
+	}
+
+	@Override
+	public String getDisplayName() {
+		return "Activity Palette";
+	}
+
+	@Override
+	public String getFilePrefix() {
+		return "ActivityPalette";
+	}
+
+	@Override
+	public String getUUID() {
+		return "ad9f3a60-5967-11dd-ae16-0800200c9a66";
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationPanel.java b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationPanel.java
new file mode 100644
index 0000000..6166e60
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationPanel.java
@@ -0,0 +1,284 @@
+/*******************************************************************************
+ * 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.ui.activitypalette;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.EAST;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.FlowLayout.LEFT;
+import static java.awt.FlowLayout.RIGHT;
+import static javax.swing.BoxLayout.Y_AXIS;
+import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showInputDialog;
+import static javax.swing.border.BevelBorder.LOWERED;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.BoxLayout;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.border.BevelBorder;
+
+import org.apache.log4j.Logger;
+
+@SuppressWarnings("serial")
+public class ActivityPaletteConfigurationPanel extends JPanel {
+	private static Logger logger = Logger
+			.getLogger(ActivityPaletteConfigurationPanel.class);
+
+	private Map<String,List<String>> values = new HashMap<>();
+	private Map<String,String> names = new HashMap<>();
+	private DefaultComboBoxModel<String> model;
+	private DefaultListModel<String> listModel;
+	private JList<String> propertyListItems;
+	private String selectedKey;
+	private JButton deleteTypeButton;
+	private final ActivityPaletteConfiguration config;
+
+	public ActivityPaletteConfigurationPanel(ActivityPaletteConfiguration config) {
+		super(new BorderLayout());
+		this.config = config;
+
+		model = new DefaultComboBoxModel<>();
+		for (String key : config.getInternalPropertyMap().keySet()) {
+			if (key.startsWith("taverna.")
+					&& config.getPropertyStringList(key) != null) {
+				model.addElement(key);
+				values.put(key,
+						new ArrayList<>(config.getPropertyStringList(key)));
+			}
+			if (key.startsWith("name.taverna."))
+				names.put(key, config.getProperty(key).toString());
+		}
+		deleteTypeButton = new JButton("Delete");
+
+		final JButton addTypeButton = new JButton("Add");
+		final JComboBox<String> comboBox = new JComboBox<>(model);
+		comboBox.setRenderer(new DefaultListCellRenderer() {
+			@Override
+			public Component getListCellRendererComponent(JList<?> list,
+					Object value, int index, boolean isSelected,
+					boolean cellHasFocus) {
+				if (value != null && value instanceof String) {
+					String name = names.get("name." + value);
+					if (name != null)
+						value = name;
+				}
+				return super.getListCellRendererComponent(list, value, index,
+						isSelected, cellHasFocus);
+			}
+		});
+
+		deleteTypeButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				String displayText = names.get("name." + selectedKey);
+				if (displayText == null)
+					displayText = selectedKey;
+				if (confirm("Confirm removal",
+						"Are you sure you wish to remove the type "
+								+ displayText + "?")) {
+					names.remove("name." + selectedKey);
+					values.remove(selectedKey);
+					model.removeElement(selectedKey);
+					comboBox.setSelectedIndex(0);
+				}
+			}
+		});
+
+		addTypeButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				String key = input("New key", "Provide the new key.");
+				if (key == null)
+					return;
+				String name = input("Name for the key",
+						"Provide the name for the key: " + key);
+				if (name == null)
+					return;
+
+				values.put(key, new ArrayList<String>());
+				names.put("name." + key, name);
+				model.addElement(key);
+				comboBox.setSelectedItem(key);
+			}
+		});
+
+		comboBox.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				if (comboBox.getSelectedItem() != null
+						&& comboBox.getSelectedItem() instanceof String) {
+					selectedKey = (String) comboBox.getSelectedItem();
+					List<String> selectedList = values.get(selectedKey);
+					populateList(selectedList);
+					deleteTypeButton.setEnabled(selectedList.size() == 0);
+				}
+			}
+		});
+
+		JPanel propertySelectionPanel = new JPanel(new FlowLayout(LEFT));
+		propertySelectionPanel.add(new JLabel("Activity type:"));
+		propertySelectionPanel.add(comboBox);
+		propertySelectionPanel.add(addTypeButton);
+		propertySelectionPanel.add(deleteTypeButton);
+		add(propertySelectionPanel, NORTH);
+
+		JPanel listPanel = new JPanel(new BorderLayout());
+		listModel = new DefaultListModel<>();
+		propertyListItems = new JList<>(listModel);
+		propertyListItems.setBorder(new BevelBorder(LOWERED));
+
+		listPanel.add(propertyListItems, CENTER);
+		listPanel.add(listButtons(), EAST);
+
+		add(listPanel, CENTER);
+
+		add(applyButtonPanel(), SOUTH);
+
+		if (model.getSize() > 0)
+			comboBox.setSelectedItem(model.getElementAt(0));
+	}
+
+	private void populateList(List<String> selectedList) {
+		listModel.removeAllElements();
+		for (String item : selectedList)
+			listModel.addElement(item);
+	}
+
+	private JPanel applyButtonPanel() {
+		JPanel applyPanel = new JPanel(new FlowLayout(RIGHT));
+		JButton applyButton = new JButton("Apply");
+
+		applyButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				config.getInternalPropertyMap().clear();
+				for (String key : values.keySet()) {
+					List<String> properties = values.get(key);
+					config.setPropertyStringList(key, new ArrayList<>(
+							properties));
+				}
+				for (String key : names.keySet())
+					config.setProperty(key, names.get(key));
+				store();
+			}
+		});
+
+		applyPanel.add(applyButton);
+		return applyPanel;
+	}
+
+	private void store() {
+		try {
+			//FIXME
+			//ConfigurationManager.getInstance().store(config);
+		} catch (Exception e1) {
+			logger.error("There was an error storing the configuration:"
+					+ config.getFilePrefix() + " (UUID=" + config.getUUID()
+					+ ")", e1);
+		}
+	}
+
+	private JPanel listButtons() {
+		JPanel panel = new JPanel();
+		panel.setLayout(new BoxLayout(panel, Y_AXIS));
+		JButton addButton = new JButton("+");
+		addButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				String value = input("New property", "Provide new value for: "
+						+ selectedKey);
+				if (value != null) {
+					listModel.addElement(value);
+					values.get(selectedKey).add(value);
+					deleteTypeButton.setEnabled(false);
+				}
+			}
+		});
+
+		JButton deleteButton = new JButton("-");
+		deleteButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				Object value = propertyListItems.getSelectedValue();
+				if (confirm("Confirm removal",
+						"Are you sure you wish to remove " + value + "?")) {
+					listModel.removeElement(value);
+					values.get(selectedKey).remove(value);
+					if (values.get(selectedKey).size() == 0)
+						deleteTypeButton.setEnabled(true);
+				}
+			}
+		});
+
+		panel.add(addButton);
+		panel.add(deleteButton);
+
+		return panel;
+	}
+
+	private boolean confirm(String title, String message) {
+		return showConfirmDialog(this, message, title, YES_NO_OPTION,
+				WARNING_MESSAGE) == YES_OPTION;
+	}
+
+	private String input(String title, String message) {
+		return showInputDialog(this, message, title, INFORMATION_MESSAGE);
+	}
+
+/*	private JButton getAddTypeButton() {
+		JButton result = new JButton("Add");
+		result.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				String val = input("New property value","New property value");
+				if (val!=null) {
+					if (values.get(val) == null) {
+						model.addElement(val);
+						values.put(val, new ArrayList<String>());
+					} else
+						showMessageDialog(ActivityPaletteConfigurationPanel.this, "This property already exists");
+				}
+			}
+		});
+		return result;
+	}
+*/
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationUIFactory.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationUIFactory.java b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationUIFactory.java
new file mode 100644
index 0000000..39c4a5a
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationUIFactory.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * 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.ui.activitypalette;
+
+import javax.swing.JPanel;
+
+import uk.org.taverna.configuration.Configurable;
+import uk.org.taverna.configuration.ConfigurationUIFactory;
+
+public class ActivityPaletteConfigurationUIFactory implements
+		ConfigurationUIFactory {
+	private ActivityPaletteConfiguration activityPaletteConfiguration;
+
+	@Override
+	public boolean canHandle(String uuid) {
+		return uuid != null && uuid.equals(getConfigurable().getUUID());
+	}
+
+	@Override
+	public Configurable getConfigurable() {
+		return activityPaletteConfiguration;
+	}
+
+	@Override
+	public JPanel getConfigurationPanel() {
+		return new ActivityPaletteConfigurationPanel(
+				activityPaletteConfiguration);
+	}
+
+	public void setActivityPaletteConfiguration(
+			ActivityPaletteConfiguration activityPaletteConfiguration) {
+		this.activityPaletteConfiguration = activityPaletteConfiguration;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-activity-palette-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
new file mode 100644
index 0000000..6c9fed9
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@
+#net.sf.taverna.t2.workbench.ui.activitypalette.ActivityPaletteConfigurationUIFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context-osgi.xml b/taverna-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context-osgi.xml
new file mode 100644
index 0000000..34921f5
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context-osgi.xml
@@ -0,0 +1,17 @@
+<?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="ServiceDescriptionRegistryImpl" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry"/>
+	<service ref="ServiceDescriptionsConfigurationImpl" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration"/>
+
+	<reference id="configurationManager" interface="uk.org.taverna.configuration.ConfigurationManager" />
+	<reference id="applicationConfiguration" interface="uk.org.taverna.configuration.app.ApplicationConfiguration" />
+
+	<list id="serviceDescriptionProviders" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider" cardinality="0..N" greedy-proxying="true"/>
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context.xml
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context.xml b/taverna-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context.xml
new file mode 100644
index 0000000..9f7110f
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context.xml
@@ -0,0 +1,23 @@
+<?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 name="ServiceDescriptionRegistryImpl" class="net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionRegistryImpl">
+		<constructor-arg name="applicationConfiguration" ref="applicationConfiguration" />
+		<property name="serviceDescriptionProvidersList" ref="serviceDescriptionProviders" />
+		<property name="serviceDescriptionsConfig">
+		 	<ref local="ServiceDescriptionsConfigurationImpl"/>
+		</property>
+	</bean>
+
+	<bean id="ServiceDescriptionsConfigurationImpl" class="net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionsConfigurationImpl">
+		<constructor-arg ref="configurationManager"/>
+	</bean>
+
+	<!-- Don't think ActivityPalette is still used -->
+	<!-- <bean id="ActivityPaletteConfiguration" class="net.sf.taverna.t2.workbench.ui.activitypalette.ActivityPaletteConfiguration">
+		<constructor-arg ref="configurationManager"/>
+	</bean> -->
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/test/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationTest.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/test/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationTest.java b/taverna-activity-palette-impl/src/test/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationTest.java
new file mode 100644
index 0000000..081a9af
--- /dev/null
+++ b/taverna-activity-palette-impl/src/test/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationTest.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * 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.ui.activitypalette;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import uk.org.taverna.configuration.app.impl.ApplicationConfigurationImpl;
+import uk.org.taverna.configuration.impl.ConfigurationManagerImpl;
+
+public class ActivityPaletteConfigurationTest {
+
+	private ActivityPaletteConfiguration conf;
+	private ConfigurationManagerImpl manager;
+
+	@Before
+	public void setup() {
+		File f = new File(System.getProperty("java.io.tmpdir"));
+		final File d = new File(f,UUID.randomUUID().toString());
+		d.mkdir();
+		manager = new ConfigurationManagerImpl(new ApplicationConfigurationImpl() {
+			@Override
+			public File getApplicationHomeDir() {
+				return d;
+			}
+		});
+		conf=new ActivityPaletteConfiguration(manager);
+		conf.restoreDefaults();
+	}
+
+	@Test
+	public void testEmptyList() throws Exception {
+		conf.setPropertyStringList("list", new ArrayList<String>());
+		assertTrue("Result was not a list but was:"+conf.getProperty("list"),conf.getPropertyStringList("list") instanceof List);
+		assertTrue("Result was not a list but was:"+conf.getPropertyStringList("list"),conf.getPropertyStringList("list") instanceof List);
+		List<String> list = conf.getPropertyStringList("list");
+		assertEquals("There should be 0 elements",0,list.size());
+	}
+
+	@Test
+	public void testSingleItem() throws Exception {
+		List<String> list = new ArrayList<>();
+		list.add("fred");
+		conf.setPropertyStringList("single", list);
+
+		assertTrue("should be an ArrayList",conf.getPropertyStringList("single") instanceof List);
+		List<String> l = conf.getPropertyStringList("single");
+		assertEquals("There should be 1 element",1,l.size());
+		assertEquals("Its value should be fred","fred",l.get(0));
+	}
+
+	@Test
+	public void testList() throws Exception {
+		List<String> list = new ArrayList<>();
+		list.add("fred");
+		list.add("bloggs");
+		conf.setPropertyStringList("list", list);
+
+		assertTrue("should be an ArrayList",conf.getPropertyStringList("list") instanceof List);
+		List<String> l = conf.getPropertyStringList("list");
+		assertEquals("There should be 1 element",2,l.size());
+		assertEquals("Its value should be fred","fred",l.get(0));
+		assertEquals("Its value should be bloggs","bloggs",l.get(1));
+	}
+
+	@Test
+	public void testNull() throws Exception {
+		assertNull("Should return null",conf.getProperty("blah blah blah"));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PartitionAlgorithmSetSPI
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PartitionAlgorithmSetSPI b/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PartitionAlgorithmSetSPI
new file mode 100644
index 0000000..9f3c02d
--- /dev/null
+++ b/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PartitionAlgorithmSetSPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.partition.DummyPartitionAlgorithmSet
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PropertyExtractorSPI
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PropertyExtractorSPI b/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PropertyExtractorSPI
new file mode 100644
index 0000000..f5e7226
--- /dev/null
+++ b/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PropertyExtractorSPI
@@ -0,0 +1,3 @@
+net.sf.taverna.t2.partition.DummyExtractor1
+net.sf.taverna.t2.partition.DummyExtractor2
+net.sf.taverna.t2.partition.DummyExtractor3
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.QueryFactory
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.QueryFactory b/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.QueryFactory
new file mode 100644
index 0000000..2d26d31a
--- /dev/null
+++ b/taverna-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.QueryFactory
@@ -0,0 +1,2 @@
+net.sf.taverna.t2.partition.DummyActivityQueryFactory
+net.sf.taverna.t2.partition.DummyQueryFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/pom.xml b/taverna-activity-palette-ui/pom.xml
new file mode 100644
index 0000000..84d8d66
--- /dev/null
+++ b/taverna-activity-palette-ui/pom.xml
@@ -0,0 +1,88 @@
+<?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>taverna-activity-palette-ui</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Activity Palette UI Component</name>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-activity-icons-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-activity-palette-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-menu-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-helper-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-workflow-view</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-ui</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>uibuilder</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-services-api</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>commons-beanutils</groupId>
+			<artifactId>commons-beanutils</artifactId>
+			<version>${commons.beanutils.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/PathElementFilterTreeNode.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/PathElementFilterTreeNode.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/PathElementFilterTreeNode.java
new file mode 100644
index 0000000..5c65bc0
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/PathElementFilterTreeNode.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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.ui.servicepanel;
+
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode;
+
+/**
+ * @author alanrw
+ */
+public class PathElementFilterTreeNode extends FilterTreeNode {
+	private static final long serialVersionUID = 6491242031931630314L;
+
+	public PathElementFilterTreeNode(String userObject) {
+		super(userObject);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/RootFilterTreeNode.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/RootFilterTreeNode.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/RootFilterTreeNode.java
new file mode 100644
index 0000000..86fca05
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/RootFilterTreeNode.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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.ui.servicepanel;
+
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode;
+
+/**
+ * @author alanrw
+ */
+public class RootFilterTreeNode extends FilterTreeNode {
+	private static final long serialVersionUID = 1047743498806473971L;
+
+	public RootFilterTreeNode(String userObject) {
+		super(userObject);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilter.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilter.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilter.java
new file mode 100644
index 0000000..a83ac2d
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilter.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * 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.ui.servicepanel;
+
+import static java.beans.Introspector.getBeanInfo;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.Filter;
+
+import org.apache.log4j.Logger;
+
+public class ServiceFilter implements Filter {
+	private static Logger logger = Logger.getLogger(ServiceFilter.class);
+
+	private String filterString;
+	private boolean superseded;
+	private String[] filterLowerCaseSplit;
+	private final Object rootToIgnore;
+
+	public ServiceFilter(String filterString, Object rootToIgnore) {
+		this.filterString = filterString;
+		this.rootToIgnore = rootToIgnore;
+		this.filterLowerCaseSplit = filterString.toLowerCase().split(" ");
+		this.superseded = false;
+	}
+
+	private boolean basicFilter(DefaultMutableTreeNode node) {
+		if (node == rootToIgnore)
+			return false;
+		if (filterString.isEmpty())
+			return true;
+
+		if (node.getUserObject() instanceof ServiceDescription) {
+			ServiceDescription serviceDescription = (ServiceDescription) node
+					.getUserObject();
+			for (String searchTerm : filterLowerCaseSplit) {
+				if (superseded)
+					return false;
+				String[] typeSplit = searchTerm.split(":", 2);
+				String type;
+				String keyword;
+				if (typeSplit.length == 2) {
+					type = typeSplit[0];
+					keyword = typeSplit[1].toLowerCase();
+				} else {
+					type = null;
+					keyword = searchTerm.toLowerCase();
+				}
+				try {
+					if (!doesPropertySatisfy(serviceDescription, type, keyword))
+						return false;
+				} catch (IntrospectionException | IllegalArgumentException
+						| IllegalAccessException | InvocationTargetException e) {
+					logger.error(
+							"failed to get properties of service description",
+							e);
+					return false;
+				}
+			}
+			return true;
+		}
+		for (String searchString : filterLowerCaseSplit)
+			if (!node.getUserObject().toString().toLowerCase().contains(
+					searchString))
+				return false;
+		return true;
+	}
+
+	/**
+	 * Determine whether a service description satisfies a search term.
+	 * 
+	 * @param serviceDescription
+	 *            The service description bean to look in.
+	 * @param type
+	 *            The name of the property to look in, or <tt>null</tt> to
+	 *            search in all public non-expert properties.
+	 * @param searchTerm
+	 *            The string to search for.
+	 * @return <tt>true</tt> if-and-only-if the description matches.
+	 */
+	private boolean doesPropertySatisfy(ServiceDescription serviceDescription,
+			String type, String searchTerm) throws IllegalAccessException,
+			IllegalArgumentException, InvocationTargetException,
+			IntrospectionException {
+		BeanInfo beanInfo = getBeanInfo(serviceDescription.getClass());
+		for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) {
+			if (superseded)
+				return false;
+			if ((type == null && !property.isHidden() && !property.isExpert())
+					|| property.getName().equalsIgnoreCase(type)) {
+				Method readMethod = property.getReadMethod();
+				if (readMethod == null)
+					continue;
+				Object readProperty = readMethod.invoke(serviceDescription,
+						new Object[0]);
+				if (readProperty == null)
+					continue;
+				if (readProperty.toString().toLowerCase().contains(searchTerm))
+					return true;
+				// Dig deeper?
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public boolean pass(DefaultMutableTreeNode node) {
+		return basicFilter(node);
+	}
+
+	@Override
+	public String filterRepresentation(String original) {
+		return original;
+	}
+
+	/**
+	 * @return the superseded
+	 */
+	@Override
+	public boolean isSuperseded() {
+		return superseded;
+	}
+
+	/**
+	 * @param superseded
+	 *            the superseded to set
+	 */
+	@Override
+	public void setSuperseded(boolean superseded) {
+		this.superseded = superseded;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilterTreeNode.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilterTreeNode.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilterTreeNode.java
new file mode 100644
index 0000000..0e559ce
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilterTreeNode.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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.ui.servicepanel;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode;
+
+/**
+ * @author alanrw
+ */
+public class ServiceFilterTreeNode extends FilterTreeNode {
+	private static final long serialVersionUID = 6066698619971305454L;
+	
+	public ServiceFilterTreeNode(ServiceDescription userObject) {
+		super(userObject);
+	}
+
+	@Override
+	public ServiceDescription getUserObject() {
+		return (ServiceDescription) super.getUserObject();
+	}
+}


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

Posted by st...@apache.org.
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());
+	}
+
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUI.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUI.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUI.java
new file mode 100644
index 0000000..41d7a15
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUI.java
@@ -0,0 +1,1512 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.PAGE_END;
+import static java.awt.Dialog.ModalExclusionType.APPLICATION_EXCLUDE;
+import static java.awt.Toolkit.getDefaultToolkit;
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
+import static javax.swing.JOptionPane.NO_OPTION;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.JTable.AUTO_RESIZE_ALL_COLUMNS;
+import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
+import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED;
+import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.KEYSTORE;
+import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.TRUSTSTORE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ALERT_TITLE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Image;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.prefs.Preferences;
+
+import javax.swing.JButton;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableColumn;
+
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType;
+import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser;
+import net.sf.taverna.t2.security.credentialmanager.UsernamePassword;
+
+import org.apache.log4j.Logger;
+import org.bouncycastle.openssl.PEMReader;
+import org.bouncycastle.openssl.PEMWriter;
+
+/**
+ * Provides a UI for the Credential Manager for users to manage their
+ * credentials saved by the Credential Manager in Taverna's Keystore and
+ * Trustore. Credentials include username and passwords pairs, key pairs, proxy
+ * key pairs and trusted certificates of CA's and s. Credentials are stored in
+ * two Bouncy Castle "UBER"-type keystores: the Keystore (containing passwords
+ * and (normal and proxy) key pairs) and the Truststore (containing trusted
+ * certificates).
+ *
+ * Inspired by the Portlecle tool (http://portecle.sourceforge.net/)
+ * and Firefox's Certificate Manager.
+ *
+ * @author Alex Nenadic
+ */
+
+@SuppressWarnings("serial")
+public class CredentialManagerUI extends JFrame {
+	private static Logger logger = Logger.getLogger(CredentialManagerUI.class);
+	/** Default tabbed pane width */
+	private static final int DEFAULT_FRAME_WIDTH = 650;
+	/** Default tabbed pane height */
+	private static final int DEFAULT_FRAME_HEIGHT = 400;
+	/** Credential Manager icon (when frame is minimised)*/
+	private static final Image credManagerIconImage = getDefaultToolkit()
+			.createImage(
+					CredentialManagerUI.class
+							.getResource("/images/cred_manager_transparent.png"));
+
+	/**
+	 * Credential Manager to manage all operations on the Keystore and
+	 * Truststore
+	 */
+	public final CredentialManager credManager;
+	private final DistinguishedNameParser dnParser;
+	
+	////////////// Tabs //////////////
+
+	/**
+	 * Tabbed pane to hold tables containing various entries in the Keystore and
+	 * Truststore
+	 */
+	private JTabbedPane keyStoreTabbedPane;
+	/** Tab 1: holds passwords table */
+	private JPanel passwordsTab = new JPanel(new BorderLayout(10, 10));
+	/** Tab 1: name */
+	public static final String PASSWORDS = "Passwords";
+	/** Tab 2: holds key pairs (user certificates) table */
+	private JPanel keyPairsTab = new JPanel(new BorderLayout(10, 10));
+	/** Tab 2: name */
+	public static final String KEYPAIRS = "Your Certificates";
+	/** Tab 3: holds trusted certificates table */
+	private JPanel trustedCertificatesTab = new JPanel(new BorderLayout(10, 10));
+	/** Tab 3: name */
+	public static final String TRUSTED_CERTIFICATES = "Trusted Certificates";
+
+	////////////// Tables //////////////
+
+	/** Password entries' table */
+	private JTable passwordsTable;
+	/** Key pair entries' table */
+	private JTable keyPairsTable;
+	/** Trusted certificate entries' table */
+	private JTable trustedCertsTable;
+	/** Password entry column type */
+	public static final String PASSWORD_ENTRY_TYPE = "Password";
+	/** Key pair entry column type */
+	public static final String KEY_PAIR_ENTRY_TYPE = "Key Pair";
+	/** Trusted cert entry column type */
+	public static final String TRUST_CERT_ENTRY_TYPE = "Trusted Certificate";
+
+	/**
+	 * Overrides the Object's clone method to prevent the singleton object to be
+	 * cloned.
+	 */
+	@Override
+	public Object clone() throws CloneNotSupportedException {
+		throw new CloneNotSupportedException();
+	}
+
+	/**
+	 * Creates a new Credential Manager UI's frame.
+	 */
+	public CredentialManagerUI(CredentialManager credentialManager,
+			DistinguishedNameParser dnParser) {
+		credManager = credentialManager;
+		this.dnParser = dnParser;
+		setModalExclusionType(APPLICATION_EXCLUDE);
+		// Initialise the UI components
+		initComponents();
+	}
+
+	private void initComponents() {
+		/*
+		 * Initialise the tabbed pane that contains the tabs with tabular
+		 * representations of the Keystore's content.
+		 */
+		keyStoreTabbedPane = new JTabbedPane();
+		/*
+		 * Initialise the tab containing the table for username/password entries
+		 * from the Keystore
+		 */
+		passwordsTable = initTable(PASSWORDS, passwordsTab);
+		/*
+		 * Initialise the tab containing the table for key pair entries from the
+		 * Keystore
+		 */
+		keyPairsTable = initTable(KEYPAIRS, keyPairsTab);
+		/*
+		 * Initialise the tab containing the table for proxy entries from the
+		 * Keystore
+		 */
+		//proxiesTable = initTable(PROXIES, proxiesTab);
+		/*
+		 * Initialise the tab containing the table for trusted certificate
+		 * entries from the Truststore
+		 */
+		trustedCertsTable = initTable(TRUSTED_CERTIFICATES,
+				trustedCertificatesTab);
+		/*
+		 * Set the size of the tabbed pane to the preferred size - the size of
+		 * the main application frame depends on it.
+		 */
+		keyStoreTabbedPane.setPreferredSize(new Dimension(DEFAULT_FRAME_WIDTH,
+				DEFAULT_FRAME_HEIGHT));
+
+		JPanel globalButtons = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+		JButton resetJavaAuthCache = new JButton("Clear HTTP authentication");
+		resetJavaAuthCache.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				clearAuthenticationCache();
+			}
+		});
+		globalButtons.add(resetJavaAuthCache);
+
+		// Button for changing Credential Manager's master password
+		JButton changeMasterPasswordButton = new JButton(
+				"Change master password");
+		changeMasterPasswordButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				changeMasterPassword();
+			}
+		});
+		globalButtons.add(changeMasterPasswordButton);
+
+		// Add change master password to the main application frame
+		getContentPane().add(globalButtons, NORTH);
+		// Add tabbed pane to the main application frame
+		getContentPane().add(keyStoreTabbedPane, CENTER);
+
+		// Handle application close
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeFrame();
+			}
+		});
+		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+
+		pack();
+
+		// Centre the frame in the centre of the screen
+		setLocationRelativeTo(null);
+
+		// Set the frame's icon
+		setIconImage(credManagerIconImage);
+
+		// Set the frame's title
+		setTitle("Credential Manager");
+
+		// setModal(true);
+		// setVisible(true);
+	}
+
+	protected void clearAuthenticationCache() {
+		if (!credManager.resetAuthCache())
+			showMessageDialog(
+					this,
+					"Java's internal HTTP authentication cache could not be cleared. \n\n"
+							+ "Taverna can only clear the cache using an undocumented Java API \n"
+							+ "that might not work if you are using a Java VM other than \n"
+							+ "Java 6 from Sun. You can restarting Taverna to clear the cache.",
+					"Could not clear authentication cache", ERROR_MESSAGE);
+		else
+			showMessageDialog(
+					this,
+					"Java's internal HTTP authentication cache has been cleared. \n\n"
+							+ "You might also need to edit or delete individual \n"
+							+ "password entries in the credential manager \n"
+							+ "if a relevant password has previously been saved.",
+					"Cleared authentication cache", INFORMATION_MESSAGE);
+	}
+
+	protected void changeMasterPassword() {
+		ChangeMasterPasswordDialog changePasswordDialog = new ChangeMasterPasswordDialog(
+				this, "Change master password", true,
+				"Change master password for Credential Manager", credManager);
+		changePasswordDialog.setLocationRelativeTo(null);
+		changePasswordDialog.setVisible(true);
+		String password = changePasswordDialog.getPassword();
+		if (password == null) // user cancelled
+			return; // do nothing
+
+		try {
+			credManager.changeMasterPassword(password);
+			showMessageDialog(this, "Master password changed sucessfully",
+					ALERT_TITLE, INFORMATION_MESSAGE);
+		} catch (CMException cme) {
+			/*
+			 * Failed to change the master password for Credential Manager -
+			 * warn the user
+			 */
+			String exMessage = "Failed to change master password for Credential Manager";
+			logger.error(exMessage);
+			showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE);
+		}
+	}
+
+	/**
+	 * Initialise the tabs and tables with the content from the Keystore and Truststore.
+	 */
+	private JTable initTable(String tableType, JPanel tab) {
+		JTable table = null;
+
+		if (tableType.equals(PASSWORDS)) { // Passwords table
+			// The Passwords table's data model
+			PasswordsTableModel passwordsTableModel = new PasswordsTableModel(credManager);
+			// The table itself
+			table = new JTable(passwordsTableModel);
+
+			/*
+			 * Set the password and alias columns of the Passwords table to be
+			 * invisible by removing them from the column model (they will still
+			 * present in the table model)
+			 * 
+			 * Remove the last column first
+			 */
+			TableColumn aliasColumn = table.getColumnModel().getColumn(5);
+			table.getColumnModel().removeColumn(aliasColumn);
+			TableColumn passwordColumn = table.getColumnModel().getColumn(4);
+			table.getColumnModel().removeColumn(passwordColumn);
+			TableColumn lastModifiedDateColumn = table.getColumnModel().getColumn(3);
+			table.getColumnModel().removeColumn(lastModifiedDateColumn);
+
+			// Buttons
+			JButton newPasswordButton = new JButton("New");
+			newPasswordButton.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					newPassword();
+				}
+			});
+
+			final JButton viewPasswordButton = new JButton("Details");
+			viewPasswordButton.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					viewPassword();
+				}
+			});
+			viewPasswordButton.setEnabled(false);
+
+			final JButton editPasswordButton = new JButton("Edit");
+			editPasswordButton.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					editPassword();
+				}
+			});
+			editPasswordButton.setEnabled(false);
+
+			final JButton deletePasswordButton = new JButton("Delete");
+			deletePasswordButton.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					deletePassword();
+				}
+			});
+			deletePasswordButton.setEnabled(false);
+
+			/*
+			 * Selection listener for passwords table to enable/disable action
+			 * buttons accordingly
+			 */
+			class PasswordsTableSelectionListner implements
+					ListSelectionListener {
+				@Override
+				public void valueChanged(ListSelectionEvent e) {
+					if (e.getSource() != passwordsTable.getSelectionModel())
+						return;
+					if (passwordsTable.getSelectedRow() == -1) {
+						// nothing is selected
+						viewPasswordButton.setEnabled(false);
+						editPasswordButton.setEnabled(false);
+						deletePasswordButton.setEnabled(false);
+					} else {
+						if (!viewPasswordButton.isEnabled())
+							viewPasswordButton.setEnabled(true);
+						if (!editPasswordButton.isEnabled())
+							editPasswordButton.setEnabled(true);
+						if (!deletePasswordButton.isEnabled())
+							deletePasswordButton.setEnabled(true);
+					}
+				}
+			}
+			table.getSelectionModel().addListSelectionListener(new PasswordsTableSelectionListner());
+
+			// Panel to hold the buttons
+			JPanel bp = new JPanel();
+			bp.add(viewPasswordButton);
+			bp.add(editPasswordButton);
+			bp.add(newPasswordButton);
+			bp.add(deletePasswordButton);
+
+			// Add button panel to the tab
+			tab.add(bp, PAGE_END);
+
+		} else if (tableType.equals(KEYPAIRS)) { // Key Pairs tab
+			// The Key Pairs table's data model
+			KeyPairsTableModel keyPairsTableModel = new KeyPairsTableModel(credManager);
+			// The table itself
+			table = new JTable(keyPairsTableModel);
+
+			/*
+			 * Set the alias and service URIs columns of the KayPairs table to
+			 * be invisible by removing them from the column model (they will
+			 * still present in the table model)
+			 * 
+			 * Remove the last column first
+			 */
+			TableColumn aliasColumn = table.getColumnModel().getColumn(6);
+			table.getColumnModel().removeColumn(aliasColumn);
+			TableColumn serviceURIsColumn = table.getColumnModel().getColumn(5);
+			table.getColumnModel().removeColumn(serviceURIsColumn);
+			TableColumn lastModifiedDateColumn = table.getColumnModel().getColumn(4);
+			table.getColumnModel().removeColumn(lastModifiedDateColumn);
+
+			// Buttons
+			final JButton viewKeyPairButton = new JButton("Details");
+			viewKeyPairButton.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					viewCertificate();
+				}
+			});
+			viewKeyPairButton.setEnabled(false);
+
+			JButton importKeyPairButton = new JButton("Import");
+			importKeyPairButton.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					importKeyPair();
+				}
+			});
+
+			final JButton exportKeyPairButton = new JButton("Export");
+			exportKeyPairButton.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					exportKeyPair();
+				}
+			});
+			exportKeyPairButton.setEnabled(false);
+
+			final JButton deleteKeyPairButton = new JButton("Delete");
+			deleteKeyPairButton.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					deleteKeyPair();
+				}
+			});
+			deleteKeyPairButton.setEnabled(false);
+
+			/*
+			 * Selection listener for key pairs table to enable/disable action
+			 * buttons accordingly
+			 */
+			class KeyPairsTableSelectionListner implements
+					ListSelectionListener {
+				@Override
+				public void valueChanged(ListSelectionEvent e) {
+					if (e.getSource() != keyPairsTable.getSelectionModel())
+						return;
+					if (keyPairsTable.getSelectedRow() == -1) {
+						// nothing is selected
+						viewKeyPairButton.setEnabled(false);
+						exportKeyPairButton.setEnabled(false);
+						deleteKeyPairButton.setEnabled(false);
+					} else {
+						if (!viewKeyPairButton.isEnabled())
+							viewKeyPairButton.setEnabled(true);
+						if (!exportKeyPairButton.isEnabled())
+							exportKeyPairButton.setEnabled(true);
+						if (!deleteKeyPairButton.isEnabled())
+							deleteKeyPairButton.setEnabled(true);
+					}
+				}
+			}
+			table.getSelectionModel().addListSelectionListener(
+					new KeyPairsTableSelectionListner());
+
+			// Panel to hold the buttons
+			JPanel bp = new JPanel();
+			bp.add(viewKeyPairButton);
+			bp.add(importKeyPairButton);
+			bp.add(exportKeyPairButton);
+			bp.add(deleteKeyPairButton);
+
+			// Add button panel to the tab
+			tab.add(bp, PAGE_END);
+		} else if (tableType.equals(TRUSTED_CERTIFICATES)) { // Certificates tab
+
+			// The Trusted Certificate table's data model
+			TrustedCertsTableModel trustedCertificatesTableModel = new TrustedCertsTableModel(credManager);
+			// The table itself
+			table = new JTable(trustedCertificatesTableModel);
+
+			/*
+			 * Set the alias columns of the Trusted Certs table to be invisible
+			 * by removing them from the column model (they will still be
+			 * present in the table model)
+			 * 
+			 * Remove the last column first
+			 */
+			TableColumn aliasColumn = table.getColumnModel().getColumn(5);
+			table.getColumnModel().removeColumn(aliasColumn);
+			TableColumn lastModifiedDateColumn = table.getColumnModel().getColumn(4);
+			table.getColumnModel().removeColumn(lastModifiedDateColumn);
+
+			// Buttons
+			final JButton viewTrustedCertificateButton = new JButton("Details");
+			viewTrustedCertificateButton
+					.addActionListener(new ActionListener() {
+						@Override
+						public void actionPerformed(ActionEvent e) {
+							viewCertificate();
+						}
+					});
+			viewTrustedCertificateButton.setEnabled(false);
+
+			JButton importTrustedCertificateButton = new JButton("Import");
+			importTrustedCertificateButton
+					.addActionListener(new ActionListener() {
+						@Override
+						public void actionPerformed(ActionEvent e) {
+							importTrustedCertificate();
+						}
+					});
+
+			final JButton exportTrustedCertificateButton = new JButton("Export");
+			exportTrustedCertificateButton
+					.addActionListener(new ActionListener() {
+						@Override
+						public void actionPerformed(ActionEvent e) {
+							exportTrustedCertificate();
+						}
+					});
+			exportTrustedCertificateButton.setEnabled(false);
+
+			final JButton deleteTrustedCertificateButton = new JButton("Delete");
+			deleteTrustedCertificateButton
+					.addActionListener(new ActionListener() {
+						@Override
+						public void actionPerformed(ActionEvent e) {
+							deleteTrustedCertificate();
+						}
+					});
+			deleteTrustedCertificateButton.setEnabled(false);
+
+			// Selection listener for trusted certs table to enable/disable action buttons accordingly
+			class TrustedCertsTableSelectionListener implements
+					ListSelectionListener {
+				@Override
+				public void valueChanged(ListSelectionEvent e) {
+					if (e.getSource() != trustedCertsTable.getSelectionModel())
+						return;
+					if (trustedCertsTable.getSelectedRow() == -1) {
+						// nothing is selected
+						viewTrustedCertificateButton.setEnabled(false);
+						exportTrustedCertificateButton.setEnabled(false);
+						deleteTrustedCertificateButton.setEnabled(false);
+					} else {
+						if (!viewTrustedCertificateButton.isEnabled())
+							viewTrustedCertificateButton.setEnabled(true);
+						if (!exportTrustedCertificateButton.isEnabled())
+							exportTrustedCertificateButton.setEnabled(true);
+						if (!deleteTrustedCertificateButton.isEnabled())
+							deleteTrustedCertificateButton.setEnabled(true);
+					}
+				}
+			}
+			table.getSelectionModel().addListSelectionListener(
+					new TrustedCertsTableSelectionListener());
+
+			// Panel to hold the buttons
+			JPanel bp = new JPanel();
+			bp.add(viewTrustedCertificateButton);
+			bp.add(importTrustedCertificateButton);
+			bp.add(exportTrustedCertificateButton);
+			bp.add(deleteTrustedCertificateButton);
+
+			// Add button panel to the tab
+			tab.add(bp, PAGE_END);
+		} else {
+			throw new RuntimeException("Unknown table type " + tableType);
+		}
+
+		table.setShowGrid(false);
+		table.setRowMargin(0);
+		table.getColumnModel().setColumnMargin(0);
+		table.getTableHeader().setReorderingAllowed(false);
+		table.setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS);
+		// Top accommodates entry icons with 2 pixels spare space (images are
+		// 16x16 pixels)
+		table.setRowHeight(18);
+
+		// Add custom renderrers for the table headers and cells
+		for (int iCnt = 0; iCnt < table.getColumnCount(); iCnt++) {
+			TableColumn column = table.getColumnModel().getColumn(iCnt);
+			column.setHeaderRenderer(new TableHeaderRenderer());
+			column.setCellRenderer(new TableCellRenderer());
+		}
+
+		// Make the first column small and not resizable (it holds icons to
+		// represent different entry types)
+		TableColumn typeCol = table.getColumnModel().getColumn(0);
+		typeCol.setResizable(false);
+		typeCol.setMinWidth(20);
+		typeCol.setMaxWidth(20);
+		typeCol.setPreferredWidth(20);
+
+		// Set the size for the second column
+		// (i.e. Service URI column of Passwords table, and
+		// Certificate Name column of the Kay Pairs and Trusted Certificates tables)
+		// We do not care about the size of other columns.
+		TableColumn secondCol = table.getColumnModel().getColumn(1);
+		secondCol.setMinWidth(20);
+		secondCol.setMaxWidth(10000);
+		secondCol.setPreferredWidth(300);
+
+		// Put the table into a scroll pane
+		JScrollPane jspTableScrollPane = new JScrollPane(table,
+				VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		jspTableScrollPane.getViewport().setBackground(table.getBackground());
+
+		// Put the scroll pane on the tab panel
+		tab.add(jspTableScrollPane, CENTER);
+		jspTableScrollPane.setBorder(new EmptyBorder(3, 3, 3, 3));
+
+		/*
+		 * Add mouse listeners to show an entry's details if it is
+		 * double-clicked
+		 */
+		table.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseClicked(MouseEvent evt) {
+				tableDoubleClick(evt);
+			}
+		});
+
+		// Add the tab to the tabbed pane
+		keyStoreTabbedPane.addTab(tableType, tab);
+
+		return table;
+	}
+
+	/**
+	 * Displays the details of the username/password pair entry - this includes
+	 * showing the plaintext password and service URI for this entry.
+	 */
+	private void viewPassword() {
+		// Which username/password pair entry has been selected, if any?
+		int iRow = passwordsTable.getSelectedRow();
+		if (iRow == -1) // no row currently selected
+			return;
+
+		// Get current values for service URI, username and password
+		String serviceURI = (String) passwordsTable.getValueAt(iRow, 1); // current entry's service URI
+
+		String username = (String) passwordsTable.getValueAt(iRow, 2); // current entry's username
+
+		/*
+		 * Because the password column is not visible we call the getValueAt
+		 * method on the table model rather than at the JTable
+		 */
+		String password = (String) passwordsTable.getModel()
+				.getValueAt(iRow, 4); // current entry's password value
+
+		// Let the user view service URI, username and password of the entry
+		ViewUsernamePasswordEntryDialog viewServicePassDialog = new ViewUsernamePasswordEntryDialog(
+				this, serviceURI, username, password);
+
+		viewServicePassDialog.setLocationRelativeTo(this);
+		viewServicePassDialog.setVisible(true);
+	}
+
+	/**
+	 * Lets a user insert a new username/password/service URI tuple to the
+	 * Keystore.
+	 */
+	private void newPassword() {
+		URI serviceURI = null; // service URI
+		String username = null; // username
+		String password = null; // password
+
+		// Loop until the user cancels or enters everything correctly
+		while (true) {
+			/*
+			 * Let the user insert a new password entry (by specifying service
+			 * URI, username and password)
+			 */
+			NewEditPasswordEntryDialog newPasswordDialog = new NewEditPasswordEntryDialog(
+					this, "New username and password for a service", true,
+					serviceURI, username, password, credManager);
+			newPasswordDialog.setLocationRelativeTo(this);
+			newPasswordDialog.setVisible(true);
+
+			serviceURI = newPasswordDialog.getServiceURI(); // get service URI
+			username = newPasswordDialog.getUsername(); // get username
+			password = newPasswordDialog.getPassword(); // get password
+
+			if (password == null) { // user cancelled - any of the above three
+				// fields is null
+				// do nothing
+				return;
+			}
+
+			/*
+			 * Check if a password entry with the given service URI already
+			 * exists in the Keystore. We ask this here as the user may wish to
+			 * overwrite the existing password entry. Checking for key pair
+			 * entries' URIs is done in the NewEditPasswordEntry dialog.
+			 */
+
+			/*
+			 * Get list of service URIs for all the password entries in the
+			 * Keystore
+			 */
+			List<URI> serviceURIs = null;
+			try {
+				serviceURIs = credManager
+						.getServiceURIsForAllUsernameAndPasswordPairs();
+			} catch (CMException cme) {
+				showMessageDialog(this, "Failed to get service URIs for all username and password pairs "
+						+ "to check if the entered service URI already exists",
+						ERROR_TITLE, ERROR_MESSAGE);
+				return;
+			}
+			if (serviceURIs.contains(serviceURI)) { // if such a URI already
+				// exists
+				// Ask if the user wants to overwrite it
+				int answer = showConfirmDialog(
+								this,
+								"Credential Manager already contains a password entry with the same service URI.\n"
+										+ "Do you want to overwrite it?",
+								ALERT_TITLE,
+								YES_NO_OPTION);
+
+				// Add the new password entry in the Keystore
+				try {
+					if (answer == YES_OPTION) {
+						credManager.addUsernameAndPasswordForService(
+								new UsernamePassword(username, password),
+								serviceURI);
+						break;
+					}
+				} catch (CMException cme) {
+					showMessageDialog(
+							this,
+							"Credential Manager failed to insert a new username and password pair",
+							ERROR_TITLE, ERROR_MESSAGE);
+				}
+				/*
+				 * Otherwise show the same window with the entered service URI,
+				 * username and password values
+				 */
+			} else
+				// Add the new password entry in the Keystore
+				try {
+					credManager.addUsernameAndPasswordForService(new UsernamePassword(username,
+							password), serviceURI);
+					break;
+				} catch (CMException cme) {
+					showMessageDialog(
+							this,
+							"Credential Manager failed to insert a new username and password pair",
+							ERROR_TITLE, ERROR_MESSAGE);
+				}
+		}
+	}
+
+	/**
+	 * Lets a user insert a new username/password pair for a given service URI
+	 * to the Keystore.
+	 */
+	public void newPasswordForService(URI serviceURI) {
+		/*
+		 * As this method can be called from outside of Credential Manager UI,
+		 * e.g. from wsdl-activity-ui or rshell-activity-ui to pop up a dialog
+		 * to ask the user for username and password, we also want to make sure
+		 * the main Credential Manager UI Dialog is visible as it may be clearer
+		 * to the user what is going on
+		 */
+		if (!isVisible() || getState() == ICONIFIED)
+			setVisible(true);
+
+		// Make sure password tab is selected as this method may
+		// be called from outside of Credential Manager UI.
+		keyStoreTabbedPane.setSelectedComponent(passwordsTab);
+
+		String username = null; // username
+		String password = null; // password
+
+		// Loop until the user cancels or enters everything correctly
+		while (true) {
+
+//			if(!this.isVisible()){ // if Cred Man UI is already showing but e.g. obscured by another window or minimised
+//				// Do not bring it up!
+//			} // actually we now want to show it as it makes it clearer to the user what is going on
+
+			// Let the user insert a new password entry for the given service
+			// URI (by specifying username and password)
+			NewEditPasswordEntryDialog newPasswordDialog = new NewEditPasswordEntryDialog(
+					this, "New username and password for a service", true,
+					serviceURI, username, password, credManager);
+			newPasswordDialog.setLocationRelativeTo(this);
+			newPasswordDialog.setVisible(true);
+
+			serviceURI = newPasswordDialog.getServiceURI(); // get service URI
+			username = newPasswordDialog.getUsername(); // get username
+			password = newPasswordDialog.getPassword(); // get password
+
+			if (password == null) // user cancelled - any of the above three
+				// fields is null
+				// do nothing
+				return;
+
+			/*
+			 * Check if a password entry with the given service URI already
+			 * exists in the Keystore. We ask this here as the user may wish to
+			 * overwrite the existing password entry. Checking for key pair
+			 * entries' URIs is done in the NewEditPasswordEntry dialog.
+			 */
+
+			// Get list of service URIs for all the password entries in the
+			// Keystore
+			List<URI> serviceURIs = null;
+			try {
+				serviceURIs = credManager
+						.getServiceURIsForAllUsernameAndPasswordPairs();
+			} catch (CMException cme) {
+				showMessageDialog(this, "Failed to get service URIs for all username and password pairs "
+						+ "to check if the entered service URI already exists",
+						ERROR_TITLE, ERROR_MESSAGE);
+				return;
+			}
+			if (serviceURIs.contains(serviceURI)) { // if such a URI already
+				// exists
+				// Ask if the user wants to overwrite it
+				int answer = showConfirmDialog(
+						this,
+						"Credential Manager already contains a password entry with the same service URI.\n"
+								+ "Do you want to overwrite it?", ALERT_TITLE,
+						YES_NO_OPTION);
+
+				// Add the new password entry in the Keystore
+				try {
+					if (answer == YES_OPTION) {
+						credManager.addUsernameAndPasswordForService(
+								new UsernamePassword(username, password),
+								serviceURI);
+						break;
+					}
+				} catch (CMException cme) {
+					String exMessage = "Credential Manager failed to insert a new username and password pair";
+					showMessageDialog(this, exMessage, ERROR_TITLE,
+							ERROR_MESSAGE);
+				}
+				// Otherwise show the same window with the entered service
+				// URI, username and password values
+			} else
+				// Add the new password entry in the Keystore
+				try {
+					credManager.addUsernameAndPasswordForService(new UsernamePassword(username,
+							password), serviceURI);
+					break;
+				} catch (CMException cme) {
+					showMessageDialog(this, "Credential Manager failed to insert a new username and password pair",
+							ERROR_TITLE,
+							ERROR_MESSAGE);
+				}
+		}
+	}
+
+	/**
+	 * Lets a user edit a username and password entry or their related service
+	 * URI to the Keystore.
+	 */
+	private void editPassword() {
+		// Which password entry has been selected?
+		int iRow = passwordsTable.getSelectedRow();
+		if (iRow == -1) { // no row currently selected
+			return;
+		}
+
+		// Get current values for service URI, username and password
+		URI serviceURI = URI.create((String) passwordsTable.getValueAt(iRow, 1)); // current entry's service URI
+
+		String username = (String) passwordsTable.getValueAt(iRow, 2); // current entry's username
+
+		/*
+		 * Because the password column is not visible we call the getValueAt
+		 * method on the table model rather than at the JTable
+		 */
+		String password = (String) passwordsTable.getModel()
+				.getValueAt(iRow, 4); // current entry's password value
+
+		while (true) { // loop until user cancels or enters everything correctly
+			// Let the user edit service URI, username or password of a password entry
+			NewEditPasswordEntryDialog editPasswordDialog = new NewEditPasswordEntryDialog(
+					this, "Edit username and password for a service", true,
+					serviceURI, username, password, credManager);
+
+			editPasswordDialog.setLocationRelativeTo(this);
+			editPasswordDialog.setVisible(true);
+
+			// New values
+			URI newServiceURI = editPasswordDialog.getServiceURI(); // get new service URI
+			String newUsername = editPasswordDialog.getUsername(); // get new username
+			String newPassword = editPasswordDialog.getPassword(); // get new password
+
+			if (newPassword == null) // user cancelled - any of the above three
+				// fields is null
+				// do nothing
+				return;
+
+			// Is anything actually modified?
+			boolean isModified = !serviceURI.equals(newServiceURI)
+					|| !username.equals(newUsername)
+					|| !password.equals(newPassword);
+
+			if (isModified) {
+				/*
+				 * Check if a different password entry with the new URI (i.e.
+				 * alias) already exists in the Keystore We ask this here as the
+				 * user may wish to overwrite that other password entry.
+				 */
+
+				// Get list of URIs for all passwords in the Keystore
+				List<URI> serviceURIs = null;
+				try {
+					serviceURIs = credManager
+							.getServiceURIsForAllUsernameAndPasswordPairs();
+				} catch (CMException cme) {
+					showMessageDialog(this, "Failed to get service URIs for all username and password pairs "
+							+ "to check if the modified entry already exists",
+							ERROR_TITLE,
+							ERROR_MESSAGE);
+					return;
+				}
+
+				// If the modified service URI already exists and is not the
+				// currently selected one
+				if (!newServiceURI.equals(serviceURI)
+						&& serviceURIs.contains(newServiceURI)) {
+					int answer = showConfirmDialog(
+							this,
+							"The Keystore already contains username and password pair for the entered service URI.\n"
+									+ "Do you want to overwrite it?",
+							ALERT_TITLE, YES_NO_OPTION);
+
+					try {
+						if (answer == YES_OPTION) {
+							/*
+							 * Overwrite that other entry entry and save the new
+							 * one in its place. Also remove the current one
+							 * that we are editing - as it is replacing the
+							 * other entry.
+							 */
+							credManager
+									.deleteUsernameAndPasswordForService(serviceURI);
+							credManager.addUsernameAndPasswordForService(
+									new UsernamePassword(newUsername,
+											newPassword), newServiceURI);
+							break;
+						}
+					} catch (CMException cme) {
+						showMessageDialog(
+								this,
+								"Failed to update the username and password pair in the Keystore",
+								ERROR_TITLE, ERROR_MESSAGE);
+					}
+					// Otherwise show the same window with the entered
+					// service URI, username and password values
+				} else
+					try {
+						if (!newServiceURI.equals(serviceURI))
+							credManager
+									.deleteUsernameAndPasswordForService(serviceURI);
+						credManager.addUsernameAndPasswordForService(
+								new UsernamePassword(newUsername, newPassword), newServiceURI);
+						break;
+					} catch (CMException cme) {
+						showMessageDialog(
+								this,
+								"Failed to update the username and password pair in the Keystore",
+								ERROR_TITLE, ERROR_MESSAGE);
+					}
+			} else // nothing actually modified
+				break;
+		}
+	}
+
+	/**
+	 * Lets the user delete the selected username and password entries from the
+	 * Keystore.
+	 */
+	private void deletePassword() {
+		// Which entries have been selected?
+		int[] selectedRows = passwordsTable.getSelectedRows();
+		if (selectedRows.length == 0) // no password entry selected
+			return;
+
+		// Ask user to confirm the deletion
+		if (showConfirmDialog(
+				null,
+				"Are you sure you want to delete the selected username and password entries?",
+				ALERT_TITLE, YES_NO_OPTION) != YES_OPTION)
+			return;
+
+		String exMessage = null;
+		for (int i = selectedRows.length - 1; i >= 0; i--) { // delete from backwards
+			// Get service URI for the current entry
+			URI serviceURI = URI.create((String) passwordsTable.getValueAt(selectedRows[i], 1));
+			// current entry's service URI
+			try {
+				// Delete the password entry from the Keystore
+				credManager.deleteUsernameAndPasswordForService(serviceURI);
+			} catch (CMException cme) {
+				exMessage = "Failed to delete the username and password pair from the Keystore";
+			}
+		}
+		if (exMessage != null)
+			showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE);
+	}
+
+	/**
+	 * Shows the contents of a (user or trusted) certificate.
+	 */
+	private void viewCertificate() {
+		int selectedRow = -1;
+		String alias = null;
+		X509Certificate certToView = null;
+		ArrayList<String> serviceURIs = null;
+		KeystoreType keystoreType = null;
+
+		// Are we showing user's public key certificate?
+		if (keyPairsTab.isShowing()) {
+			keystoreType = KEYSTORE;
+			selectedRow = keyPairsTable.getSelectedRow();
+
+			if (selectedRow != -1)
+				/*
+				 * Because the alias column is not visible we call the
+				 * getValueAt method on the table model rather than at the
+				 * JTable
+				 */
+				alias = (String) keyPairsTable.getModel().getValueAt(selectedRow, 6); // current entry's Keystore alias
+		}
+		// Are we showing trusted certificate?
+		else if (trustedCertificatesTab.isShowing()) {
+			keystoreType = TRUSTSTORE;
+			selectedRow = trustedCertsTable.getSelectedRow();
+
+			if (selectedRow != -1)
+				/*
+				 * Get the selected trusted certificate entry's Truststore alias
+				 * Alias column is invisible so we get the value from the table
+				 * model
+				 */
+				alias = (String) trustedCertsTable.getModel().getValueAt(
+						selectedRow, 5);
+		}
+
+		try {
+			if (selectedRow != -1) { // something has been selected
+				// Get the entry's certificate
+				certToView = dnParser.convertCertificate(credManager
+						.getCertificate(keystoreType, alias));
+
+				// Show the certificate's contents to the user
+				ViewCertDetailsDialog viewCertDetailsDialog = new ViewCertDetailsDialog(
+						this, "Certificate details", true, certToView,
+						serviceURIs, dnParser);
+				viewCertDetailsDialog.setLocationRelativeTo(this);
+				viewCertDetailsDialog.setVisible(true);
+			}
+		} catch (CMException cme) {
+			String exMessage = "Failed to get certificate details to display to the user";
+			logger.error(exMessage, cme);
+			showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE);
+		}
+	}
+
+	/**
+	 * Lets a user import a key pair from a PKCS #12 keystore file to the
+	 * Keystore.
+	 */
+	private void importKeyPair() {
+		/*
+		 * Let the user choose a PKCS #12 file (keystore) containing a public
+		 * and private key pair to import
+		 */
+		File importFile = selectImportExportFile(
+				"PKCS #12 file to import from", // title
+				new String[] { ".p12", ".pfx" }, // array of file extensions
+				// for the file filter
+				"PKCS#12 Files (*.p12, *.pfx)", // description of the filter
+				"Import", // text for the file chooser's approve button
+				"keyPairDir"); // preference string for saving the last chosen directory
+
+		if (importFile == null)
+			return;
+
+		// The PKCS #12 keystore is not a file
+		if (!importFile.isFile()) {
+			showMessageDialog(this, "Your selection is not a file",
+					ALERT_TITLE, WARNING_MESSAGE);
+			return;
+		}
+
+		// Get the user to enter the password that was used to encrypt the
+		// private key contained in the PKCS #12 file
+		GetPasswordDialog getPasswordDialog = new GetPasswordDialog(this,
+				"Import key pair entry", true,
+				"Enter the password that was used to encrypt the PKCS #12 file");
+		getPasswordDialog.setLocationRelativeTo(this);
+		getPasswordDialog.setVisible(true);
+
+		String pkcs12Password = getPasswordDialog.getPassword();
+
+		if (pkcs12Password == null) // user cancelled
+			return;
+		else if (pkcs12Password.isEmpty()) // empty password
+			// FIXME: Maybe user did not have the password set for the private key???
+			return;
+
+		try {
+			// Load the PKCS #12 keystore from the file
+			// (this is using the BouncyCastle provider !!!)
+			KeyStore pkcs12Keystore = credManager.loadPKCS12Keystore(importFile,
+					pkcs12Password);
+
+			/*
+			 * Display the import key pair dialog supplying all the private keys
+			 * stored in the PKCS #12 file (normally there will be only one
+			 * private key inside, but could be more as this is a keystore after
+			 * all).
+			 */
+			NewKeyPairEntryDialog importKeyPairDialog = new NewKeyPairEntryDialog(
+					this, "Credential Manager", true, pkcs12Keystore, dnParser);
+			importKeyPairDialog.setLocationRelativeTo(this);
+			importKeyPairDialog.setVisible(true);
+
+			// Get the private key and certificate chain of the key pair
+			Key privateKey = importKeyPairDialog.getPrivateKey();
+			Certificate[] certChain = importKeyPairDialog.getCertificateChain();
+
+			if (privateKey == null || certChain == null)
+				// User did not select a key pair for import or cancelled
+				return;
+
+			/*
+			 * Check if a key pair entry with the same alias already exists in
+			 * the Keystore
+			 */
+			if (credManager.hasKeyPair(privateKey, certChain)
+					&& showConfirmDialog(this,
+							"The keystore already contains the key pair entry with the same private key.\n"
+									+ "Do you want to overwrite it?",
+							ALERT_TITLE, YES_NO_OPTION) != YES_OPTION)
+				return;
+
+			// Place the private key and certificate chain into the Keystore
+			credManager.addKeyPair(privateKey, certChain);
+
+			// Display success message
+			showMessageDialog(this, "Key pair import successful", ALERT_TITLE,
+					INFORMATION_MESSAGE);
+		} catch (Exception ex) { // too many exceptions to catch separately
+			String exMessage = "Failed to import the key pair entry to the Keystore. "
+					+ ex.getMessage();
+			logger.error(exMessage, ex);
+			showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE);
+		}
+	}
+
+	/**
+	 * Lets a user export user's private and public key pair to a PKCS #12
+	 * keystore file.
+	 */
+	private void exportKeyPair() {
+		// Which key pair entry has been selected?
+		int selectedRow = keyPairsTable.getSelectedRow();
+		if (selectedRow == -1) // no row currently selected
+			return;
+
+		// Get the key pair entry's Keystore alias
+		String alias = (String) keyPairsTable.getModel().getValueAt(selectedRow, 6);
+
+		// Let the user choose a PKCS #12 file (keystore) to export public and
+		// private key pair to
+		File exportFile = selectImportExportFile("Select a file to export to", // title
+				new String[] { ".p12", ".pfx" }, // array of file extensions
+				// for the file filter
+				"PKCS#12 Files (*.p12, *.pfx)", // description of the filter
+				"Export", // text for the file chooser's approve button
+				"keyPairDir"); // preference string for saving the last chosen directory
+
+		if (exportFile == null)
+			return;
+
+		// If file already exist - ask the user if he wants to overwrite it
+		if (exportFile.isFile()
+				&& showConfirmDialog(this,
+						"The file with the given name already exists.\n"
+								+ "Do you want to overwrite it?", ALERT_TITLE,
+						YES_NO_OPTION) == NO_OPTION)
+			return;
+
+		// Get the user to enter the password for the PKCS #12 keystore file
+		GetPasswordDialog getPasswordDialog = new GetPasswordDialog(this,
+				"Credential Manager", true,
+				"Enter the password for protecting the exported key pair");
+		getPasswordDialog.setLocationRelativeTo(this);
+		getPasswordDialog.setVisible(true);
+
+		String pkcs12Password = getPasswordDialog.getPassword();
+
+		if (pkcs12Password == null) { // user cancelled or empty password
+			// Warn the user
+			showMessageDialog(
+					this,
+					"You must supply a password for protecting the exported key pair.",
+					ALERT_TITLE, INFORMATION_MESSAGE);
+			return;
+		}
+
+		// Export the key pair
+		try {
+			credManager.exportKeyPair(alias, exportFile, pkcs12Password);
+			showMessageDialog(this, "Key pair export successful", ALERT_TITLE,
+					INFORMATION_MESSAGE);
+		} catch (CMException cme) {
+			showMessageDialog(this, cme.getMessage(), ERROR_TITLE,
+					ERROR_MESSAGE);
+		}
+	}
+
+	/**
+	 * Lets a user delete selected key pair entries from the Keystore.
+	 */
+	private void deleteKeyPair() {
+		// Which entries have been selected?
+		int[] selectedRows = keyPairsTable.getSelectedRows();
+		if (selectedRows.length == 0) // no key pair entry selected
+			return;
+
+		// Ask user to confirm the deletion
+		if (showConfirmDialog(null,
+				"Are you sure you want to delete the selected key pairs?",
+				ALERT_TITLE, YES_NO_OPTION) != YES_OPTION)
+			return;
+
+		String exMessage = null;
+		for (int i = selectedRows.length - 1; i >= 0; i--) { // delete from backwards
+			// Get the alias for the current entry
+			String alias = (String) keyPairsTable.getModel().getValueAt(
+					selectedRows[i], 6);
+			try {
+				// Delete the key pair entry from the Keystore
+				credManager.deleteKeyPair(alias);
+			} catch (CMException cme) {
+				logger.warn("failed to delete " + alias, cme);
+				exMessage = "Failed to delete the key pair(s) from the Keystore";
+			}
+		}
+		if (exMessage != null)
+			showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE);
+	}
+
+	/**
+	 * Lets a user import a trusted certificate from a PEM or DER encoded file
+	 * into the Truststore.
+	 */
+	private void importTrustedCertificate() {
+		// Let the user choose a file containing trusted certificate(s) to
+		// import
+		File certFile = selectImportExportFile(
+				"Certificate file to import from", // title
+				new String[] { ".pem", ".crt", ".cer", ".der", "p7", ".p7c" }, // file extensions filters
+				"Certificate Files (*.pem, *.crt, , *.cer, *.der, *.p7, *.p7c)", // filter descriptions
+				"Import", // text for the file chooser's approve button
+				"trustedCertDir"); // preference string for saving the last chosen directory
+		if (certFile == null)
+			return;
+
+		// Load the certificate(s) from the file
+		ArrayList<X509Certificate> trustCertsList = new ArrayList<>();
+		CertificateFactory cf;
+		try {
+			cf = CertificateFactory.getInstance("X.509");
+		} catch (Exception e) {
+			// Nothing we can do! Things are badly misconfigured
+			cf = null;
+		}
+
+		if (cf != null) {
+			try (FileInputStream fis = new FileInputStream(certFile)) {
+				for (Certificate cert : cf.generateCertificates(fis))
+					trustCertsList.add((X509Certificate) cert);
+			} catch (Exception cex) {
+				// Do nothing
+			}
+
+			if (trustCertsList.size() == 0) {
+				// Could not load certificates as any of the above types
+				try (FileInputStream fis = new FileInputStream(certFile);
+						PEMReader pr = new PEMReader(
+								new InputStreamReader(fis), null, cf
+										.getProvider().getName())) {
+					/*
+					 * Try as openssl PEM format - which sligtly differs from
+					 * the one supported by JCE
+					 */
+					Object cert;
+					while ((cert = pr.readObject()) != null)
+						if (cert instanceof X509Certificate)
+							trustCertsList.add((X509Certificate) cert);
+				} catch (Exception cex) {
+					// do nothing
+				}
+			}
+		}
+
+		if (trustCertsList.size() == 0) {
+			/* Failed to load certifcate(s) using any of the known encodings */
+			showMessageDialog(this,
+					"Failed to load certificate(s) using any of the known encodings -\n"
+							+ "file format not recognised.", ERROR_TITLE,
+					ERROR_MESSAGE);
+			return;
+		}
+
+		// Show the list of certificates contained in the file for the user to
+		// select the ones to import
+		NewTrustCertsDialog importTrustCertsDialog = new NewTrustCertsDialog(this,
+				"Credential Manager", true, trustCertsList, dnParser);
+
+		importTrustCertsDialog.setLocationRelativeTo(this);
+		importTrustCertsDialog.setVisible(true);
+		List<X509Certificate> selectedTrustCerts = importTrustCertsDialog
+				.getTrustedCertificates(); // user-selected trusted certs to import
+
+		// If user cancelled or did not select any cert to import
+		if (selectedTrustCerts == null || selectedTrustCerts.isEmpty())
+			return;
+
+		try {
+			for (X509Certificate cert : selectedTrustCerts)
+				// Import the selected trusted certificates
+				credManager.addTrustedCertificate(cert);
+
+			// Display success message
+			showMessageDialog(this, "Trusted certificate(s) import successful",
+					ALERT_TITLE, INFORMATION_MESSAGE);
+		} catch (CMException cme) {
+			String exMessage = "Failed to import trusted certificate(s) to the Truststore";
+			logger.error(exMessage, cme);
+			showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE);
+		}
+	}
+
+	/**
+	 * Lets the user export one (at the moment) or more (in future) trusted
+	 * certificate entries to a PEM-encoded file.
+	 */
+	private boolean exportTrustedCertificate() {
+		// Which trusted certificate has been selected?
+		int selectedRow = trustedCertsTable.getSelectedRow();
+		if (selectedRow == -1) // no row currently selected
+			return false;
+
+		// Get the trusted certificate entry's Keystore alias
+		String alias = (String) trustedCertsTable.getModel()
+				.getValueAt(selectedRow, 3);
+		// the alias column is invisible so we get the value from the table
+		// model
+
+		// Let the user choose a file to export to
+		File exportFile = selectImportExportFile("Select a file to export to", // title
+				new String[] { ".pem" }, // array of file extensions for the
+				// file filter
+				"Certificate Files (*.pem)", // description of the filter
+				"Export", // text for the file chooser's approve button
+				"trustedCertDir"); // preference string for saving the last chosen directory
+		if (exportFile == null)
+			return false;
+
+		// If file already exist - ask the user if he wants to overwrite it
+		if (exportFile.isFile()
+				&& showConfirmDialog(this,
+						"The file with the given name already exists.\n"
+								+ "Do you want to overwrite it?", ALERT_TITLE,
+						YES_NO_OPTION) == NO_OPTION)
+			return false;
+
+		// Export the trusted certificate
+		try (PEMWriter pw = new PEMWriter(new FileWriter(exportFile))) {
+			// Get the trusted certificate
+			pw.writeObject(credManager.getCertificate(TRUSTSTORE, alias));
+		} catch (Exception ex) {
+			String exMessage = "Failed to export the trusted certificate from the Truststore.";
+			logger.error(exMessage, ex);
+			showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE);
+			return false;
+		}
+		showMessageDialog(this, "Trusted certificate export successful",
+				ALERT_TITLE, INFORMATION_MESSAGE);
+		return true;
+	}
+
+	/**
+	 * Lets a user delete the selected trusted certificate entries from the
+	 * Truststore.
+	 */
+	private void deleteTrustedCertificate() {
+		// Which entries have been selected?
+		int[] selectedRows = trustedCertsTable.getSelectedRows();
+		if (selectedRows.length == 0) // no trusted cert entry selected
+			return;
+
+		// Ask user to confirm the deletion
+		if (showConfirmDialog(
+				null,
+				"Are you sure you want to delete the selected trusted certificate(s)?",
+				ALERT_TITLE, YES_NO_OPTION) != YES_OPTION)
+			return;
+
+		String exMessage = null;
+		for (int i = selectedRows.length - 1; i >= 0; i--) { // delete from backwards
+			// Get the alias for the current entry
+			String alias = (String) trustedCertsTable.getModel().getValueAt(
+					selectedRows[i], 5);
+			try {
+				// Delete the trusted certificate entry from the Truststore
+				credManager.deleteTrustedCertificate(alias);
+			} catch (CMException cme) {
+				exMessage = "Failed to delete the trusted certificate(s) from the Truststore";
+				logger.error(exMessage, cme);
+			}
+		}
+		if (exMessage != null)
+			showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE);
+	}
+
+	/**
+	 * If double click on a table occured - show the
+	 * details of the table entry.
+	 */
+	private void tableDoubleClick(MouseEvent evt) {
+		if (evt.getClickCount() > 1) { // is it a double click?
+			// Which row was clicked on (if any)?
+			Point point = new Point(evt.getX(), evt.getY());
+			int row = ((JTable) evt.getSource()).rowAtPoint(point);
+			if (row == -1)
+				return;
+			// Which table the click occured on?
+			if (((JTable) evt.getSource()).getModel() instanceof PasswordsTableModel)
+				// Passwords table
+				viewPassword();
+			else if (((JTable) evt.getSource()).getModel() instanceof KeyPairsTableModel)
+				// Key pairs table
+				viewCertificate();
+			else
+				// Trusted certificates table
+				viewCertificate();
+		}
+	}
+
+	/**
+	 * Lets the user select a file to export to or import from a key pair or a
+	 * certificate.
+	 */
+	private File selectImportExportFile(String title, String[] filter,
+			String description, String approveButtonText, String prefString) {
+		Preferences prefs = Preferences
+				.userNodeForPackage(CredentialManagerUI.class);
+		String keyPairDir = prefs.get(prefString,
+				System.getProperty("user.home"));
+		JFileChooser fileChooser = new JFileChooser();
+		fileChooser.addChoosableFileFilter(new CryptoFileFilter(filter,
+				description));
+		fileChooser.setDialogTitle(title);
+		fileChooser.setMultiSelectionEnabled(false);
+		fileChooser.setCurrentDirectory(new File(keyPairDir));
+
+		if (fileChooser.showDialog(this, approveButtonText) != APPROVE_OPTION)
+			return null;
+
+		File selectedFile = fileChooser.getSelectedFile();
+		prefs.put(prefString, fileChooser.getCurrentDirectory().toString());
+		return selectedFile;
+	}
+
+	private void closeFrame() {
+		setVisible(false);
+		dispose();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUILauncher.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUILauncher.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUILauncher.java
new file mode 100644
index 0000000..cdcabb7
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUILauncher.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static javax.swing.SwingUtilities.invokeLater;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+/**
+ * Test launcher for Credential Manager GUI (so it does not have to be
+ * launched from Taverna).
+ *
+ * @author Alexandra Nenadic
+ */
+public class CredentialManagerUILauncher extends JFrame {
+	private static final long serialVersionUID = 2079805060170251148L;
+
+	private final ImageIcon launchCMIcon = new ImageIcon(
+			CredentialManagerUILauncher.class
+					.getResource("/images/cred_manager.png"));
+
+	public CredentialManagerUILauncher() {
+		JPanel jpLaunch = new JPanel();
+		jpLaunch.setPreferredSize(new Dimension(300, 120));
+
+		JLabel jlLaunch = new JLabel("T2: Launch Credential Manager GUI");
+
+		JButton jbLaunch = new JButton();
+		jbLaunch.setIcon(launchCMIcon);
+		jbLaunch.setToolTipText("Launches Credential Manager");
+		jbLaunch.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				CredentialManagerUI cmGUI = new CredentialManagerUI(null, null);
+				if (cmGUI != null)
+					cmGUI.setVisible(true);
+			}
+		});
+
+		jpLaunch.add(jlLaunch);
+		jpLaunch.add(jbLaunch);
+
+		getContentPane().add(jpLaunch, CENTER);
+
+		// Handle application close
+		setDefaultCloseOperation(EXIT_ON_CLOSE);
+
+		pack();
+
+        // Centre the frame in the centre of the desktop
+        setLocationRelativeTo(null);
+        // Set the frame's title
+        setTitle("Credential Manager GUI Launcher");
+        setVisible(true);
+	}
+
+	/**
+	 * Launcher for the Credential Manager GUI.
+	 */
+	public static void main(String[] args) {
+        // Create and show GUI on the event handler thread
+        invokeLater(new Runnable(){
+        	@Override
+        	public void run() {
+        		new CredentialManagerUILauncher();
+        	}
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CryptoFileFilter.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CryptoFileFilter.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CryptoFileFilter.java
new file mode 100644
index 0000000..d58aa8a
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CryptoFileFilter.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.filechooser.FileFilter;
+
+/**
+ * File filter for filtering against various file extensions. Crypto files
+ * normally contain a private key (and optionally its certificate chain) or a
+ * public key certificate (and optionally its certificate chain).
+ * 
+ * .p12 or .pfx are PKCS #12 keystore files containing private key and its
+ * public key (+cert chain); .pem are ASN.1 PEM-encoded files containing one (or
+ * more concatenated) public key certificate(s); .der are ASN.1 DER-encoded
+ * files containing one public key certificate; .cer are CER-encoded files
+ * containing one ore more DER-encoded certificates; .crt files are either
+ * encoded as binary DER or as ASCII PEM. .p7 and .p7c are PKCS #7 certificate
+ * chain files (i.e. SignedData structure without data, just certificate(s)).
+ */
+public class CryptoFileFilter extends FileFilter {
+	// Description of the filter
+	private String description;
+
+	// Array of file extensions to filter against
+	private List<String> exts;
+
+	public CryptoFileFilter(String[] extList, String desc) {
+		exts = Arrays.asList(extList);
+		this.description = desc;
+	}
+
+	@Override
+	public boolean accept(File file) {
+		if (file.isDirectory())
+			return true;
+		if (file.isFile())
+			for (String ext : exts)
+				if (file.getName().toLowerCase().endsWith(ext))
+					return true;
+		return false;
+	}
+
+	public void setDescription(String desc) {
+		this.description = desc;
+	}
+
+	@Override
+	public String getDescription() {
+		return this.description;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetMasterPasswordDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetMasterPasswordDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetMasterPasswordDialog.java
new file mode 100644
index 0000000..b6f623e
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetMasterPasswordDialog.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static javax.swing.BoxLayout.Y_AXIS;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.WARN_TITLE;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+/**
+ * Dialog used for getting a master password for Credential Manager from the
+ * users.
+ * 
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class GetMasterPasswordDialog extends NonBlockedHelpEnabledDialog {
+	/** Password entry field */
+	private JPasswordField passwordField;
+	/** The entered password */
+	private String password = null;
+	/** Text giving user the instructions what to do in the dialog */
+	private String instructions;
+
+	public GetMasterPasswordDialog(String instructions) {
+		super((Frame) null, "Enter master password", true);
+		this.instructions = instructions;
+		initComponents();
+	}
+
+	private void initComponents() {
+		getContentPane().setLayout(new BorderLayout());
+
+		JLabel instructionsLabel = new JLabel(instructions);
+		// instructionsLabel.setFont(new Font(null, Font.PLAIN, 11));
+
+		JPanel instructionsPanel = new JPanel();
+		instructionsPanel.setLayout(new BoxLayout(instructionsPanel, Y_AXIS));
+		instructionsPanel.add(instructionsLabel);
+		instructionsPanel.setBorder(new EmptyBorder(10, 5, 10, 0));
+
+		JLabel passwordLabel = new JLabel("Password");
+		passwordLabel.setBorder(new EmptyBorder(0, 5, 0, 0));
+
+		passwordField = new JPasswordField(15);
+		JPanel passwordPanel = new JPanel(new GridLayout(1, 1, 5, 5));
+		passwordPanel.add(passwordLabel);
+		passwordPanel.add(passwordField);
+
+		JPanel mainPanel = new JPanel(new BorderLayout());
+		mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
+		mainPanel.add(instructionsPanel, NORTH);
+		mainPanel.add(passwordPanel, CENTER);
+
+		JButton okButton = new JButton("OK");
+		okButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				okPressed();
+			}
+		});
+
+		JButton cancelButton = new JButton("Cancel");
+		cancelButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				cancelPressed();
+			}
+		});
+		JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		buttonsPanel.add(okButton);
+		buttonsPanel.add(cancelButton);
+
+		getContentPane().add(mainPanel, CENTER);
+		getContentPane().add(buttonsPanel, SOUTH);
+
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeDialog();
+			}
+		});
+
+		setResizable(false);
+		getRootPane().setDefaultButton(okButton);
+		pack();
+	}
+
+	/**
+	 * Get the password entered in the dialog.
+	 */
+	public String getPassword() {
+		return password;
+	}
+
+	/**
+	 * Check that the entered password is not empty and store the entered
+	 * password.
+	 */
+	private boolean checkPassword() {
+		password = new String(passwordField.getPassword());
+
+		if (password.isEmpty()) {
+			showMessageDialog(this, "The password cannot be empty",
+					WARN_TITLE, WARNING_MESSAGE);
+			return false;
+		}
+
+		return true;
+	}
+
+	private void okPressed() {
+		if (checkPassword())
+			closeDialog();
+	}
+
+	private void cancelPressed() {
+		/*
+		 * Set the password to null as it might have changed in the meantime if
+		 * user entered something then cancelled.
+		 */
+		password = null;
+		closeDialog();
+	}
+
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetPasswordDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetPasswordDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetPasswordDialog.java
new file mode 100644
index 0000000..45a0f88
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetPasswordDialog.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.Font.PLAIN;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.WARN_TITLE;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+/**
+ * A general dialog for entering a password.
+ * 
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class GetPasswordDialog extends NonBlockedHelpEnabledDialog {
+	/** Instructions for user explaining the purpose of the password */
+	private String instructions = null;
+	/* Password entry password field */
+	private JPasswordField passwordField;
+	/* Stores the password entered */
+	private String password = null;
+
+	public GetPasswordDialog(JFrame parent, String title, boolean modal,
+			String instr) {
+		super(parent, title, modal);
+		instructions = instr;
+		initComponents();
+	}
+
+	public GetPasswordDialog(JDialog parent, String title, boolean modal,
+			String instr) {
+		super(parent, title, modal);
+		instructions = instr;
+		initComponents();
+	}
+
+	private void initComponents() {
+		getContentPane().setLayout(new BorderLayout());
+
+		JLabel passwordLabel = new JLabel("Password");
+		passwordField = new JPasswordField(15);
+
+		JButton okButton = new JButton("OK");
+		okButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				okPressed();
+			}
+		});
+
+		JButton cancelButton = new JButton("Cancel");
+		cancelButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				cancelPressed();
+			}
+		});
+
+		JLabel instructionsLabel; // Instructions
+		if (instructions != null) {
+			instructionsLabel = new JLabel(instructions);
+			instructionsLabel.setFont(new Font(null, PLAIN, 11));
+			instructionsLabel.setBorder(new EmptyBorder(5, 5, 5, 5));
+			getContentPane().add(instructionsLabel, NORTH);
+		}
+        
+        JPanel passwordPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+        passwordPanel.add(passwordLabel);
+        passwordPanel.add(passwordField);
+        passwordPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
+
+        JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+        buttonsPanel.add(okButton);
+        buttonsPanel.add(cancelButton);
+
+        getContentPane().add(passwordPanel, CENTER);
+        getContentPane().add(buttonsPanel, SOUTH);
+
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeDialog();
+			}
+		});
+
+        setResizable(false);
+        getRootPane().setDefaultButton(okButton);
+        pack();
+	}
+
+	/**
+	 * Get the password entered in the dialog.
+	 */
+	public String getPassword() {
+		return password;
+	}
+
+	/**
+	 * Check that the password entered is not empty and store the entered
+	 * password.
+	 */
+	private boolean checkPassword() {
+		password = new String(passwordField.getPassword());
+
+		if (password.isEmpty()) {
+			showMessageDialog(this, "The password cannot be empty",
+					WARN_TITLE, WARNING_MESSAGE);
+			return false;
+		}
+
+		return true;
+	}
+
+	private void okPressed() {
+        if (checkPassword())
+            closeDialog();
+    }
+
+	private void cancelPressed() {
+		password = null;
+		closeDialog();
+	}
+
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/KeyPairsTableModel.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/KeyPairsTableModel.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/KeyPairsTableModel.java
new file mode 100644
index 0000000..712cdf6
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/KeyPairsTableModel.java
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.KEYSTORE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.KEY_PAIR_ENTRY_TYPE;
+
+import java.util.TreeMap;
+
+import javax.swing.JFrame;
+import javax.swing.table.AbstractTableModel;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.security.credentialmanager.KeystoreChangedEvent;
+
+import org.apache.log4j.Logger;
+
+/**
+ * The table model used to display the Keystore's key pair entries.
+ *
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class KeyPairsTableModel extends AbstractTableModel implements Observer<KeystoreChangedEvent> {
+	private static final Logger logger = Logger.getLogger(KeyPairsTableModel.class);
+
+	/** Column names*/
+    private String[] columnNames;
+    /** Table data*/
+    private Object[][] data;
+	private CredentialManager credManager;
+
+    public KeyPairsTableModel(CredentialManager credentialManager) {
+        credManager = credentialManager;
+
+		if (credManager == null) {
+			/* Failed to instantiate Credential Manager - warn the user and exit */
+			String sMessage = "Failed to instantiate Credential Manager. ";
+			logger.error("CM GUI: " + sMessage);
+			showMessageDialog(new JFrame(), sMessage,
+					ERROR_TITLE, ERROR_MESSAGE);
+			return;
+		}
+
+       	data = new Object[0][0];
+        columnNames = new String[] {
+        	"Entry Type", // type of the Keystore entry
+        	"Owner", // owner's common name
+        	"Issuer", // issuer's common name
+        	"Serial Number", // public key certificate's serial number
+        	"Last Modified", // last modified date of the entry
+            "URLs", // the invisible column holding the list of URLs associated with this entry
+            "Alias" // the invisible column holding the actual alias in the Keystore
+        };
+
+		try {
+			load();
+		} catch (CMException cme) {
+			String sMessage = "Failed to load key pairs";
+			logger.error(sMessage, cme);
+			showMessageDialog(new JFrame(), sMessage,
+					ERROR_TITLE, ERROR_MESSAGE);
+			return;
+		}
+
+        // Start observing changes to the Keystore
+        credManager.addObserver(this);
+    }
+
+    /**
+     * Load the table model with the key pair entries from the Keystore.
+     */
+    public void load() throws CMException {
+    	// Place key pair entries' aliases in a tree map to sort them
+    	TreeMap<String, String> sortedAliases = new TreeMap<>();
+
+		for (String alias: credManager.getAliases(KEYSTORE))
+			/*
+			 * We are only interested in key pair entries here.
+			 * 
+			 * Alias for such entries is constructed as
+			 * "keypair#<CERT_SERIAL_NUMBER>#<CERT_COMMON_NAME>" where
+			 */
+			if (alias.startsWith("keypair#"))
+				sortedAliases.put(alias, alias);
+
+		// Create one table row for each key pair entry
+		data = new Object[sortedAliases.size()][7];
+
+		/*
+		 * Iterate through the sorted aliases (if any), retrieving the key pair
+		 * entries and populating the table model
+		 */
+		int iCnt = 0;
+		for (String alias : sortedAliases.values()) {
+			/*
+			 * Populate the type column - it is set with an integer but a custom
+			 * cell renderer will cause a suitable icon to be displayed
+			 */
+			data[iCnt][0] = KEY_PAIR_ENTRY_TYPE;
+
+			/*
+			 * Split the alias string to extract owner, issuer and serial number
+			 * alias =
+			 * "keypair#"<SUBJECT_COMMON_NAME>"#"<ISSUER_COMMON_NAME>"#"<SERIAL_NUMBER>
+			 */
+			String[] aliasComponents = alias.split("#");
+
+			// Populate the owner column extracted from the alias
+			data[iCnt][1] = aliasComponents[1];
+
+			// Populate the issuer column extracted from the alias
+			data[iCnt][2] = aliasComponents[2];
+
+			// Populate the serial number column extracted from the alias
+			data[iCnt][3] = aliasComponents[3];
+
+			// Populate the modified date column ("UBER" keystore type supports creation date)
+			//data[iCnt][4] = credManager.getEntryCreationDate(CredentialManager.KEYSTORE, alias);
+
+			// Populate the invisible URLs list column
+			//data[iCnt][5] = credManager.getServiceURLsForKeyPair(alias);
+
+			// Populate the invisible alias column
+			data[iCnt][6] = alias;
+
+			iCnt++;
+		}
+
+		fireTableDataChanged();
+    }
+
+	/**
+	 * Get the number of columns in the table.
+	 */
+	@Override
+	public int getColumnCount() {
+		return columnNames.length;
+	}
+
+	/**
+	 * Get the number of rows in the table.
+	 */
+	@Override
+	public int getRowCount() {
+		return data.length;
+	}
+
+	/**
+	 * Get the name of the column at the given position.
+	 */
+	@Override
+	public String getColumnName(int iCol) {
+		return columnNames[iCol];
+	}
+
+	/**
+	 * Get the cell value at the given row and column position.
+	 */
+	@Override
+	public Object getValueAt(int iRow, int iCol) {
+		return data[iRow][iCol];
+	}
+
+	/**
+	 * Get the class at of the cells at the given column position.
+	 */
+	@Override
+	public Class<? extends Object> getColumnClass(int iCol) {
+		return getValueAt(0, iCol).getClass();
+	}
+
+	/**
+	 * Is the cell at the given row and column position editable?
+	 */
+	@Override
+	public boolean isCellEditable(int iRow, int iCol) {
+		// The table is always read-only
+		return false;
+	}
+
+	@Override
+	public void notify(Observable<KeystoreChangedEvent> sender,
+			KeystoreChangedEvent message) throws Exception {
+		// reload the table
+		if (message.keystoreType.equals(KEYSTORE))
+			load();
+	}
+}


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

Posted by st...@apache.org.
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/GraphNode.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphNode.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphNode.java
new file mode 100644
index 0000000..3f3f85f
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphNode.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * 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 java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A node of a graph that can optionally contain other graphs.
+ * 
+ * @author David Withers
+ */
+public class GraphNode extends GraphShapeElement {
+	private List<GraphNode> sourceNodes = new ArrayList<>();
+	private List<GraphNode> sinkNodes = new ArrayList<>();
+	private Graph graph;
+	private boolean expanded;
+
+	/**
+	 * Constructs a new instance of Node.
+	 * 
+	 */
+	public GraphNode(GraphController graphController) {
+		super(graphController);
+	}
+
+	/**
+	 * Adds a sink node.
+	 * 
+	 * @param sinkNode
+	 *            the sink node to add
+	 */
+	public void addSinkNode(GraphNode sinkNode) {
+		sinkNode.setParent(this);
+		sinkNodes.add(sinkNode);
+	}
+
+	/**
+	 * Adds a source node.
+	 * 
+	 * @param sourceNode
+	 *            the source node to add
+	 */
+	public void addSourceNode(GraphNode sourceNode) {
+		sourceNode.setParent(this);
+		sourceNodes.add(sourceNode);
+	}
+
+	/**
+	 * Returns the graph that this node contains.
+	 * 
+	 * @return the graph that this node contains
+	 */
+	public Graph getGraph() {
+		return graph;
+	}
+
+	/**
+	 * Returns the sinkNodes.
+	 * 
+	 * @return the sinkNodes
+	 */
+	public List<GraphNode> getSinkNodes() {
+		return sinkNodes;
+	}
+
+	/**
+	 * Returns the sourceNodes.
+	 * 
+	 * @return the sourceNodes
+	 */
+	public List<GraphNode> getSourceNodes() {
+		return sourceNodes;
+	}
+
+	/**
+	 * Returns true if this node is expanded to show the contained graph.
+	 * 
+	 * @return true if this node is expanded
+	 */
+	public boolean isExpanded() {
+		return expanded;
+	}
+
+	/**
+	 * Removes a sink node.
+	 * 
+	 * @param sinkNode
+	 *            the node to remove
+	 * @return true if the node was removed, false otherwise
+	 */
+	public boolean removeSinkNode(GraphNode sinkNode) {
+		return sinkNodes.remove(sinkNode);
+	}
+
+	/**
+	 * Removes a source node.
+	 * 
+	 * @param sourceNode
+	 *            the node to remove
+	 * @return true if the node was removed, false otherwise
+	 */
+	public boolean removeSourceNode(GraphNode sourceNode) {
+		return sourceNodes.remove(sourceNode);
+	}
+
+	/**
+	 * Sets whether this node is expanded to show the contained graph.
+	 * 
+	 * @param expanded
+	 *            true if this node is expanded
+	 */
+	public void setExpanded(boolean expanded) {
+		this.expanded = expanded;
+	}
+
+	/**
+	 * Sets the graph that this node contains.
+	 * 
+	 * @param graph
+	 *            the new graph
+	 */
+	public void setGraph(Graph graph) {
+		if (graph != null)
+			graph.setParent(this);
+		this.graph = graph;
+	}
+
+	@Override
+	public void setSelected(boolean selected) {
+		super.setSelected(selected);
+		if (isExpanded())
+			getGraph().setSelected(selected);
+	}
+}

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/GraphShapeElement.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphShapeElement.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphShapeElement.java
new file mode 100644
index 0000000..1bb8b6d
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphShapeElement.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * 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.models.graph;
+
+import java.awt.Dimension;
+import java.awt.Point;
+
+/**
+ * A Graph element that has shape, size and position properties.
+ * 
+ * @author David Withers
+ */
+public class GraphShapeElement extends GraphElement {
+	public enum Shape {
+		BOX, RECORD, HOUSE, INVHOUSE, DOT, CIRCLE, TRIANGLE, INVTRIANGLE
+	}
+
+	private Shape shape;
+	private int x, y, width, height;
+
+	public GraphShapeElement(GraphController graphController) {
+		super(graphController);
+	}
+
+	/**
+	 * Returns the height.
+	 * 
+	 * @return the height
+	 */
+	public int getHeight() {
+		return height;
+	}
+
+	/**
+	 * Returns the position.
+	 * 
+	 * @return the position
+	 */
+	public Point getPosition() {
+		return new Point(x, y);
+	}
+
+	/**
+	 * Returns the shape of the element.
+	 * 
+	 * @return the shape of the element
+	 */
+	public Shape getShape() {
+		return shape;
+	}
+
+	/**
+	 * Returns the width.
+	 * 
+	 * @return the width
+	 */
+	public int getWidth() {
+		return width;
+	}
+
+	/**
+	 * Sets the position.
+	 * 
+	 * @param position
+	 *            the new position
+	 */
+	public void setPosition(Point position) {
+		x = position.x;
+		y = position.y;
+	}
+
+	/**
+	 * Sets the shape of the element.
+	 * 
+	 * @param shape
+	 *            the new shape of the element
+	 */
+	public void setShape(Shape shape) {
+		this.shape = shape;
+	}
+
+	/**
+	 * Returns the size of the element.
+	 * 
+	 * @return the size of the element
+	 */
+	public Dimension getSize() {
+		return new Dimension(width, height);
+	}
+
+	/**
+	 * Sets the size of the element.
+	 * 
+	 * @param size
+	 *            the new size of the node
+	 */
+	public void setSize(Dimension size) {
+		width = size.width;
+		height = size.height;
+	}
+}

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/dot/GraphLayout.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/dot/GraphLayout.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/dot/GraphLayout.java
new file mode 100644
index 0000000..1205953
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/dot/GraphLayout.java
@@ -0,0 +1,326 @@
+/*******************************************************************************
+ * 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.models.graph.dot;
+
+import static java.lang.Float.parseFloat;
+import static net.sf.taverna.t2.workbench.models.graph.Graph.Alignment.HORIZONTAL;
+
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.models.graph.Graph;
+import net.sf.taverna.t2.workbench.models.graph.GraphController;
+import net.sf.taverna.t2.workbench.models.graph.GraphEdge;
+import net.sf.taverna.t2.workbench.models.graph.GraphElement;
+import net.sf.taverna.t2.workbench.models.graph.GraphNode;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Lays out a graph from a DOT layout.
+ * 
+ * @author David Withers
+ */
+public class GraphLayout implements DOTParserVisitor {
+	private static final Logger logger = Logger.getLogger(GraphLayout.class);
+	private static final int BORDER = 10;
+
+	private Rectangle bounds;
+	private Rectangle requiredBounds;
+	private GraphController graphController;
+	private int xOffset;
+	private int yOffset;
+
+	public Rectangle layoutGraph(GraphController graphController, Graph graph,
+			String laidOutDot, Rectangle requiredBounds) throws ParseException {
+		this.graphController = graphController;
+		this.requiredBounds = requiredBounds;
+
+		bounds = null;
+		xOffset = 0;
+		yOffset = 0;
+
+		logger.debug(laidOutDot);
+		DOTParser parser = new DOTParser(new StringReader(laidOutDot));
+		parser.parse().jjtAccept(this, graph);
+
+		// int xOffset = (bounds.width - bounds.width) / 2;
+		// int yOffset = (bounds.height - bounds.height) / 2;
+
+		return new Rectangle(xOffset, yOffset, bounds.width, bounds.height);
+	}
+
+	@Override
+	public Object visit(SimpleNode node, Object data) {
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTParse node, Object data) {
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTGraph node, Object data) {
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTStatementList node, Object data) {
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTStatement node, Object data) {
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTAttributeStatement node, Object data) {
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTNodeStatement node, Object data) {
+		GraphElement element = graphController.getElement(removeQuotes(node
+				.getName()));
+		if (element != null)
+			return node.childrenAccept(this, element);
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTNodeId node, Object data) {
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTPort node, Object data) {
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTEdgeStatement node, Object data) {
+		StringBuilder id = new StringBuilder();
+		id.append(removeQuotes(node.getName()));
+		if (node.getPort() != null) {
+			id.append(":");
+			id.append(removeQuotes(node.getPort()));
+		}
+		if (node.children != null)
+			for (Node child : node.children)
+				if (child instanceof ASTEdgeRHS) {
+					NamedNode rhsNode = (NamedNode) child.jjtAccept(this, data);
+					id.append("->");
+					id.append(removeQuotes(rhsNode.getName()));
+					if (rhsNode.getPort() != null) {
+						id.append(":");
+						id.append(removeQuotes(rhsNode.getPort()));
+					}
+				}
+		GraphElement element = graphController.getElement(id.toString());
+		if (element != null)
+			return node.childrenAccept(this, element);
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTSubgraph node, Object data) {
+		GraphElement element = graphController.getElement(removeQuotes(
+				node.getName()).substring("cluster_".length()));
+		if (element != null)
+			return node.childrenAccept(this, element);
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTEdgeRHS node, Object data) {
+		return node;
+	}
+
+	@Override
+	public Object visit(ASTAttributeList node, Object data) {
+		return node.childrenAccept(this, data);
+	}
+
+	@Override
+	public Object visit(ASTAList node, Object data) {
+		if (data instanceof Graph) {
+			Graph graph = (Graph) data;
+			if ("bb".equalsIgnoreCase(node.getName())) {
+				Rectangle rect = getRectangle(node.getValue());
+				if (rect.width == 0 && rect.height == 0) {
+					rect.width = 500;
+					rect.height = 500;
+				}
+				if (bounds == null) {
+					bounds = calculateBounds(rect);
+					rect = bounds;
+				}
+				graph.setSize(rect.getSize());
+				graph.setPosition(rect.getLocation());
+			} else if ("lp".equalsIgnoreCase(node.getName())) {
+				if (bounds != null)
+					graph.setLabelPosition(getPoint(node.getValue()));
+			}
+		} else if (data instanceof GraphNode) {
+			GraphNode graphNode = (GraphNode) data;
+			if ("width".equalsIgnoreCase(node.getName()))
+				graphNode.setSize(new Dimension(getSize(node.getValue()),
+						graphNode.getHeight()));
+			else if ("height".equalsIgnoreCase(node.getName()))
+				graphNode.setSize(new Dimension(graphNode.getWidth(),
+						getSize(node.getValue())));
+			else if ("pos".equalsIgnoreCase(node.getName())) {
+				Point position = getPoint(node.getValue());
+				position.x = position.x - (graphNode.getWidth() / 2);
+				position.y = position.y - (graphNode.getHeight() / 2);
+				graphNode.setPosition(position);
+			} else if ("rects".equalsIgnoreCase(node.getName())) {
+				List<Rectangle> rectangles = getRectangles(node.getValue());
+				List<GraphNode> sinkNodes = graphNode.getSinkNodes();
+				if (graphController.getAlignment().equals(HORIZONTAL)) {
+					Rectangle rect = rectangles.remove(0);
+					graphNode.setSize(rect.getSize());
+					graphNode.setPosition(rect.getLocation());
+				} else {
+					Rectangle rect = rectangles.remove(sinkNodes.size());
+					graphNode.setSize(rect.getSize());
+					graphNode.setPosition(rect.getLocation());
+				}
+				Point origin = graphNode.getPosition();
+				for (GraphNode sinkNode : sinkNodes) {
+					Rectangle rect = rectangles.remove(0);
+					rect.setLocation(rect.x - origin.x, rect.y - origin.y);
+					sinkNode.setSize(rect.getSize());
+					sinkNode.setPosition(rect.getLocation());
+				}
+				for (GraphNode sourceNode : graphNode.getSourceNodes()) {
+					Rectangle rect = rectangles.remove(0);
+					rect.setLocation(rect.x - origin.x, rect.y - origin.y);
+					sourceNode.setSize(rect.getSize());
+					sourceNode.setPosition(rect.getLocation());
+				}
+			}
+		} else if (data instanceof GraphEdge) {
+			GraphEdge graphEdge = (GraphEdge) data;
+			if ("pos".equalsIgnoreCase(node.getName()))
+				graphEdge.setPath(getPath(node.getValue()));
+		}
+		return node.childrenAccept(this, data);
+	}
+
+	private Rectangle calculateBounds(Rectangle bounds) {
+		bounds = new Rectangle(bounds);
+		bounds.width += BORDER;
+		bounds.height += BORDER;
+		Rectangle newBounds = new Rectangle(bounds);
+		double ratio = bounds.width / (float) bounds.height;
+		double requiredRatio = requiredBounds.width
+				/ (float) requiredBounds.height;
+		// adjust the bounds so they match the aspect ration of the required bounds
+		if (ratio > requiredRatio)
+			newBounds.height = (int) (ratio / requiredRatio * bounds.height);
+		else if (ratio < requiredRatio)
+			newBounds.width = (int) (requiredRatio / ratio * bounds.width);
+
+		xOffset = (newBounds.width - bounds.width) / 2;
+		yOffset = (newBounds.height - bounds.height) / 2;
+		// adjust the bounds and so they are not less than the required bounds
+		if (newBounds.width < requiredBounds.width) {
+			xOffset += (requiredBounds.width - newBounds.width) / 2;
+			newBounds.width = requiredBounds.width;
+		}
+		if (newBounds.height < requiredBounds.height) {
+			yOffset += (requiredBounds.height - newBounds.height) / 2;
+			newBounds.height = requiredBounds.height;
+		}
+		// adjust the offset for the border
+		xOffset += BORDER / 2;
+		yOffset += BORDER / 2;
+		return newBounds;
+	}
+
+	private List<Point> getPath(String value) {
+		List<Point> path = new ArrayList<>();
+		for (String point : removeQuotes(value).split(" ")) {
+			String[] coords = point.split(",");
+			if (coords.length == 2) {
+				int x = (int) parseFloat(coords[0]) + xOffset;
+				int y = (int) parseFloat(coords[1]) + yOffset;
+				path.add(new Point(x, flipY(y)));
+			}
+		}
+		return path;
+	}
+
+	private int flipY(int y) {
+		return bounds.height - y;
+	}
+
+	private List<Rectangle> getRectangles(String value) {
+		List<Rectangle> rectangles = new ArrayList<>();
+		String[] rects = value.split(" ");
+		for (String rectangle : rects)
+			rectangles.add(getRectangle(rectangle));
+		return rectangles;
+	}
+
+	private Rectangle getRectangle(String value) {
+		String[] coords = removeQuotes(value).split(",");
+		Rectangle rectangle = new Rectangle();
+		rectangle.x = (int) parseFloat(coords[0]);
+		rectangle.y = (int) parseFloat(coords[3]);
+		rectangle.width = (int) parseFloat(coords[2]) - rectangle.x;
+		rectangle.height = rectangle.y - (int) parseFloat(coords[1]);
+		rectangle.x += xOffset;
+		rectangle.y += yOffset;
+		if (bounds != null)
+			rectangle.y = flipY(rectangle.y);
+		else
+			rectangle.y = rectangle.height - rectangle.y;
+		return rectangle;
+	}
+
+	private Point getPoint(String value) {
+		String[] coords = removeQuotes(value).split(",");
+		return new Point(xOffset + (int) parseFloat(coords[0]), flipY(yOffset
+				+ (int) parseFloat(coords[1])));
+	}
+
+	private int getSize(String value) {
+		return (int) (parseFloat(removeQuotes(value)) * 72);
+	}
+
+	private String removeQuotes(String value) {
+		String result = value.trim();
+		if (result.startsWith("\""))
+			result = result.substring(1);
+		if (result.endsWith("\""))
+			result = result.substring(0, result.length() - 1);
+		result = result.replaceAll("\\\\", "");
+		return result;
+	}
+}

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/SVGGraph.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraph.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraph.java
new file mode 100644
index 0000000..be92cdd
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraph.java
@@ -0,0 +1,439 @@
+/*******************************************************************************
+ * 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.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_BLACK_VALUE;
+import static org.apache.batik.util.CSSConstants.CSS_NONE_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_END_VALUE;
+import static org.apache.batik.util.SVGConstants.SVG_FILL_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_MIDDLE_VALUE;
+import static org.apache.batik.util.SVGConstants.SVG_MOUSEMOVE_EVENT_TYPE;
+import static org.apache.batik.util.SVGConstants.SVG_MOUSEUP_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_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.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.GraphEdge;
+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.SVGMouseMovedEventListener;
+import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOutEventListener;
+import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOverEventListener;
+import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseUpEventListener;
+
+import org.apache.batik.dom.svg.SVGOMAnimationElement;
+import org.apache.batik.dom.svg.SVGOMGElement;
+import org.apache.batik.dom.svg.SVGOMPolygonElement;
+import org.apache.batik.dom.svg.SVGOMTextElement;
+import org.w3c.dom.Text;
+import org.w3c.dom.events.EventTarget;
+import org.w3c.dom.svg.SVGElement;
+
+/**
+ * SVG representation of a graph.
+ * 
+ * @author David Withers
+ */
+public class SVGGraph extends Graph {
+	private SVGGraphController graphController;
+	private SVGGraphElementDelegate delegate;
+	private SVGMouseClickEventListener mouseClickAction;
+	private SVGMouseMovedEventListener mouseMovedAction;
+	private SVGMouseUpEventListener mouseUpAction;
+	@SuppressWarnings("unused")
+	private SVGMouseOverEventListener mouseOverAction;
+	@SuppressWarnings("unused")
+	private SVGMouseOutEventListener mouseOutAction;
+	private SVGOMGElement mainGroup, labelGroup;
+	private SVGOMPolygonElement polygon, completedPolygon;
+	private SVGOMTextElement label, iteration, error;
+	private Text labelText, iterationText, errorsText;
+	private SVGOMAnimationElement animateShape, animatePosition, animateLabel;
+
+	public SVGGraph(SVGGraphController graphController) {
+		super(graphController);
+		this.graphController = graphController;
+
+		mouseClickAction = new SVGMouseClickEventListener(this);
+		mouseMovedAction = new SVGMouseMovedEventListener(this);
+		mouseUpAction = new SVGMouseUpEventListener(this);
+		mouseOverAction = new SVGMouseOverEventListener(this);
+		mouseOutAction = new SVGMouseOutEventListener(this);
+
+		mainGroup = graphController.createGElem();
+		mainGroup.setAttribute(SVG_FONT_SIZE_ATTRIBUTE, "10");
+		mainGroup.setAttribute(SVG_FONT_FAMILY_ATTRIBUTE, "Helvetica");
+		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_MOUSEUP_EVENT_TYPE, mouseUpAction, false);
+		// t.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, mouseOverAction, false);
+		// t.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, mouseOutAction, false);
+
+		polygon = graphController.createPolygon();
+		mainGroup.appendChild(polygon);
+
+		completedPolygon = graphController.createPolygon();
+		completedPolygon.setAttribute(SVG_POINTS_ATTRIBUTE,
+				calculatePoints(getShape(), 0, 0));
+		completedPolygon.setAttribute(SVG_FILL_ATTRIBUTE, COMPLETED_COLOUR);
+		// completedPolygon.setAttribute(SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE, "0.8");
+		// completedPolygon.setAttribute(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE);
+		mainGroup.appendChild(completedPolygon);
+
+		labelText = graphController.createText("");
+		label = graphController.createText(labelText);
+		label.setAttribute(SVG_TEXT_ANCHOR_ATTRIBUTE, SVG_MIDDLE_VALUE);
+		label.setAttribute(SVG_FILL_ATTRIBUTE, CSS_BLACK_VALUE);
+		label.setAttribute(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE);
+		labelGroup = graphController.createGElem();
+		labelGroup.appendChild(label);
+		mainGroup.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);
+		polygon.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, CSS_BLACK_VALUE);
+		error.setAttribute(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE);
+		polygon.appendChild(error);
+
+		animateShape = createAnimationElement(graphController, SVG_ANIMATE_TAG,
+				SVG_POINTS_ATTRIBUTE, null);
+
+		animatePosition = createAnimationElement(graphController,
+				SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE,
+				TRANSFORM_TRANSLATE);
+
+		animateLabel = createAnimationElement(graphController,
+				SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE,
+				TRANSFORM_TRANSLATE);
+
+		delegate = new SVGGraphElementDelegate(graphController, this, mainGroup);
+	}
+
+	public SVGElement getSVGElement() {
+		return mainGroup;
+	}
+
+	@Override
+	public void addEdge(GraphEdge edge) {
+		if (edge instanceof SVGGraphEdge) {
+			final SVGGraphEdge svgGraphEdge = (SVGGraphEdge) edge;
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+				    float opacity = svgGraphEdge.getOpacity();
+					svgGraphEdge.setOpacity(0);
+					mainGroup.appendChild(svgGraphEdge.getSVGElement());
+					svgGraphEdge.setOpacity(opacity);
+				}
+			});
+		}
+		super.addEdge(edge);
+	}
+
+	@Override
+	public void addNode(GraphNode node) {
+		super.addNode(node);
+		if (node instanceof SVGGraphNode) {
+			final SVGGraphNode svgGraphNode = (SVGGraphNode) node;
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+				    float opacity = svgGraphNode.getOpacity();
+					svgGraphNode.setOpacity(0);
+					mainGroup.appendChild(svgGraphNode.getSVGElement());
+					svgGraphNode.setOpacity(opacity);
+				}
+			});
+		}
+	}
+
+	@Override
+	public void addSubgraph(Graph subgraph) {
+		super.addSubgraph(subgraph);
+		if (subgraph instanceof SVGGraph) {
+			final SVGGraph svgGraph = (SVGGraph) subgraph;
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+				    float opacity = svgGraph.getOpacity();
+					svgGraph.setOpacity(0);
+					mainGroup.appendChild(svgGraph.getSVGElement());
+					svgGraph.setOpacity(opacity);
+				}
+			});
+		}
+	}
+
+	@Override
+	public boolean removeEdge(GraphEdge edge) {
+		if (edge instanceof SVGGraphEdge) {
+			final SVGGraphEdge svgGraphEdge = (SVGGraphEdge) edge;
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					mainGroup.removeChild(svgGraphEdge.getSVGElement());
+				}
+			});
+		}
+		return super.removeEdge(edge);
+	}
+
+	@Override
+	public boolean removeNode(GraphNode node) {
+		if (node instanceof SVGGraphNode) {
+			final SVGGraphNode svgGraphNode = (SVGGraphNode) node;
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					mainGroup.removeChild(svgGraphNode.getSVGElement());
+				}
+			});
+		}
+		return super.removeNode(node);
+	}
+
+	@Override
+	public boolean removeSubgraph(Graph subgraph) {
+		if (subgraph instanceof SVGGraph) {
+			final SVGGraph svgGraph = (SVGGraph) subgraph;
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					mainGroup.removeChild(svgGraph.getSVGElement());
+				}
+			});
+		}
+		return super.removeSubgraph(subgraph);
+	}
+
+	@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, polygon,
+								graphController.getAnimationSpeed(),
+								oldPosition.x + ", " + oldPosition.y,
+								position.x + ", " + position.y);
+					else
+						polygon.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);
+			updateShape(oldSize.width, oldSize.height);
+		}
+	}
+
+	@Override
+	public void setShape(Shape shape) {
+		final Dimension oldSize = getSize();
+		final Shape currentShape = getShape();
+		if (shape != null && !shape.equals(currentShape)) {
+			super.setShape(shape);
+			updateShape(oldSize.width, oldSize.height);
+		}
+	}
+
+	@Override
+	public void setLabel(final String label) {
+		super.setLabel(label);
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				labelText.setData(label);
+			}
+		});
+	}
+
+	@Override
+	public void setLabelPosition(final Point labelPosition) {
+		final Point oldLabelPosition = getLabelPosition();
+		if (labelPosition != null && !labelPosition.equals(oldLabelPosition)) {
+			super.setLabelPosition(labelPosition);
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					if (graphController.isAnimatable()
+							&& oldLabelPosition != null)
+						animate(animateLabel, labelGroup,
+								graphController.getAnimationSpeed(),
+								oldLabelPosition.x + ", " + oldLabelPosition.y,
+								labelPosition.x + ", " + labelPosition.y);
+					else
+						labelGroup.setAttribute(SVG_TRANSFORM_ATTRIBUTE,
+								"translate(" + labelPosition.x + " "
+										+ labelPosition.y + ")");
+				}
+			});
+		}
+	}
+
+	@Override
+	public void setIteration(final int iteration) {
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				if (iteration > 0)
+					iterationText.setData(String.valueOf(iteration));
+				else
+					iterationText.setData("");
+			}
+		});
+	}
+
+	@Override
+	public void setCompleted(final float complete) {
+		super.setCompleted(complete);
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				Dimension size = getSize();
+				Point position = getPosition();
+				completedPolygon.setAttribute(
+						SVG_POINTS_ATTRIBUTE,
+						calculatePoints(getShape(),
+								(int) (size.width * complete), size.height));
+				completedPolygon.setAttribute(SVG_TRANSFORM_ATTRIBUTE,
+						"translate(" + position.x + " " + position.y + ")");
+			}
+		});
+	}
+
+	private void updateShape(final int oldWidth, final int oldHeight) {
+		if (getShape() != null && getWidth() > 0f && getHeight() > 0f) {
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					if (graphController.isAnimatable())
+						animate(animateShape,
+								polygon,
+								graphController.getAnimationSpeed(),
+								calculatePoints(getShape(), oldWidth, oldHeight),
+								calculatePoints(getShape(), getWidth(),
+										getHeight()));
+					else {
+						polygon.setAttribute(
+								SVG_POINTS_ATTRIBUTE,
+								calculatePoints(getShape(), getWidth(),
+										getHeight()));
+						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 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/SVGGraphController.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphController.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphController.java
new file mode 100644
index 0000000..a4c8e4c
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphController.java
@@ -0,0 +1,555 @@
+/*******************************************************************************
+ * 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.awt.Color.BLACK;
+import static java.awt.Color.GREEN;
+import static java.lang.Float.parseFloat;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.animate;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.calculateAngle;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createAnimationElement;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createSVGDocument;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.getDot;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.getHexValue;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.svgNS;
+import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TAG;
+import static org.apache.batik.util.SVGConstants.SVG_ELLIPSE_TAG;
+import static org.apache.batik.util.SVGConstants.SVG_FILL_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_G_TAG;
+import static org.apache.batik.util.SVGConstants.SVG_LINE_TAG;
+import static org.apache.batik.util.SVGConstants.SVG_PATH_TAG;
+import static org.apache.batik.util.SVGConstants.SVG_POINTS_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_POLYGON_TAG;
+import static org.apache.batik.util.SVGConstants.SVG_RECT_TAG;
+import static org.apache.batik.util.SVGConstants.SVG_STYLE_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_TEXT_TAG;
+import static org.apache.batik.util.SVGConstants.SVG_TRANSFORM_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_VIEW_BOX_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.SVGConstants.SVG_Y_ATTRIBUTE;
+
+import java.awt.Color;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.models.graph.DotWriter;
+import net.sf.taverna.t2.workbench.models.graph.Graph;
+import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment;
+import net.sf.taverna.t2.workbench.models.graph.GraphController;
+import net.sf.taverna.t2.workbench.models.graph.GraphEdge;
+import net.sf.taverna.t2.workbench.models.graph.GraphElement;
+import net.sf.taverna.t2.workbench.models.graph.GraphNode;
+import net.sf.taverna.t2.workbench.models.graph.dot.GraphLayout;
+import net.sf.taverna.t2.workbench.models.graph.dot.ParseException;
+
+import org.apache.batik.bridge.UpdateManager;
+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.swing.JSVGCanvas;
+import org.apache.batik.swing.gvt.GVTTreeRendererAdapter;
+import org.apache.batik.swing.gvt.GVTTreeRendererEvent;
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+import org.w3c.dom.svg.SVGDocument;
+import org.w3c.dom.svg.SVGElement;
+import org.w3c.dom.svg.SVGPoint;
+import org.w3c.dom.svg.SVGSVGElement;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+public class SVGGraphController extends GraphController {
+	private static final Logger logger = Logger.getLogger(SVGGraphController.class);
+	@SuppressWarnings("unused")
+	private static final Timer timer = new Timer("SVG Graph controller timer", true);
+	private static final String dotErrorMessage = "Cannot draw diagram(s)\n" +
+			"\n" +
+			"Install dot as described\n" +
+			"at http://www.taverna.org.uk\n" +
+			"and specify its location\n" +
+			"in the workbench preferences";
+
+	private Map<String, List<SVGGraphEdge>> datalinkMap = new HashMap<>();
+	private final JSVGCanvas svgCanvas;
+	private SVGDocument svgDocument;
+	private GraphLayout graphLayout = new GraphLayout();
+	private EdgeLine edgeLine;
+	private UpdateManager updateManager;
+	private ExecutorService executor = Executors.newFixedThreadPool(1);
+	private boolean drawingDiagram = false;
+	private int animationSpeed;
+	private Rectangle bounds, oldBounds;
+	private SVGOMAnimationElement animateBounds;
+	private boolean dotMissing = false;
+	private final WorkbenchConfiguration workbenchConfiguration;
+
+	public SVGGraphController(Workflow dataflow, Profile profile,
+			boolean interactive, JSVGCanvas svgCanvas, EditManager editManager,
+			MenuManager menuManager, ColourManager colourManager,
+			WorkbenchConfiguration workbenchConfiguration) {
+		super(dataflow, profile, interactive, svgCanvas, editManager,
+				menuManager, colourManager);
+		this.svgCanvas = svgCanvas;
+		this.workbenchConfiguration = workbenchConfiguration;
+		installUpdateManager();
+		layoutSVGDocument(svgCanvas.getBounds());
+		svgCanvas.setDocument(getSVGDocument());
+	}
+
+	public SVGGraphController(Workflow dataflow, Profile profile,
+			boolean interactive, JSVGCanvas svgCanvas, Alignment alignment,
+			PortStyle portStyle, EditManager editManager,
+			MenuManager menuManager, ColourManager colourManager,
+			WorkbenchConfiguration workbenchConfiguration) {
+		super(dataflow, profile, interactive, svgCanvas, alignment, portStyle,
+				editManager, menuManager, colourManager);
+		this.svgCanvas = svgCanvas;
+		this.workbenchConfiguration = workbenchConfiguration;
+		installUpdateManager();
+		layoutSVGDocument(svgCanvas.getBounds());
+		svgCanvas.setDocument(getSVGDocument());
+	}
+
+	private void installUpdateManager() {
+		svgCanvas.addGVTTreeRendererListener(new GVTTreeRendererAdapter() {
+			@Override
+			public void gvtRenderingCompleted(GVTTreeRendererEvent ev) {
+				setUpdateManager(svgCanvas.getUpdateManager());
+			}
+		});
+	}
+	
+	@Override
+	public GraphEdge createGraphEdge() {
+		return new SVGGraphEdge(this);
+	}
+
+	@Override
+	public Graph createGraph() {
+		return new SVGGraph(this);
+	}
+
+	@Override
+	public GraphNode createGraphNode() {
+		return new SVGGraphNode(this);
+	}
+
+	public JSVGCanvas getSVGCanvas() {
+		return svgCanvas;
+	}
+
+	public synchronized SVGDocument getSVGDocument() {
+		if (svgDocument == null)
+			svgDocument = createSVGDocument();
+		return svgDocument;
+	}
+
+	@Override
+	public void redraw() {
+		Graph graph = generateGraph();
+		Rectangle actualBounds = layoutGraph(graph, svgCanvas.getBounds());
+		setBounds(actualBounds);
+		transformGraph(getGraph(), graph);
+	}
+
+	private void layoutSVGDocument(Rectangle bounds) {
+		animateBounds = createAnimationElement(this, SVG_ANIMATE_TAG,
+				SVG_VIEW_BOX_ATTRIBUTE, null);
+		updateManager = null;
+		datalinkMap.clear();
+
+		Graph graph = getGraph();
+		if (graph instanceof SVGGraph) {
+			SVGGraph svgGraph = (SVGGraph) graph;
+			SVGSVGElement svgElement = getSVGDocument().getRootElement();
+			SVGElement graphElement = svgGraph.getSVGElement();
+			svgElement.appendChild(graphElement);
+
+			setBounds(layoutGraph(graph, bounds));
+
+			edgeLine = EdgeLine.createAndAdd(getSVGDocument(), this);
+		}
+		drawingDiagram = true;
+	}
+
+	public Rectangle layoutGraph(Graph graph, Rectangle bounds) {
+		Rectangle actualBounds = null;
+		bounds = new Rectangle(bounds);
+		StringWriter stringWriter = new StringWriter();
+		DotWriter dotWriter = new DotWriter(stringWriter);
+		try {
+			dotWriter.writeGraph(graph);
+			String layout = getDot(stringWriter.toString(), workbenchConfiguration);
+			if (layout.isEmpty())
+				logger.warn("Invalid dot returned");
+			else
+				actualBounds = graphLayout.layoutGraph(this, graph, layout, bounds);
+		} catch (IOException e) {
+			outputMessage(dotErrorMessage);
+			setDotMissing(true);
+			logger.warn("Couldn't generate dot");
+		} catch (ParseException e) {
+			logger.warn("Couldn't layout graph", e);
+		}
+		return actualBounds;
+	}
+
+	private void setDotMissing(boolean b) {
+		this.dotMissing = b;
+	}
+
+	public boolean isDotMissing() {
+		return dotMissing;
+	}
+
+	public void setBounds(final Rectangle bounds) {
+		oldBounds = this.bounds;
+		this.bounds = bounds;
+		updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				SVGSVGElement svgElement = getSVGDocument().getRootElement();
+				if (isAnimatable() && oldBounds != null) {
+					String from = "0 0 " + oldBounds.width + " "
+							+ oldBounds.height;
+					String to = "0 0 " + bounds.width + " " + bounds.height;
+					animate(animateBounds, svgElement, getAnimationSpeed(),
+							from, to);
+				} else if ((svgElement != null) && (bounds != null))
+					svgElement.setAttribute(SVG_VIEW_BOX_ATTRIBUTE,
+							"0 0 " + String.valueOf(bounds.width) + " "
+									+ String.valueOf(bounds.height));
+			}
+		});
+	}
+
+	private void outputMessage(final String message) {
+		SVGSVGElement svgElement = getSVGDocument().getRootElement();
+		String[] parts = message.split("\n");
+		int initialPosition = 200;
+		for (int i = 0; i < parts.length; i++) {
+			Text errorsText = createText(parts[i]);
+			SVGOMTextElement error = (SVGOMTextElement) createElement(SVG_TEXT_TAG);
+			error.setAttribute(SVG_Y_ATTRIBUTE,
+					Integer.toString(initialPosition + i * 60));
+			error.setAttribute(SVG_FONT_SIZE_ATTRIBUTE, "20");
+			error.setAttribute(SVG_FONT_FAMILY_ATTRIBUTE, "sans-serif");
+			error.setAttribute(SVG_FILL_ATTRIBUTE, "red");
+			error.appendChild(errorsText);
+			svgElement.appendChild(error);
+		}
+		bounds = new Rectangle(300, parts.length * 60 + 200);
+		svgCanvas.setDocument(getSVGDocument());
+	}
+
+	public void setUpdateManager(UpdateManager updateManager) {
+		this.updateManager = updateManager;
+		drawingDiagram = false;
+		resetSelection();
+	}
+
+	@Override
+	public boolean startEdgeCreation(GraphElement graphElement, Point point) {
+		boolean alreadyStarted = edgeCreationFromSource || edgeCreationFromSink;
+		boolean started = super.startEdgeCreation(graphElement, point);
+		if (!alreadyStarted && started) {
+			if (edgeMoveElement instanceof SVGGraphEdge) {
+				SVGGraphEdge svgGraphEdge = (SVGGraphEdge) edgeMoveElement;
+				SVGPoint sourcePoint = svgGraphEdge.getPathElement()
+						.getPointAtLength(0f);
+				edgeLine.setSourcePoint(new Point((int) sourcePoint.getX(),
+						(int) sourcePoint.getY()));
+			} else
+				edgeLine.setSourcePoint(point);
+			edgeLine.setTargetPoint(point);
+			edgeLine.setColour(Color.BLACK);
+			// edgeLine.setVisible(true);
+		}
+		return started;
+	}
+
+	@Override
+	public boolean moveEdgeCreationTarget(GraphElement graphElement, Point point) {
+		boolean linkValid = super.moveEdgeCreationTarget(graphElement, point);
+		if (edgeMoveElement instanceof SVGGraphEdge)
+			((SVGGraphEdge) edgeMoveElement).setVisible(false);
+		if (edgeCreationFromSink) {
+			edgeLine.setSourcePoint(point);
+			if (linkValid)
+				edgeLine.setColour(GREEN);
+			else
+				edgeLine.setColour(BLACK);
+			edgeLine.setVisible(true);
+		} else if (edgeCreationFromSource) {
+			edgeLine.setTargetPoint(point);
+			if (linkValid)
+				edgeLine.setColour(GREEN);
+			else
+				edgeLine.setColour(BLACK);
+			edgeLine.setVisible(true);
+		}
+		return linkValid;
+	}
+
+	@Override
+	public boolean stopEdgeCreation(GraphElement graphElement, Point point) {
+		GraphEdge movedEdge = edgeMoveElement;
+		boolean edgeCreated = super.stopEdgeCreation(graphElement, point);
+		if (!edgeCreated && movedEdge instanceof SVGGraphEdge)
+			((SVGGraphEdge) movedEdge).setVisible(true);
+		edgeLine.setVisible(false);
+		return edgeCreated;
+	}
+
+	@Override
+	public void setEdgeActive(String edgeId, boolean active) {
+		if (datalinkMap.containsKey(edgeId))
+			for (GraphEdge datalink : datalinkMap.get(edgeId))
+				datalink.setActive(active);
+	}
+
+	public Element createElement(String tag) {
+		return getSVGDocument().createElementNS(svgNS, tag);
+	}
+
+	SVGOMGElement createGElem() {
+		return (SVGOMGElement) createElement(SVG_G_TAG);
+	}
+
+	SVGOMPolygonElement createPolygon() {
+		return (SVGOMPolygonElement) createElement(SVG_POLYGON_TAG);
+	}
+
+	SVGOMEllipseElement createEllipse() {
+		return (SVGOMEllipseElement) createElement(SVG_ELLIPSE_TAG);
+	}
+
+	SVGOMPathElement createPath() {
+		return (SVGOMPathElement) createElement(SVG_PATH_TAG);
+	}
+
+	SVGOMRectElement createRect() {
+		return (SVGOMRectElement) createElement(SVG_RECT_TAG);
+	}
+
+	public Text createText(String text) {
+		return getSVGDocument().createTextNode(text);
+	}
+
+	SVGOMTextElement createText(Text text) {
+		SVGOMTextElement elem = (SVGOMTextElement) createElement(SVG_TEXT_TAG);
+		elem.appendChild(text);
+		return elem;
+	}
+
+	public void updateSVGDocument(final Runnable thread) {
+		if (updateManager == null && !drawingDiagram)
+			thread.run();
+		else if (!executor.isShutdown())
+			executor.execute(new Runnable() {
+				@Override
+				public void run() {
+					waitForUpdateManager();
+					try {
+						updateManager.getUpdateRunnableQueue().invokeLater(
+								thread);
+					} catch (IllegalStateException e) {
+						logger.error("Update of SVG failed", e);
+					}
+				}
+
+				private void waitForUpdateManager() {
+					try {
+						while (updateManager == null)
+							Thread.sleep(200);
+					} catch (InterruptedException e) {
+					}
+				}
+			});
+//		if (updateManager == null)
+//			thread.run();
+//		else
+//			updateManager.getUpdateRunnableQueue().invokeLater(thread);
+	}
+
+	public boolean isAnimatable() {
+		return animationSpeed > 0 && updateManager != null && !drawingDiagram;
+	}
+
+	/**
+	 * Returns the animation speed in milliseconds.
+	 *
+	 * @return the animation speed in milliseconds
+	 */
+	public int getAnimationSpeed() {
+		return animationSpeed;
+	}
+
+	/**
+	 * Sets the animation speed in milliseconds. A value of 0 turns off animation.
+	 *
+	 * @param animationSpeed the animation speed in milliseconds
+	 */
+	public void setAnimationSpeed(int animationSpeed) {
+		this.animationSpeed = animationSpeed;
+	}
+
+	@Override
+	public void shutdown() {
+		super.shutdown();
+		executor.execute(new Runnable() {
+			@Override
+			public void run() {
+				getSVGCanvas().stopProcessing();
+				executor.shutdown();
+			}
+		});
+	}
+
+}
+
+class EdgeLine {
+	private static final float arrowLength = 10f;
+	private static final float arrowWidth = 3f;
+
+	private Element line;
+	private Element pointer;
+	private SVGGraphController graphController;
+
+	private EdgeLine(SVGGraphController graphController) {
+		this.graphController = graphController;
+	}
+
+	public static EdgeLine createAndAdd(SVGDocument svgDocument, SVGGraphController graphController) {
+		EdgeLine edgeLine = new EdgeLine(graphController);
+		edgeLine.line = svgDocument.createElementNS(svgNS, SVG_LINE_TAG);
+		edgeLine.line.setAttribute(SVG_STYLE_ATTRIBUTE,
+				"fill:none;stroke:black");
+		edgeLine.line.setAttribute("pointer-events", "none");
+		edgeLine.line.setAttribute("visibility", "hidden");
+		edgeLine.line.setAttribute(SVG_X1_ATTRIBUTE, "0");
+		edgeLine.line.setAttribute(SVG_Y1_ATTRIBUTE, "0");
+		edgeLine.line.setAttribute(SVG_X2_ATTRIBUTE, "0");
+		edgeLine.line.setAttribute(SVG_Y2_ATTRIBUTE, "0");
+
+		edgeLine.pointer = svgDocument.createElementNS(svgNS, SVG_POLYGON_TAG);
+		edgeLine.pointer.setAttribute(SVG_STYLE_ATTRIBUTE,
+				"fill:black;stroke:black");
+		edgeLine.pointer.setAttribute(SVG_POINTS_ATTRIBUTE, "0,0 "
+				+ -arrowLength + "," + arrowWidth + " " + -arrowLength + ","
+				+ -arrowWidth + " 0,0");
+		edgeLine.pointer.setAttribute("pointer-events", "none");
+		edgeLine.pointer.setAttribute("visibility", "hidden");
+
+		Element svgRoot = svgDocument.getDocumentElement();
+		svgRoot.insertBefore(edgeLine.line, null);
+		svgRoot.insertBefore(edgeLine.pointer, null);
+
+		return edgeLine;
+	}
+
+	public void setSourcePoint(final Point point) {
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				line.setAttribute(SVG_X1_ATTRIBUTE,
+						String.valueOf(point.getX()));
+				line.setAttribute(SVG_Y1_ATTRIBUTE,
+						String.valueOf(point.getY()));
+
+				float x = parseFloat(line.getAttribute(SVG_X2_ATTRIBUTE));
+				float y = parseFloat(line.getAttribute(SVG_Y2_ATTRIBUTE));
+				double angle = calculateAngle(line);
+
+				pointer.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate(" + x
+						+ " " + y + ") rotate(" + angle + " 0 0) ");
+			}
+		});
+	}
+
+	public void setTargetPoint(final Point point) {
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				line.setAttribute(SVG_X2_ATTRIBUTE,
+						String.valueOf(point.getX()));
+				line.setAttribute(SVG_Y2_ATTRIBUTE,
+						String.valueOf(point.getY()));
+
+				double angle = calculateAngle(line);
+				pointer.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+						+ point.x + " " + point.y + ") rotate(" + angle
+						+ " 0 0) ");
+			}
+		});
+	}
+
+	public void setColour(final Color colour) {
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				String hexColour = getHexValue(colour);
+				line.setAttribute(SVG_STYLE_ATTRIBUTE, "fill:none;stroke:"
+						+ hexColour + ";");
+				pointer.setAttribute(SVG_STYLE_ATTRIBUTE, "fill:" + hexColour
+						+ ";stroke:" + hexColour + ";");
+			}
+		});
+	}
+
+	public void setVisible(final boolean visible) {
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				if (visible) {
+					line.setAttribute("visibility", "visible");
+					pointer.setAttribute("visibility", "visible");
+				} else {
+					line.setAttribute("visibility", "hidden");
+					pointer.setAttribute("visibility", "hidden");
+				}
+			}
+		});
+	}
+}

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/SVGGraphEdge.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphEdge.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphEdge.java
new file mode 100644
index 0000000..7884d62
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphEdge.java
@@ -0,0 +1,311 @@
+/*******************************************************************************
+ * 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.SELECTED_COLOUR;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.adjustPathLength;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.animate;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.calculateAngle;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createAnimationElement;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.getHexValue;
+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_INLINE_VALUE;
+import static org.apache.batik.util.CSSConstants.CSS_NONE_VALUE;
+import static org.apache.batik.util.SMILConstants.SMIL_ADDITIVE_ATTRIBUTE;
+import static org.apache.batik.util.SMILConstants.SMIL_SUM_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_FILL_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_MOUSEDOWN_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_TRANSFORM_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_ZERO_VALUE;
+import static org.apache.batik.util.SVGConstants.TRANSFORM_ROTATE;
+import static org.apache.batik.util.SVGConstants.TRANSFORM_TRANSLATE;
+
+import java.awt.Color;
+import java.awt.Point;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphEdge;
+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.SVGMouseOutEventListener;
+import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOverEventListener;
+
+import org.apache.batik.dom.svg.SVGGraphicsElement;
+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.w3c.dom.events.EventTarget;
+import org.w3c.dom.svg.SVGElement;
+
+/**
+ * SVG representation of a graph edge.
+ * 
+ * @author David Withers
+ */
+public class SVGGraphEdge extends GraphEdge {
+	private static final String ARROW_LENGTH = "8.5";
+	private static final String ARROW_WIDTH = "3";
+	private static final String ELLIPSE_RADIUS = "3.5";
+
+	private SVGGraphController graphController;
+	private SVGGraphElementDelegate delegate;
+	private SVGMouseClickEventListener mouseClickAction;
+	private SVGMouseDownEventListener mouseDownAction;
+	@SuppressWarnings("unused")
+	private SVGMouseOverEventListener mouseOverAction;
+	@SuppressWarnings("unused")
+	private SVGMouseOutEventListener mouseOutAction;
+	private SVGOMGElement mainGroup;
+	private SVGOMPathElement path, deleteButton;
+	private SVGOMPolygonElement polygon;
+	private SVGOMEllipseElement ellipse;
+	private SVGGraphicsElement arrowHead;
+	private SVGOMAnimationElement animatePath, animatePosition, animateRotation;
+
+	public SVGGraphEdge(SVGGraphController graphController) {
+		super(graphController);
+		this.graphController = graphController;
+
+		mouseClickAction = new SVGMouseClickEventListener(this);
+		mouseDownAction = new SVGMouseDownEventListener(this);
+		mouseOverAction = new SVGMouseOverEventListener(this);
+		mouseOutAction = new SVGMouseOutEventListener(this);
+
+		mainGroup = graphController.createGElem();
+		mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, CSS_BLACK_VALUE);
+		mainGroup.setAttribute(SVG_STROKE_DASHARRAY_ATTRIBUTE, CSS_NONE_VALUE);
+		mainGroup.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "1");
+
+		path = graphController.createPath();
+		path.setAttribute(SVG_FILL_ATTRIBUTE, SVG_NONE_VALUE);
+		EventTarget t = (EventTarget) path;
+		t.addEventListener(SVG_CLICK_EVENT_TYPE, mouseClickAction, false);
+		// t.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, mouseOverAction, false);
+		// t.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, mouseOutAction, false);
+		mainGroup.appendChild(path);
+
+		polygon = graphController.createPolygon();
+		polygon.setAttribute(SVG_POINTS_ATTRIBUTE, ARROW_LENGTH + ", 0"
+				+ " 0, -" + ARROW_WIDTH + " 0," + ARROW_WIDTH);
+		t = (EventTarget) polygon;
+		t.addEventListener(SVG_CLICK_EVENT_TYPE, mouseClickAction, 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);
+
+		ellipse = graphController.createEllipse();
+		ellipse.setAttribute(SVG_CX_ATTRIBUTE, ELLIPSE_RADIUS);
+		ellipse.setAttribute(SVG_CY_ATTRIBUTE, SVG_ZERO_VALUE);
+		ellipse.setAttribute(SVG_RX_ATTRIBUTE, ELLIPSE_RADIUS);
+		ellipse.setAttribute(SVG_RY_ATTRIBUTE, ELLIPSE_RADIUS);
+
+		arrowHead = polygon;
+		mainGroup.appendChild(arrowHead);
+
+		deleteButton = graphController.createPath();
+		deleteButton.setAttribute(SVG_STROKE_ATTRIBUTE, "red");
+		deleteButton.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "2");
+		deleteButton.setAttribute(SVG_D_ATTRIBUTE,
+				"M-3.5,-7L3.5,0M-3.5,0L3.5,-7");
+		deleteButton.setAttribute(CSS_DISPLAY_PROPERTY, CSS_NONE_VALUE);
+		mainGroup.appendChild(deleteButton);
+
+		animatePath = createAnimationElement(graphController, SVG_ANIMATE_TAG,
+				SVG_D_ATTRIBUTE, null);
+
+		animatePosition = createAnimationElement(graphController,
+				SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE,
+				TRANSFORM_TRANSLATE);
+
+		animateRotation = createAnimationElement(graphController,
+				SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE,
+				TRANSFORM_ROTATE);
+		animateRotation.setAttribute(SMIL_ADDITIVE_ATTRIBUTE, SMIL_SUM_VALUE);
+
+		delegate = new SVGGraphElementDelegate(graphController, this, mainGroup);
+	}
+
+	public SVGElement getSVGElement() {
+		return mainGroup;
+	}
+
+	/**
+	 * Returns the path.
+	 * 
+	 * @return the path
+	 */
+	public SVGOMPathElement getPathElement() {
+		return path;
+	}
+
+	@Override
+	public void setSelected(boolean selected) {
+		super.setSelected(selected);
+		final String color = selected ? SELECTED_COLOUR
+				: getHexValue(getColor());
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, color);
+				mainGroup.setAttribute(SVG_FILL_ATTRIBUTE, color);
+			}
+		});
+	}
+
+	@Override
+	public void setActive(final boolean active) {
+		super.setActive(active);
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				if (active) {
+					path.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "2");
+					deleteButton.setAttribute(CSS_DISPLAY_PROPERTY,
+							CSS_INLINE_VALUE);
+				} else {
+					path.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "1");
+					deleteButton.setAttribute(CSS_DISPLAY_PROPERTY,
+							CSS_NONE_VALUE);
+				}
+			}
+		});
+	}
+
+	@Override
+	public void setArrowHeadStyle(final ArrowStyle arrowHeadStyle) {
+		super.setArrowHeadStyle(arrowHeadStyle);
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				if (ArrowStyle.NONE.equals(arrowHeadStyle))
+					mainGroup.removeChild(arrowHead);
+				else if (ArrowStyle.NORMAL.equals(arrowHeadStyle)) {
+					mainGroup.removeChild(arrowHead);
+					arrowHead = polygon;
+					mainGroup.appendChild(arrowHead);
+				} else if (ArrowStyle.DOT.equals(arrowHeadStyle)) {
+					mainGroup.removeChild(arrowHead);
+					arrowHead = ellipse;
+					mainGroup.appendChild(arrowHead);
+				}
+			}
+		});
+	}
+
+	@Override
+	public void setPath(final List<Point> pointList) {
+		if (pointList == null)
+			return;
+
+		final List<Point> oldPointList = getPath();
+		super.setPath(pointList);
+		graphController.updateSVGDocument(new Runnable() {
+			@Override
+			public void run() {
+				Point lastPoint = pointList.get(pointList.size() - 1);
+				double angle = calculateAngle(pointList);
+				if (graphController.isAnimatable() && oldPointList != null) {
+					adjustPathLength(oldPointList, pointList.size());
+					Point oldLastPoint = oldPointList.get(oldPointList.size() - 1);
+					double oldAngle = calculateAngle(oldPointList);
+					animate(animatePath, path,
+							graphController.getAnimationSpeed(),
+							SVGUtil.getPath(oldPointList),
+							SVGUtil.getPath(pointList));
+
+					animate(animatePosition, polygon,
+							graphController.getAnimationSpeed(), oldLastPoint.x
+									+ ", " + oldLastPoint.y, lastPoint.x + ", "
+									+ lastPoint.y);
+
+					animate(animateRotation, polygon,
+							graphController.getAnimationSpeed(), oldAngle
+									+ " 0 0", angle + " 0 0");
+
+					ellipse.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+							+ lastPoint.x + " " + lastPoint.y + ") rotate("
+							+ angle + " 0 0) ");
+					deleteButton.setAttribute(SVG_TRANSFORM_ATTRIBUTE,
+							"translate(" + lastPoint.x + " " + lastPoint.y
+									+ ")");
+				} else {
+					path.setAttribute(SVG_D_ATTRIBUTE,
+							SVGUtil.getPath(pointList));
+					polygon.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+							+ lastPoint.x + " " + lastPoint.y + ") rotate("
+							+ angle + " 0 0) ");
+					ellipse.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+							+ lastPoint.x + " " + lastPoint.y + ") rotate("
+							+ angle + " 0 0) ");
+					deleteButton.setAttribute(SVG_TRANSFORM_ATTRIBUTE,
+							"translate(" + lastPoint.x + " " + lastPoint.y
+									+ ")");
+				}
+			}
+		});
+	}
+
+	@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/SVGGraphElementDelegate.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphElementDelegate.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphElementDelegate.java
new file mode 100644
index 0000000..cf7f852
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphElementDelegate.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (C) 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.models.graph.svg;
+
+import static net.sf.taverna.t2.workbench.models.graph.GraphElement.LineStyle.NONE;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphSettings.SELECTED_COLOUR;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.animate;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createAnimationElement;
+import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.getHexValue;
+import static org.apache.batik.util.CSSConstants.CSS_DISPLAY_PROPERTY;
+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_OPACITY_PROPERTY;
+import static org.apache.batik.util.CSSConstants.CSS_POINTER_EVENTS_PROPERTY;
+import static org.apache.batik.util.CSSConstants.CSS_VISIBLEPAINTED_VALUE;
+import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TAG;
+import static org.apache.batik.util.SVGConstants.SVG_FILL_ATTRIBUTE;
+import static org.apache.batik.util.SVGConstants.SVG_NONE_VALUE;
+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 java.awt.Color;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphElement;
+import net.sf.taverna.t2.workbench.models.graph.GraphElement.LineStyle;
+
+import org.apache.batik.dom.svg.SVGOMAnimationElement;
+import org.apache.batik.dom.svg.SVGOMElement;
+
+/**
+ * Delegate for GraphElements. Logically a superclass of SVGGraph, SVGGraphNode
+ * and SVGGraphEdge (if java had multiple inheritance).
+ * 
+ * @author David Withers
+ */
+public class SVGGraphElementDelegate {
+	private SVGGraphController graphController;
+	private GraphElement graphElement;
+	private SVGOMElement mainGroup;
+	private SVGOMAnimationElement animateOpacity;
+
+	public SVGGraphElementDelegate(SVGGraphController graphController,
+			GraphElement graphElement, SVGOMElement mainGroup) {
+		this.graphController = graphController;
+		this.graphElement = graphElement;
+		this.mainGroup = mainGroup;
+
+		animateOpacity = createAnimationElement(graphController,
+				SVG_ANIMATE_TAG, CSS_OPACITY_PROPERTY, null);
+	}
+
+	public void setSelected(final boolean selected) {
+		boolean currentSelected = graphElement.isSelected();
+		if (currentSelected != selected
+				&& !LineStyle.NONE.equals(graphElement.getLineStyle()))
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE,
+							selected ? SELECTED_COLOUR
+									: getHexValue(graphElement.getColor()));
+					mainGroup.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE,
+							selected ? "2" : "1");
+				}
+			});
+	}
+
+	public void setLineStyle(final LineStyle lineStyle) {
+		LineStyle currentLineStyle = graphElement.getLineStyle();
+		if (!currentLineStyle.equals(lineStyle))
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					String stroke = SVG_NONE_VALUE, dash = SVG_NONE_VALUE;
+					switch (lineStyle) {
+					case DOTTED:
+						stroke = getHexValue(graphElement.getColor());
+						dash = "1,5";
+						break;
+					case SOLID:
+						stroke = getHexValue(graphElement.getColor());
+					default:
+						break;
+					}
+					mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, stroke);
+					mainGroup
+							.setAttribute(SVG_STROKE_DASHARRAY_ATTRIBUTE, dash);
+				}
+			});
+	}
+
+	public void setColor(final Color color) {
+		Color currentColor = graphElement.getColor();
+		if (currentColor != color && NONE != graphElement.getLineStyle())
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE,
+							getHexValue(color));
+				}
+			});
+	}
+
+	public void setFillColor(final Color fillColor) {
+		Color currentFillColor = graphElement.getFillColor();
+		if (currentFillColor != fillColor)
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					mainGroup.setAttribute(SVG_FILL_ATTRIBUTE,
+							getHexValue(fillColor));
+				}
+			});
+	}
+
+	public void setVisible(final boolean visible) {
+		boolean currentVisible = graphElement.isVisible();
+		if (currentVisible != visible)
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					mainGroup.setAttribute(CSS_DISPLAY_PROPERTY,
+							visible ? CSS_INLINE_VALUE : CSS_NONE_VALUE);
+				}
+			});
+	}
+
+	public void setOpacity(final float opacity) {
+		final float currentOpacity = graphElement.getOpacity();
+		if (currentOpacity != opacity)
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					if (graphController.isAnimatable())
+						animate(animateOpacity, mainGroup,
+								graphController.getAnimationSpeed(),
+								String.valueOf(currentOpacity),
+								String.valueOf(opacity));
+					else
+						mainGroup.setAttribute(CSS_OPACITY_PROPERTY,
+								String.valueOf(opacity));
+				}
+			});
+	}
+
+	public void setFiltered(final boolean filtered) {
+		boolean currentFiltered = graphElement.isFiltered();
+		if (currentFiltered != filtered)
+			graphController.updateSVGDocument(new Runnable() {
+				@Override
+				public void run() {
+					mainGroup.setAttribute(CSS_POINTER_EVENTS_PROPERTY,
+							filtered ? CSS_NONE_VALUE
+									: CSS_VISIBLEPAINTED_VALUE);
+					setOpacity(filtered ? 0.2f : 1f);
+				}
+			});
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueClient.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueClient.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueClient.java
new file mode 100644
index 0000000..0e033cc
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueClient.java
@@ -0,0 +1,785 @@
+package net.sf.taverna.biocatalogue.model.connectivity;
+
+import java.io.*;
+import java.net.*;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import net.sf.taverna.biocatalogue.model.BioCataloguePluginConstants;
+import net.sf.taverna.biocatalogue.model.Pair;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.biocatalogue.model.SoapOperationPortIdentity;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI.ResourceIndex;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.config.BioCataloguePluginConfiguration;
+
+import org.apache.log4j.Logger;
+import org.biocatalogue.x2009.xml.rest.Annotations;
+import org.biocatalogue.x2009.xml.rest.AnnotationsDocument;
+import org.biocatalogue.x2009.xml.rest.CollectionCoreStatistics;
+import org.biocatalogue.x2009.xml.rest.Filters;
+import org.biocatalogue.x2009.xml.rest.FiltersDocument;
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+import org.biocatalogue.x2009.xml.rest.RestMethod;
+import org.biocatalogue.x2009.xml.rest.RestMethodDocument;
+import org.biocatalogue.x2009.xml.rest.RestMethods;
+import org.biocatalogue.x2009.xml.rest.RestMethodsDocument;
+import org.biocatalogue.x2009.xml.rest.Search;
+import org.biocatalogue.x2009.xml.rest.SearchDocument;
+import org.biocatalogue.x2009.xml.rest.Service;
+import org.biocatalogue.x2009.xml.rest.ServiceDocument;
+import org.biocatalogue.x2009.xml.rest.ServiceProvider;
+import org.biocatalogue.x2009.xml.rest.ServiceProviderDocument;
+import org.biocatalogue.x2009.xml.rest.ServiceProviders;
+import org.biocatalogue.x2009.xml.rest.ServiceProvidersDocument;
+import org.biocatalogue.x2009.xml.rest.Services;
+import org.biocatalogue.x2009.xml.rest.ServicesDocument;
+import org.biocatalogue.x2009.xml.rest.SoapInput;
+import org.biocatalogue.x2009.xml.rest.SoapInputDocument;
+import org.biocatalogue.x2009.xml.rest.SoapOperation;
+import org.biocatalogue.x2009.xml.rest.SoapOperationDocument;
+import org.biocatalogue.x2009.xml.rest.SoapOperations;
+import org.biocatalogue.x2009.xml.rest.SoapOperationsDocument;
+import org.biocatalogue.x2009.xml.rest.SoapOutput;
+import org.biocatalogue.x2009.xml.rest.SoapOutputDocument;
+import org.biocatalogue.x2009.xml.rest.SoapService;
+import org.biocatalogue.x2009.xml.rest.SoapServiceDocument;
+import org.biocatalogue.x2009.xml.rest.Tag;
+import org.biocatalogue.x2009.xml.rest.TagDocument;
+import org.biocatalogue.x2009.xml.rest.Tags;
+import org.biocatalogue.x2009.xml.rest.TagsDocument;
+import org.biocatalogue.x2009.xml.rest.User;
+import org.biocatalogue.x2009.xml.rest.UserDocument;
+import org.biocatalogue.x2009.xml.rest.Users;
+import org.biocatalogue.x2009.xml.rest.UsersDocument;
+
+import com.google.gson.Gson;
+
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class BioCatalogueClient
+{
+  // ******* CONSTANTS *******
+  // plugin details
+  public static final String PLUGIN_VERSION = "0.1.1";
+  public static final String PLUGIN_USER_AGENT = "Taverna2-ServiceCatalogue-plugin/" +
+                                                 PLUGIN_VERSION +
+                                                 " Java/" + System.getProperty("java.version");
+  
+  public static final String XML_MIME_TYPE = "application/xml";
+  public static final String JSON_MIME_TYPE = "application/json";
+  public static final String LITE_JSON_MIME_TYPE = "application/biocat-lite+json";
+  
+  public static final String XML_DATA_FORMAT = ".xml";
+  public static final String JSON_DATA_FORMAT = ".json";
+  public static final String LITE_JSON_DATA_FORMAT = ".bljson";
+  
+  
+  
+  // API URLs
+  public static final String DEFAULT_API_SANDBOX_BASE_URL = "http://sandbox.biocatalogue.org";
+  public static final String DEFAULT_API_TEST_SERVER_BASE_URL = "http://test.biocatalogue.org";
+  public static final String DEFAULT_API_LIVE_SERVER_BASE_URL = "http://www.biocatalogue.org";
+  
+  private static String BASE_URL;    // BioCatalogue base URL to use (can be updated at runtime)
+  
+  public static String API_REGISTRIES_URL;
+  public static String API_SERVICE_PROVIDERS_URL;
+  public static String API_USERS_URL;
+  public static String API_USER_FILTERS_URL;
+  public static String API_SERVICES_URL;
+  public static String API_SERVICE_FILTERS_URL;
+  public static String API_SOAP_OPERATIONS_URL;
+  public static String API_SOAP_OPERATION_FILTERS_URL;
+  public static String API_REST_METHODS_URL;
+  public static String API_REST_METHOD_FILTERS_URL;
+  public static String API_TAG_CLOUD_URL;
+  public static String API_SEARCH_URL;
+  public static String API_LOOKUP_URL;
+  
+  // URL modifiers
+  public static final Map<String,String> API_INCLUDE_SUMMARY = Collections.singletonMap("include","summary");          // for fetching Service
+  public static final Map<String,String> API_INCLUDE_ANCESTORS = Collections.singletonMap("include", "ancestors,inputs,outputs");     // for fetching SOAP Operations and REST Methods
+  public static final String[] API_SORT_BY_NAME = {"sort","name"};                   // for tag cloud
+  public static final String[] API_SORT_BY_COUNTS = {"sort","counts"};               // for tag cloud
+  public static final String[] API_ALSO_INPUTS_OUTPUTS = {"also","inputs,outputs"};  // for annotations on SOAP operation
+  
+  public static final String API_PER_PAGE_PARAMETER = "per_page";
+  public static final String API_PAGE_PARAMETER = "page";
+  public static final String API_LIMIT_PARAMETER = "limit";
+  public static final String API_SERVICE_MONITORING_URL_SUFFIX = "/monitoring";
+  public static final String API_FILTERED_INDEX_SUFFIX = "/filtered_index";
+  
+  // API Request scope
+  public static final String API_SCOPE_PARAMETER = "scope";
+  public static final String API_SCOPE_SOAP_OPERATIONS = "soap_operations";
+  public static final String API_SCOPE_REST_METHODS = "rest_methods";
+  public static final String API_SCOPE_SERVICES = "services";
+  public static final String API_SCOPE_SERVICE_PROVIDERS = "service_providers";
+  public static final String API_SCOPE_REGISTRIES = "registries";
+  public static final String API_SCOPE_USERS = "users";
+  
+  public static final String API_TAG_PARAMETER = "tag";
+  
+  public static final String API_LOOKUP_WSDL_LOCATION_PARAMETER = "wsdl_location";
+  public static final String API_LOOKUP_OPERATION_NAME_PARAMETER = "operation_name";
+  public static final String API_LOOKUP_SOAP_INPUT_NAME_PARAMETER = "input_name";
+  public static final String API_LOOKUP_SOAP_OUTPUT_NAME_PARAMETER = "output_name";
+  
+  
+  // *************************
+  
+  // universal date formatters
+  private static final DateFormat DATE_FORMATTER = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy");
+  private static final DateFormat SHORT_DATE_FORMATTER = new SimpleDateFormat("HH:mm 'on' dd/MM/yyyy");
+  private static final DateFormat API_LOGGING_TIMESTAMP_FORMATTER = DateFormat.getDateTimeInstance();
+  
+  
+  // SETTINGS
+  private Properties iniSettings;    // settings that are read/stored from/to INI file
+  
+  private File fAPIOperationLog;
+  private PrintWriter pwAPILogWriter;
+  
+  // the logger
+  private Logger logger = Logger.getLogger(BioCatalogueClient.class);
+  
+  private static BioCatalogueClient INSTANCE;
+  
+  // default constructor
+  private BioCatalogueClient()
+  {
+    // TODO: load any config settings (if necessary)
+    
+    // load the BioCatalogue API base URL from the plugin's configuration settings
+    this.setBaseURL(BioCataloguePluginConfiguration.getInstance().
+            getProperty(BioCataloguePluginConfiguration.SERVICE_CATALOGUE_BASE_URL));
+    
+    // open API operation log file, if necessary
+    if (BioCataloguePluginConstants.PERFORM_API_RESPONSE_TIME_LOGGING || 
+        BioCataloguePluginConstants.PERFORM_API_XML_DATA_BINDING_TIME_LOGGING )
+    {
+      try {
+        BioCataloguePluginConstants.LOG_FILE_FOLDER.mkdirs(); // just in case this log file was never written - create the folder as well
+        fAPIOperationLog = new File(BioCataloguePluginConstants.LOG_FILE_FOLDER, 
+                                    BioCataloguePluginConstants.API_OPERATION_LOG_FILENAME);
+        pwAPILogWriter = new PrintWriter(new FileOutputStream(fAPIOperationLog, true), true);  // auto-flush makes sure that even if app crashes, log will not be lost
+      }
+      catch (NullPointerException e) {
+        pwAPILogWriter = new PrintWriter(System.out, true);
+        logger.error("ERROR: Folder to log API operation details is unknown (using System.out instead)... Details:", e);
+      }
+      catch (FileNotFoundException e) {
+        logger.error("ERROR: Couldn't open API operation log file... Details:", e);
+      }
+    }
+  }
+  
+  public static synchronized BioCatalogueClient getInstance() {
+	  if (INSTANCE == null) {
+		  INSTANCE = new BioCatalogueClient();
+	  }
+	  return INSTANCE;
+  }
+  
+  
+  public String getBaseURL() {
+    return this.BASE_URL;
+  }
+  
+  /**
+   * Updates the base API URL and also
+   * updates derived URLs of sub-URLs
+   * (e.g. BASE_URL + /services, etc)
+   * 
+   * @param baseURL The new value for the BioCatalogue API base URL.
+   */
+  public void setBaseURL(String baseURL)
+  {
+    // make sure the base URL doesn't have a slash at the end
+    // (otherwise double slashes may occur during URL manipulation)
+    while (baseURL.endsWith("/")) { baseURL = baseURL.substring(0, baseURL.length() - 1); }
+    
+    this.BASE_URL = baseURL;
+    
+    API_REGISTRIES_URL = BASE_URL + "/registries";
+    API_SERVICE_PROVIDERS_URL = BASE_URL + "/service_providers";
+    API_USERS_URL = BASE_URL + "/users";
+    API_USER_FILTERS_URL = API_USERS_URL + "/filters";
+    API_SERVICES_URL = BASE_URL + "/services";
+    API_SERVICE_FILTERS_URL = API_SERVICES_URL + "/filters";
+    API_SOAP_OPERATIONS_URL = BASE_URL + "/soap_operations";
+    API_SOAP_OPERATION_FILTERS_URL = API_SOAP_OPERATIONS_URL + "/filters";
+    API_REST_METHODS_URL = BASE_URL + "/rest_methods";
+    API_REST_METHOD_FILTERS_URL = API_REST_METHODS_URL + "/filters";
+    API_TAG_CLOUD_URL = BASE_URL + "/tags";
+    API_SEARCH_URL = BASE_URL + "/search";
+    API_LOOKUP_URL = BASE_URL + "/lookup";
+  }
+  
+  public File getAPIOperationLog() {
+    return fAPIOperationLog;
+  }
+  
+  public PrintWriter getAPILogWriter() {
+    return pwAPILogWriter;
+  }
+  
+  
+  // ************ METHODS FOR RETRIEVAL OF SPECIALISED OBJECT FROM THE API VIA XML ************
+  
+  public Annotations getBioCatalogueAnnotations(String strAnnotationsURL) throws Exception {
+    return (parseAPIResponseStream(Annotations.class, doBioCatalogueGET(strAnnotationsURL)));
+  }
+  
+  public Filters getBioCatalogueFilters(String strURL) throws Exception {
+    return (parseAPIResponseStream(Filters.class, doBioCatalogueGET(strURL)));
+  }
+  
+  public Services getBioCatalogueServices(String strURL) throws Exception {
+    return (parseAPIResponseStream(Services.class, doBioCatalogueGET(strURL)));
+  }
+  
+  public Service getBioCatalogueService(String serviceURL) throws Exception {
+    return (parseAPIResponseStream(Service.class, doBioCatalogueGET(serviceURL)));
+  }
+  
+  public Service getBioCatalogueServiceSummary(String serviceURL) throws Exception {
+    return (parseAPIResponseStream(Service.class, doBioCatalogueGET(Util.appendAllURLParameters(serviceURL, API_INCLUDE_SUMMARY))));
+  }
+  
+  public Service getBioCatalogueServiceMonitoringData(String serviceURL) throws Exception
+  {
+    return (parseAPIResponseStream(Service.class,
+                                   doBioCatalogueGET(serviceURL + API_SERVICE_MONITORING_URL_SUFFIX))
+           );
+  }
+  
+  public SoapService getBioCatalogueSoapService(String soapServiceURL) throws Exception {
+    return (parseAPIResponseStream(SoapService.class, doBioCatalogueGET(soapServiceURL)));
+  }
+  
+  public SoapOperation getBioCatalogueSoapOperation(String soapOperationURL) throws Exception {
+    return (parseAPIResponseStream(SoapOperation.class, doBioCatalogueGET(soapOperationURL)));
+  }
+  
+  public RestMethod getBioCatalogueRestMethod(String restMethodURL) throws Exception {
+    return (parseAPIResponseStream(RestMethod.class, doBioCatalogueGET(restMethodURL)));
+  }
+  
+  public Search getBioCatalogueSearchData(String searchURL) throws Exception {
+    return (parseAPIResponseStream(Search.class, doBioCatalogueGET(searchURL)));
+  }
+  
+  public Tag getBioCatalogueTag(String searchByTagURL) throws Exception {
+    return (parseAPIResponseStream(Tag.class, doBioCatalogueGET(searchByTagURL)));
+  }
+  
+  public Tags getBioCatalogueTags(String tagsURL) throws Exception {
+    return (parseAPIResponseStream(Tags.class, doBioCatalogueGET(tagsURL)));
+  }
+  
+  
+  public ResourceLink getBioCatalogueResource(Class<? extends ResourceLink> classOfResourceToFetch, String resourceURL) throws Exception {
+    return (parseAPIResponseStream(classOfResourceToFetch, doBioCatalogueGET(resourceURL)));
+  }
+  
+  
+  public <T extends ResourceLink> Pair<CollectionCoreStatistics, List<T>> getListOfItemsFromResourceCollectionIndex(
+      Class<T> classOfCollectionOfRequiredReturnedObjects, BioCatalogueAPIRequest filteringRequest) throws Exception
+  {
+    ResourceLink matchingItems = null;
+    if (filteringRequest.getRequestType() == BioCatalogueAPIRequest.TYPE.GET) {
+      matchingItems = parseAPIResponseStream(classOfCollectionOfRequiredReturnedObjects, doBioCatalogueGET(filteringRequest.getURL()));
+    }
+    else {
+      matchingItems = parseAPIResponseStream(classOfCollectionOfRequiredReturnedObjects,
+                           doBioCataloguePOST_SendJSON_AcceptXML(filteringRequest.getURL(), filteringRequest.getData()));
+    }
+    
+    CollectionCoreStatistics statistics = null;
+    
+    List<T> matchingItemList = new ArrayList<T>();
+    
+    // SOAP Operations
+    if (classOfCollectionOfRequiredReturnedObjects.equals(SoapOperations.class)) {
+      SoapOperations soapOperations = (SoapOperations)matchingItems;
+      matchingItemList.addAll((Collection<? extends T>)(soapOperations.getResults().getSoapOperationList()));
+      statistics = soapOperations.getStatistics();
+    }
+    
+    // REST Methods
+    else if (classOfCollectionOfRequiredReturnedObjects.equals(RestMethods.class)) {
+      RestMethods restMethods = (RestMethods)matchingItems;
+      matchingItemList.addAll((Collection<? extends T>)(restMethods.getResults().getRestMethodList()));
+      statistics = restMethods.getStatistics();
+    }
+    
+    // Services
+    else if (classOfCollectionOfRequiredReturnedObjects.equals(Services.class)) {
+      Services services = (Services)matchingItems;
+      matchingItemList.addAll((Collection<? extends T>)(services.getResults().getServiceList()));
+      statistics = services.getStatistics();
+    }
+    
+    // Service Providers
+    else if (classOfCollectionOfRequiredReturnedObjects.equals(ServiceProviders.class)) {
+      ServiceProviders serviceProviders = (ServiceProviders)matchingItems;
+      matchingItemList.addAll((Collection<? extends T>)(serviceProviders.getResults().getServiceProviderList()));
+      statistics = serviceProviders.getStatistics();
+    }
+    
+    // Users
+    else if (classOfCollectionOfRequiredReturnedObjects.equals(Users.class)) {
+      Users users = (Users)matchingItems;
+      matchingItemList.addAll((Collection<? extends T>)(users.getResults().getUserList()));
+      statistics = users.getStatistics();
+    }
+    
+    // no such option - error
+    else {
+      return null;
+    }
+    
+    return new Pair<CollectionCoreStatistics, List<T>>(statistics, matchingItemList);
+  }
+  
+  
+  
+  
+  /**
+   * @param wsdlLocation
+   * @param operationName
+   * @return SoapOperation instance or <code>null</code> if nothing was found (or error occurred).
+   * @throws Exception
+   */
+  public SoapOperation lookupSoapOperation(SoapOperationIdentity soapOperationDetails) throws Exception
+  {
+    // first of all check for any problems with input data
+    if (soapOperationDetails == null || soapOperationDetails.hasError() ||
+        soapOperationDetails.getWsdlLocation() == null || soapOperationDetails.getWsdlLocation().length() == 0 ||
+        soapOperationDetails.getOperationName() == null || soapOperationDetails.getOperationName().length() == 0)
+    {
+      // something's not right - return null
+      return (null);
+    }
+    
+    String lookupURL = Util.appendURLParameter(API_LOOKUP_URL, API_LOOKUP_WSDL_LOCATION_PARAMETER, soapOperationDetails.getWsdlLocation());
+    lookupURL = Util.appendURLParameter(lookupURL, API_LOOKUP_OPERATION_NAME_PARAMETER, soapOperationDetails.getOperationName());
+    
+    ServerResponseStream lookupResponse = doBioCatalogueGET(lookupURL);
+    if (lookupResponse.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
+      return null;
+    }
+    return (parseAPIResponseStream(SoapOperation.class, lookupResponse));
+  }
+  
+  
+  public <T extends ResourceLink> T lookupSoapOperationPort(Class<T> requiredResultClass, SoapOperationPortIdentity portDetails) throws Exception
+  {
+    // first of all check for any problems with port details
+    if (portDetails == null || portDetails.hasError() ||
+        portDetails.getWsdlLocation() == null || portDetails.getWsdlLocation().length() == 0 ||
+        portDetails.getOperationName() == null || portDetails.getOperationName().length() == 0 ||
+        portDetails.getPortName() == null || portDetails.getPortName().length() == 0)
+    {
+      // something's not right - return null
+      return (null);
+    }
+    
+    // now check that specified class matches the port type
+    if (portDetails.isInput() && !requiredResultClass.equals(SoapInput.class) ||
+        !portDetails.isInput() && !requiredResultClass.equals(SoapOutput.class))
+    {
+      return (null);
+    }
+    
+    String lookupURL = Util.appendURLParameter(API_LOOKUP_URL, API_LOOKUP_WSDL_LOCATION_PARAMETER, portDetails.getWsdlLocation());
+    lookupURL = Util.appendURLParameter(lookupURL, API_LOOKUP_OPERATION_NAME_PARAMETER, portDetails.getOperationName());
+    if (portDetails.isInput()) {
+      lookupURL = Util.appendURLParameter(lookupURL, API_LOOKUP_SOAP_INPUT_NAME_PARAMETER, portDetails.getPortName());
+    }
+    else {
+      lookupURL = Util.appendURLParameter(lookupURL, API_LOOKUP_SOAP_OUTPUT_NAME_PARAMETER, portDetails.getPortName());
+    }
+    
+    ServerResponseStream lookupResponse = doBioCatalogueGET(lookupURL);
+    if (lookupResponse.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
+      return null;
+    }
+    return (parseAPIResponseStream(requiredResultClass, lookupResponse));
+  }
+  
+  
+  public Service lookupParentService(SoapOperationIdentity soapOperationDetails) throws Exception
+  {
+    SoapOperation soapOperation = this.lookupSoapOperation(soapOperationDetails);
+    if (soapOperation != null) {
+      return (getBioCatalogueService(soapOperation.getAncestors().getService().getHref()));
+    }
+    else {
+      // lookup didn't find the SOAP operation or there
+      // was some problem with the input data
+      return (null);
+    }
+  }
+  
+  
+  public Service lookupParentServiceMonitoringData(SoapOperationIdentity soapOperationDetails) throws Exception
+  {
+    SoapOperation soapOperation = this.lookupSoapOperation(soapOperationDetails);
+    if (soapOperation != null) {
+      return (getBioCatalogueServiceMonitoringData(soapOperation.getAncestors().getService().getHref()));
+    }
+    else {
+      // lookup didn't find the SOAP operation or there
+      // was some problem with the input data
+      return (null);
+    }
+  }
+  
+  
+  // ************ METHODS FOR RETRIEVAL OF SPECIALISED OBJECT FROM THE API VIA LITE JSON ************
+  
+  public BeansForJSONLiteAPI.ResourceIndex getBioCatalogueResourceLiteIndex(TYPE resourceType, String resourceIndexURL) throws Exception
+  {
+    ServerResponseStream response = doBioCatalogueGET_LITE_JSON(resourceIndexURL);
+    
+    Gson gson = new Gson();
+    return (ResourceIndex)(gson.fromJson(new InputStreamReader(response.getResponseStream()), resourceType.getJsonLiteAPIBindingBeanClass()));
+  }
+  
+  
+  public BeansForJSONLiteAPI.ResourceIndex postBioCatalogueResourceLiteIndex(TYPE resourceType, String resourceIndexURL, String postData) throws Exception
+  {
+    ServerResponseStream response = doBioCataloguePOST_SendJSON_AcceptLITEJSON(resourceIndexURL, postData);
+    
+    Gson gson = new Gson();
+    return (ResourceIndex)(gson.fromJson(new InputStreamReader(response.getResponseStream()), resourceType.getJsonLiteAPIBindingBeanClass()));
+  }
+  
+  
+  // ************ GENERIC API CONNECTIVITY METHODS ************
+  
+  /**
+   * Generic method to issue GET requests to BioCatalogue server.
+   * 
+   * This is a convenience method to be used instead of {@link BioCatalogueClient#doBioCatalogueGET_XML(String)}.
+   * 
+   * @param strURL The URL on BioCatalogue to issue GET request to.
+   * @return TODO
+   * @throws Exception
+   */
+  public ServerResponseStream doBioCatalogueGET(String strURL) throws Exception {
+    return (doBioCatalogueGET_XML(strURL));
+  }
+  
+  public ServerResponseStream doBioCatalogueGET_XML(String strURL) throws Exception {
+    return (doBioCatalogueGET(strURL, XML_MIME_TYPE, XML_DATA_FORMAT));
+  }
+  
+  public ServerResponseStream doBioCatalogueGET_JSON(String strURL) throws Exception {
+    return (doBioCatalogueGET(strURL, JSON_MIME_TYPE, JSON_DATA_FORMAT));
+  }
+  
+  public ServerResponseStream doBioCatalogueGET_LITE_JSON(String strURL) throws Exception {
+    return (doBioCatalogueGET(strURL, LITE_JSON_MIME_TYPE, LITE_JSON_DATA_FORMAT));
+  }
+  
+  
+  public ServerResponseStream doBioCatalogueGET(String strURL, String ACCEPT_HEADER, String REQUESTED_DATA_FORMAT) throws Exception
+  {
+    // TODO - HACK to speed up processing append .xml / .json / .bljson to all URLs to avoid LinkedData content negotiation
+    strURL = Util.appendStringBeforeParametersOfURL(strURL, REQUESTED_DATA_FORMAT);
+    
+    // open server connection using provided URL (with no further modifications to it)
+    URL url = new URL(strURL);
+    
+    Calendar requestStartedAt = Calendar.getInstance();
+    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+    conn.setRequestProperty("User-Agent", PLUGIN_USER_AGENT);
+    conn.setRequestProperty("Accept", ACCEPT_HEADER);
+    
+//    if(LOGGED_IN) {
+//      // if the user has "logged in", also add authentication details
+//      conn.setRequestProperty("Authorization", "Basic " + AUTH_STRING);
+//    }
+    
+    // fetch server's response
+    ServerResponseStream serverResponse = doBioCatalogueReceiveServerResponse(conn, strURL, true);
+    
+    if (BioCataloguePluginConstants.PERFORM_API_RESPONSE_TIME_LOGGING) {
+      logAPIOperation(requestStartedAt, "GET", serverResponse);
+    }
+    return (serverResponse);
+  }
+  
+  
+  
+  public ServerResponseStream doBioCataloguePOST_SendJSON_AcceptXML(String strURL, String strDataBody) throws Exception {
+    return (doBioCataloguePOST(strURL, strDataBody, JSON_MIME_TYPE, XML_MIME_TYPE, XML_DATA_FORMAT));
+  }
+  
+  public ServerResponseStream doBioCataloguePOST_SendJSON_AcceptLITEJSON(String strURL, String strDataBody) throws Exception {
+    return (doBioCataloguePOST(strURL, strDataBody, JSON_MIME_TYPE, LITE_JSON_MIME_TYPE, LITE_JSON_DATA_FORMAT));
+  }
+  
+  
+  /**
+   * Generic method to execute POST requests to BioCatalogue server.
+   * 
+   * @param strURL The URL on BioCatalogue to POST to. 
+   * @param strDataBody Body of the message to be POSTed to <code>strURL</code>. 
+   * @return An object containing server's response body as an InputStream and
+   *         a response code.
+   * @param CONTENT_TYPE_HEADER MIME type of the sent data.
+   * @param ACCEPT_HEADER MIME type of the data to be received.
+   * @param REQUESTED_DATA_FORMAT
+   * @throws Exception
+   */
+  public ServerResponseStream doBioCataloguePOST(String strURL, String strDataBody, String CONTENT_TYPE_HEADER,
+                                                 String ACCEPT_HEADER, String REQUESTED_DATA_FORMAT) throws Exception
+  {
+    // TODO - HACK to speed up processing append .xml / .json / .bljson to all URLs to avoid LinkedData content negotiation
+    strURL = Util.appendStringBeforeParametersOfURL(strURL, REQUESTED_DATA_FORMAT);
+    
+    // open server connection using provided URL (with no further modifications to it)
+    URL url = new URL (strURL);
+    
+    Calendar requestStartedAt = Calendar.getInstance();
+    HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
+    urlConn.setRequestMethod("POST");
+    urlConn.setDoOutput(true);
+    urlConn.setRequestProperty("User-Agent", PLUGIN_USER_AGENT);
+    urlConn.setRequestProperty("Content-Type", CONTENT_TYPE_HEADER);
+    urlConn.setRequestProperty("Accept", ACCEPT_HEADER);
+    
+    // prepare and POST XML data
+    OutputStreamWriter out = new OutputStreamWriter(urlConn.getOutputStream());
+    out.write(strDataBody);
+    out.close();
+    
+    
+    // fetch server's response
+    ServerResponseStream serverResponse = doBioCatalogueReceiveServerResponse(urlConn, strURL, false);
+    
+    if (BioCataloguePluginConstants.PERFORM_API_RESPONSE_TIME_LOGGING) {
+      logAPIOperation(requestStartedAt, "POST", serverResponse);
+    }
+    return (serverResponse);
+  }
+  
+  
+  /**
+   * Generic method to execute DELETE requests to myExperiment server.
+   * This is only to be called when a user is logged in. 
+   * 
+   * @param strURL The URL on myExperiment to direct DELETE request to.
+   * @return An object containing XML Document with server's response body and
+   *         a response code. Response body XML document might be null if there
+   *         was an error or the user wasn't authorised to perform a certain action.
+   *         Response code will always be set.
+   * @throws Exception
+   */
+  /*public ServerResponse doMyExperimentDELETE(String strURL) throws Exception
+  {
+    // open server connection using provided URL (with no modifications to it)
+    URL url = new URL(strURL);
+    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+    
+    // "tune" the connection
+    conn.setRequestMethod("DELETE");
+    conn.setRequestProperty("User-Agent", PLUGIN_USER_AGENT);
+    conn.setRequestProperty("Authorization", "Basic " + AUTH_STRING);
+    
+    // check server's response
+    return (doMyExperimentReceiveServerResponse(conn, strURL, true));
+  }*/
+  
+  
+  /**
+   * A common method for retrieving BioCatalogue server's response for both
+   * GET and POST requests.
+   * 
+   * @param conn Instance of the established URL connection to poll for server's response.
+   * @param strURL The URL on BioCatalogue with which the connection is established.
+   * @param bIsGetRequest Flag for identifying type of the request. True when the current 
+   *        connection executes GET request; false when it executes a POST / DELETE request.
+   * @return TODO
+   */
+  @SuppressWarnings("unchecked")
+  private ServerResponseStream doBioCatalogueReceiveServerResponse(HttpURLConnection conn, String strURL, boolean bIsGETRequest) throws Exception
+  {
+    int iResponseCode = conn.getResponseCode();
+    
+    switch (iResponseCode)
+    {
+      case HttpURLConnection.HTTP_OK:
+        // regular operation path - simply return the reference to the data input stream
+        return (new ServerResponseStream(iResponseCode, conn.getInputStream(), strURL));
+        
+      case HttpURLConnection.HTTP_BAD_REQUEST:
+        // this was a bad XML request - need full XML response to retrieve the error message from it;
+        // Java throws IOException if getInputStream() is used when non HTTP_OK response code was received -
+        // hence can use getErrorStream() straight away to fetch the error document
+        return (new ServerResponseStream(iResponseCode, conn.getErrorStream(), strURL));
+        
+      case HttpURLConnection.HTTP_UNAUTHORIZED:
+        // this content is not authorised for current user
+        return (new ServerResponseStream(iResponseCode, null, strURL));
+      
+      case HttpURLConnection.HTTP_NOT_FOUND:
+        // nothing was found at the provided URL
+        return (new ServerResponseStream(iResponseCode, conn.getErrorStream(), strURL));
+      
+      default:
+        // unexpected response code - raise an exception
+        throw new IOException("Received unexpected HTTP response code (" + iResponseCode + ") while " +
+            (bIsGETRequest ? "fetching data at " : "posting data to ") + strURL);
+    }
+  }
+  
+  
+  /**
+   * This method is here to make sure that *all* parsing of received input stream data
+   * from the API is parsed ("bound") into Java objects in a central place - so it's
+   * possible to measure performance of XmlBeans for various inputs.
+   * 
+   * NB! There is a serious limitation in Java's generics. Generic methods cannot
+   *     access any of the static context of the classes of type parameters, because
+   *     it wasn't designed for this. The only purpose of type parameters is compile-time
+   *     type-checking.
+   *     This means that even though all classes that could potentially be supplied as a
+   *     type-parameter would have certain static functionality, it's not possible to access
+   *     that through using the type-parameter like it's done in normal polymorhic situations.
+   *     Therefore, some switching based on the class of the type-parameter for this method is
+   *     done...
+   * 
+   * @param <T>
+   * @param classOfRequiredReturnedObject Class of the object that the caller expects to receive
+   *                                      after parsing provided server's response. For example,
+   *                                      a call to /tags.xml return the <pre>[tags]...[/tags]</pre>
+   *                                      document. <code>TagsDocument</code> should be used to access
+   *                                      its static factory and parse the input stream - the return
+   *                                      value will have type <code>Tags</code> -- <code>Tags.class</code>
+   *                                      is the required input value for this parameter in this situation then.
+   * @param serverResponse This object should contain the input stream obtained from the API in return
+   *                       to the call on some URL.
+   * @return               InputStream data parsed into the Java object of the supplied type [T].
+   * @throws Exception
+   */
+  @SuppressWarnings("unchecked")
+  private <T extends ResourceLink> T parseAPIResponseStream(Class<T> classOfRequiredReturnedObject, ServerResponseStream serverResponse) throws Exception
+  {
+    T parsedObject = null;
+    InputStream xmlInputStream = serverResponse.getResponseStream();
+    
+    // choose a factory to parse the response and perform parsing
+    Calendar parsingStartedAt = Calendar.getInstance();
+    if (classOfRequiredReturnedObject.equals(Annotations.class)) {
+      parsedObject = (T)AnnotationsDocument.Factory.parse(xmlInputStream).getAnnotations();
+    }
+    else if (classOfRequiredReturnedObject.equals(Filters.class)) {
+      parsedObject = (T)FiltersDocument.Factory.parse(xmlInputStream).getFilters();
+    }
+    else if (classOfRequiredReturnedObject.equals(RestMethods.class)) {
+      parsedObject = (T)RestMethodsDocument.Factory.parse(xmlInputStream).getRestMethods();
+    }
+    else if (classOfRequiredReturnedObject.equals(RestMethod.class)) {
+      parsedObject = (T)RestMethodDocument.Factory.parse(xmlInputStream).getRestMethod();
+    }
+    else if (classOfRequiredReturnedObject.equals(Search.class)) {
+      parsedObject = (T)SearchDocument.Factory.parse(xmlInputStream).getSearch();
+    }
+    else if (classOfRequiredReturnedObject.equals(Services.class)) {
+      parsedObject = (T)ServicesDocument.Factory.parse(xmlInputStream).getServices();
+    }
+    else if (classOfRequiredReturnedObject.equals(Service.class)) {
+      parsedObject = (T)ServiceDocument.Factory.parse(xmlInputStream).getService();
+    }
+    else if (classOfRequiredReturnedObject.equals(ServiceProviders.class)) {
+      parsedObject = (T)ServiceProvidersDocument.Factory.parse(xmlInputStream).getServiceProviders();
+    }
+    else if (classOfRequiredReturnedObject.equals(ServiceProvider.class)) {
+      parsedObject = (T)ServiceProviderDocument.Factory.parse(xmlInputStream).getServiceProvider();
+    }
+    else if (classOfRequiredReturnedObject.equals(SoapOperations.class)) {
+      parsedObject = (T)SoapOperationsDocument.Factory.parse(xmlInputStream).getSoapOperations();
+    }
+    else if (classOfRequiredReturnedObject.equals(SoapOperation.class)) {
+      parsedObject = (T)SoapOperationDocument.Factory.parse(xmlInputStream).getSoapOperation();
+    }
+    else if (classOfRequiredReturnedObject.equals(SoapService.class)) {
+      parsedObject = (T)SoapServiceDocument.Factory.parse(xmlInputStream).getSoapService();
+    }
+    else if (classOfRequiredReturnedObject.equals(SoapInput.class)) {
+      parsedObject = (T)SoapInputDocument.Factory.parse(xmlInputStream).getSoapInput();
+    }
+    else if (classOfRequiredReturnedObject.equals(SoapOutput.class)) {
+      parsedObject = (T)SoapOutputDocument.Factory.parse(xmlInputStream).getSoapOutput();
+    }
+    else if (classOfRequiredReturnedObject.equals(Tags.class)) {
+      parsedObject = (T)TagsDocument.Factory.parse(xmlInputStream).getTags();
+    }
+    else if (classOfRequiredReturnedObject.equals(Tag.class)) {
+      parsedObject = (T)TagDocument.Factory.parse(xmlInputStream).getTag();
+    }
+    else if (classOfRequiredReturnedObject.equals(Users.class)) {
+      parsedObject = (T)UsersDocument.Factory.parse(xmlInputStream).getUsers();
+    }
+    else if (classOfRequiredReturnedObject.equals(User.class)) {
+      parsedObject = (T)UserDocument.Factory.parse(xmlInputStream).getUser();
+    }
+    
+     
+    // log the operation if necessary
+    if (BioCataloguePluginConstants.PERFORM_API_XML_DATA_BINDING_TIME_LOGGING) {
+      logAPIOperation(parsingStartedAt, null, serverResponse);
+    }
+    
+    return (parsedObject);
+  }
+  
+  
+  // ************ HELPERS ************
+  
+  public static DateFormat getDateFormatter() {
+    return(BioCatalogueClient.DATE_FORMATTER);
+  }
+  
+  public static DateFormat getShortDateFormatter() {
+    return(BioCatalogueClient.SHORT_DATE_FORMATTER);
+  }
+  
+  
+  /**
+   * This is a helper to facilitate performance monitoring of the API usage.
+   * 
+   * @param opearationStartedAt Instance of Calendar initialised with the date/time value of
+   *                            when the logged operation was started.
+   * @param requestType "GET" or "POST" to indicate that this was the actual URL connection with the BioCatalogue server
+   *                    to fetch some data; <code>null</code> to indicate an xml-binding operation using XmlBeans.
+   * @param serverResponse Will be used to extract the request URL.
+   */
+  private void logAPIOperation(Calendar opearationStartedAt, String requestType, ServerResponseStream serverResponse)
+  {
+    // just in case check that the writer was initialised
+    if (pwAPILogWriter != null) {
+      pwAPILogWriter.println(API_LOGGING_TIMESTAMP_FORMATTER.format(opearationStartedAt.getTime()) + ", " +
+                             (System.currentTimeMillis() - opearationStartedAt.getTimeInMillis()) + ", " +
+                             (requestType == null ? "xml_parsing" : requestType) + ", " +
+                             serverResponse.getRequestURL());
+    }
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponse.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponse.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponse.java
new file mode 100644
index 0000000..d79235d
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponse.java
@@ -0,0 +1,40 @@
+package net.sf.taverna.biocatalogue.model.connectivity;
+
+import net.sf.taverna.biocatalogue.model.Util;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public abstract class ServerResponse
+{
+  // this code is to be used when a local failure is encountered and the
+  // server response is a blank / invalid one
+  public static int LOCAL_FAILURE_CODE = -1;
+  
+  // server response code - in theory should correspond to HTTP response codes 
+  private int iResponseCode;
+  
+  // URL that was used to make the request
+  private String requestURL;
+  
+  
+  public ServerResponse() {
+    // do nothing - empty constructor
+  }
+  
+  public ServerResponse(int responseCode, String requestURL) {
+    super();
+    this.iResponseCode = responseCode;
+    this.requestURL = Util.urlDecodeQuery(requestURL);
+  }
+  
+  
+  public int getResponseCode() {
+    return (this.iResponseCode);
+  }
+  
+  public String getRequestURL() {
+    return requestURL;
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponseStream.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponseStream.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponseStream.java
new file mode 100644
index 0000000..d4ebb56
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/ServerResponseStream.java
@@ -0,0 +1,30 @@
+package net.sf.taverna.biocatalogue.model.connectivity;
+
+import java.io.InputStream;
+
+/**
+ * This class is a custom version of ServerResponse which contains the
+ * InputStream with the the actual server response data.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class ServerResponseStream extends ServerResponse
+{
+  private InputStream responseStream;
+  
+  public ServerResponseStream(int responseCode, InputStream serverResponseStream, String requestURL)
+  {
+    super(responseCode, requestURL);
+    this.setResponseStream(serverResponseStream);
+  }
+  
+  public void setResponseStream(InputStream responseStream)
+  {
+    this.responseStream = responseStream;
+  }
+  
+  public InputStream getResponseStream()
+  {
+    return responseStream;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchEngine.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchEngine.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchEngine.java
new file mode 100644
index 0000000..0c4018a
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchEngine.java
@@ -0,0 +1,221 @@
+package net.sf.taverna.biocatalogue.model.search;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+//import javax.annotation.Resource;
+
+import org.apache.log4j.Logger;
+import org.biocatalogue.x2009.xml.rest.CollectionCoreStatistics;
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+
+import com.google.gson.Gson;
+
+import net.sf.taverna.biocatalogue.model.Pair;
+import net.sf.taverna.biocatalogue.model.Tag;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.biocatalogue.model.connectivity.BeanForPOSTToFilteredIndex;
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueAPIRequest;
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI.ResourceIndex;
+import net.sf.taverna.biocatalogue.model.search.SearchInstance.TYPE;
+import net.sf.taverna.biocatalogue.ui.search_results.SearchResultsRenderer;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
+
+import net.sf.taverna.biocatalogue.model.Resource;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class SearchEngine
+{
+  private static Logger logger = Logger.getLogger(SearchEngine.class);
+  
+  protected SearchInstance searchInstance;
+  protected final BioCatalogueClient client;
+  protected final SearchInstanceTracker activeSearchInstanceTracker;
+  protected final CountDownLatch doneSignal;
+  protected final SearchResultsRenderer renderer;
+  
+  
+  public SearchEngine(SearchInstance searchInstance, 
+                              SearchInstanceTracker activeSearchInstanceTracker,
+                              CountDownLatch doneSignal,
+                              SearchResultsRenderer renderer)
+  {
+    
+    this.searchInstance = searchInstance;
+    this.client = BioCatalogueClient.getInstance();
+    this.activeSearchInstanceTracker = activeSearchInstanceTracker;
+    this.doneSignal = doneSignal;
+    this.renderer = renderer;
+  }
+  
+  
+  
+  /**
+   * @return <code>true</code> if the thread launched by this search engine is still
+   *         the one treated as 'active' in the context of the user actions in the plugin;<br/>
+   *         <code>false</code> - otherwise.
+   */
+  protected boolean isCurrentSearch() {
+    return (activeSearchInstanceTracker.isCurrentSearchInstance(
+              this.searchInstance.getResourceTypeToSearchFor(), searchInstance));
+  }
+  
+  
+  
+  /**
+   * Primary API request is the one that is *generated* when the search is first executed --
+   * for further requests (like fetching more data) it won't be fully generated, but rather
+   * will be derived from this primary one.
+   */
+  protected BioCatalogueAPIRequest generateSearchRequest() {
+    return (generateSearchRequest(searchInstance.getSearchType()));
+  }
+  
+  protected BioCatalogueAPIRequest generateSearchRequest(TYPE searchType)
+  {
+    // construct search request to execute on BioCatalogue server
+    BioCatalogueAPIRequest.TYPE requestType = BioCatalogueAPIRequest.TYPE.GET;
+    String requestURL = null;
+    String requestData = null;
+    
+    switch (searchType) {
+      case QuerySearch:
+        requestURL = Util.appendURLParameter(searchInstance.getResourceTypeToSearchFor().getAPIResourceCollectionIndex(), "q", searchInstance.getSearchString());
+        break;
+        
+      case TagSearch:
+        List<String> tags = new ArrayList<String>();
+        for (Tag t : searchInstance.getSearchTags()) {
+          tags.add(t.getFullTagName());
+        }
+        String tagParamValue = Util.join(tags, "[", "]", ",");
+        requestURL = Util.appendURLParameter(searchInstance.getResourceTypeToSearchFor().getAPIResourceCollectionIndex(), "tag", tagParamValue);
+        break;
+      
+      case Filtering:
+        requestType = BioCatalogueAPIRequest.TYPE.POST;
+        
+        // get search URL for the 'base' search upon which the filtering is based
+        requestURL = generateSearchRequest(searchInstance.getServiceFilteringBasedOn()).getURL();
+        requestURL = Util.appendStringBeforeParametersOfURL(requestURL, BioCatalogueClient.API_FILTERED_INDEX_SUFFIX, true);
+        
+        // the base URL was prepared, now prepare filtering parameters as POST data
+        BeanForPOSTToFilteredIndex dataBean = new BeanForPOSTToFilteredIndex();
+        dataBean.filters = searchInstance.getFilteringSettings().getFilteringURLParameters();
+        Gson gson = new Gson();
+        requestData = gson.toJson(dataBean);
+        break;
+    }
+    
+    // make sure that the URL was generated
+    if (requestURL == null) {
+      logger.error("Primary search URL couldn't be generated; Search engine must have encountered " +
+          "an unexpected search instance type: " + searchInstance.getSearchType());
+      return (null);
+    }
+    
+    // Sort by name (for REST and SOAP only at the moment. Parent Web services do not have the sort by name facility yet.)
+//    if (!searchInstance.getResourceTypeToSearchFor().equals(net.sf.taverna.biocatalogue.model.Resource.TYPE.Service)){
+        requestURL = Util.appendURLParameter(requestURL, "sort_by", "name");
+        requestURL = Util.appendURLParameter(requestURL, "sort_order", "asc");
+        requestURL = Util.appendURLParameter(requestURL, "include", "ancestors");
+//    }
+    logger.info("Service Catalogue Plugin: Request URL for search: " + requestURL);
+    
+    // append some search-type-independent parameters and return the URL
+    requestURL = Util.appendAllURLParameters(requestURL, searchInstance.getResourceTypeToSearchFor().getAPIResourceCollectionIndexAdditionalParameters());
+    return (new BioCatalogueAPIRequest(requestType, requestURL, requestData));
+  }
+  
+  
+
+  public void startNewSearch()
+  {
+    // construct API request for this search
+    BioCatalogueAPIRequest searchRequest = generateSearchRequest();
+    
+    // perform the actual search operation
+    try
+    {
+      ResourceIndex resourceIndex = null;
+      if (searchRequest.getRequestType() == BioCatalogueAPIRequest.TYPE.GET) {
+        resourceIndex = client.getBioCatalogueResourceLiteIndex(searchInstance.getResourceTypeToSearchFor(), searchRequest.getURL());
+      }
+      else {
+        // can only be POST then!
+        resourceIndex = client.postBioCatalogueResourceLiteIndex(searchInstance.getResourceTypeToSearchFor(), searchRequest.getURL(), searchRequest.getData());
+      }
+      SearchResults searchResults = new SearchResults(searchInstance.getResourceTypeToSearchFor(), resourceIndex);
+      
+      // only update search results of the associated search instance if the caller thread of
+      // this operation is still active - synchronisation helps to make sure that the results
+      // will definitely only be rendered if the current search instance is definitely active:
+      // this way searches finishing in quick succession will 'flash' the results for a short
+      // while before being updated, but that will happen in the correct order
+      synchronized (activeSearchInstanceTracker) {
+        if (isCurrentSearch()) {
+          searchInstance.setSearchResults(searchResults);
+          renderer.renderInitialResults(searchInstance);
+        }
+      }
+    }
+    catch (Exception e) {
+      logger.error("ERROR: Couldn't execute initial phase of a search by query, details below:", e);
+    }
+    
+    // no matter if search was completed or interrupted by a new search, notify the caller  // FIXME - is this needed?
+    searchCompleteNotifyCaller();
+  }
+  
+  
+  @SuppressWarnings("unchecked")
+  public void fetchMoreResults(int resultPageNumber)
+  {
+    if (resultPageNumber < 1 || resultPageNumber > searchInstance.getSearchResults().getTotalResultPageNumber()) {
+      logger.error("Prevented attempt to fetch an invalid result page: " + resultPageNumber + ". Returning...");
+      return;
+    }
+    
+    // construct search URL to hit on BioCatalogue server --
+    // it is exactly as the one for the initial search, but with a page number
+    // parameter being added
+    BioCatalogueAPIRequest searchRequest = generateSearchRequest();
+    searchRequest.setURL(Util.appendURLParameter(searchRequest.getURL(), BioCatalogueClient.API_PAGE_PARAMETER, ""+resultPageNumber));
+    
+    // fetch required result page
+    try 
+    {
+      Pair<CollectionCoreStatistics,List<ResourceLink>> newResultBatch = client.getListOfItemsFromResourceCollectionIndex(
+          searchInstance.getResourceTypeToSearchFor().getXmlBeansGeneratedCollectionClass(), searchRequest);
+      
+      int firstNewEntryIndex = searchInstance.getSearchResults().getFirstItemIndexOn(resultPageNumber);
+      searchInstance.getSearchResults().addSearchResults(newResultBatch.getSecondObject(), firstNewEntryIndex);
+      
+      // only update search results of the associated search instance if the caller thread of
+      // this operation is still active
+      if (isCurrentSearch()) {
+        renderer.renderFurtherResults(searchInstance, firstNewEntryIndex, searchInstance.getResourceTypeToSearchFor().getApiResourceCountPerIndexPage());
+      }
+    }
+    catch (Exception e) {
+      // FIXME
+    }
+    
+    
+    // no matter if search was completed or interrupted by a new search, notify the caller  // FIXME - is this needed?
+    searchCompleteNotifyCaller();
+  }
+  
+  
+  /**
+   * This method is used for notifying the object that has started the
+   * search of this particular search operation being complete.
+   */
+  protected void searchCompleteNotifyCaller() {
+    this.doneSignal.countDown();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstance.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstance.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstance.java
new file mode 100644
index 0000000..fa43632
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstance.java
@@ -0,0 +1,490 @@
+package net.sf.taverna.biocatalogue.model.search;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import javax.swing.Icon;
+
+import net.sf.taverna.biocatalogue.model.Resource;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.Tag;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.biocatalogue.ui.search_results.SearchResultsRenderer;
+
+
+/**
+ * Class to hold settings for search instance. Objects of this type will
+ * be used to re-run a search instance at a later time -- or to apply
+ * filtering onto a previously executed search.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class SearchInstance implements Comparable<SearchInstance>, Serializable
+{
+  private static final long serialVersionUID = -5236966374301885370L;
+  
+  // CONSTANTS
+  public static enum TYPE
+  {
+    QuerySearch(ResourceManager.getImageIcon(ResourceManager.SEARCH_ICON)),
+    TagSearch(ResourceManager.getImageIcon(ResourceManager.TAG_ICON)),
+    Filtering(ResourceManager.getImageIcon(ResourceManager.FILTER_ICON));
+    
+    private Icon icon;
+    
+    /**
+     * @param icon Icon to represent search instances in different listings
+     *             - for example in search history.
+     */
+    TYPE(Icon icon) {
+      this.icon = icon;
+    }
+    
+    /**
+     * @return An icon that is most suitable to display search instance of this type in a UI component.
+     */
+    public Icon getIcon() {
+      return this.icon;
+    }
+  }
+  
+  
+  
+  // SEARCH SETTINGS - for either search by query or search by tag
+  private TYPE searchType;
+  private final TYPE serviceFilteringBasedOn; // service filtering may be based on {@link TYPE.QuerySearch} or {@link TYPE.TagSearch}
+  private final Resource.TYPE resourceTypeToSearchFor;
+  
+  private final String searchString;
+  private final List<Tag> searchTags;
+  
+  
+  // SERVICE FILTERING settings
+  private ServiceFilteringSettings filteringSettings;
+  
+  // SEARCH RESULTS
+  private transient SearchResults searchResults; // don't want to store search results when serialising...
+  
+  
+  
+  /**
+   * Constructs a query search instance for finding instance of a specific resource type.
+   * 
+   * @param searchString
+   * @param resourceTypeToSearchFor
+   */
+  public SearchInstance(String searchString, Resource.TYPE resourceTypeToSearchFor)
+  {
+    this.searchType = TYPE.QuerySearch;
+    this.serviceFilteringBasedOn = null;
+    
+    this.resourceTypeToSearchFor = resourceTypeToSearchFor; 
+    
+    this.searchString = searchString;
+    this.searchTags = null;
+  }
+  
+  
+  
+  /**
+   * Constructing a search instance for finding instance of a specific resource type by a single tag.
+   * 
+   * @param searchTag
+   * @param resourceTypeToSearchFor
+   */
+  public SearchInstance(Tag searchTag, Resource.TYPE resourceTypeToSearchFor)
+  {
+    this.searchType = TYPE.TagSearch;
+    this.serviceFilteringBasedOn = null;
+    
+    this.resourceTypeToSearchFor = resourceTypeToSearchFor;
+    
+    this.searchTags = Collections.singletonList(searchTag);
+    this.searchString = null;
+  }
+  
+  
+  /**
+   * Constructing a search instance for finding instance of a specific resource type by a list of tags.
+   * 
+   * @param searchTags
+   * @param resourceTypeToSearchFor
+   */
+  public SearchInstance(List<Tag> searchTags, Resource.TYPE resourceTypeToSearchFor)
+  {
+    this.searchType = TYPE.TagSearch;
+    this.serviceFilteringBasedOn = null;
+    
+    this.resourceTypeToSearchFor = resourceTypeToSearchFor;
+    
+    this.searchTags = searchTags;
+    this.searchString = null;
+  }
+  
+  
+  
+  /**
+   * Constructing service filtering search instance.
+   * 
+   * @param si SearchInstance to base the current on.
+   *           Can be either {@link TYPE#TagSearch} or {@link TYPE#QuerySearch} type of SearchInstance.
+   * @param filteringSettings Filtering settings associated with this search instance.
+   */
+  public SearchInstance(SearchInstance si, ServiceFilteringSettings filteringSettings) throws IllegalArgumentException
+  {
+    if (!si.isTagSearch() && !si.isQuerySearch()) {
+      throw new IllegalArgumentException("Cannot create Service Filtering search instance - " +
+                                         "supplied base search instance must be either QuerySearch or TagSearch");
+    }
+    
+    this.searchType = TYPE.Filtering;
+    this.serviceFilteringBasedOn = si.searchType;
+    
+    this.resourceTypeToSearchFor = si.resourceTypeToSearchFor;
+    
+    // this search instance inherits search term (i.e. search query or the tag) from the supplied search instance
+    this.searchString = si.isQuerySearch() ? si.searchString : null;
+    this.searchTags = si.isTagSearch() ? si.searchTags : null;
+    
+    // also, store the filtering settings that are to be applied to the newly
+    // created search instance
+    this.filteringSettings = filteringSettings;
+  }
+  
+  
+  /**
+   * Determines whether the two search instances are identical.
+   */
+  // TODO - fix the equals() method
+  public boolean equals(Object other)
+  {
+    if (other instanceof SearchInstance)
+    {
+      SearchInstance s = (SearchInstance)other;
+      
+      boolean bSearchTypesMatch = (this.searchType == s.getSearchType());
+      if (bSearchTypesMatch) {
+        switch (this.searchType) {
+          case QuerySearch:  bSearchTypesMatch = this.searchString.equals(s.getSearchString()); break;
+          
+          case TagSearch:    bSearchTypesMatch = this.searchTags.equals(s.getSearchTags()); break;
+          
+          case Filtering:    bSearchTypesMatch = this.serviceFilteringBasedOn == s.getServiceFilteringBasedOn();
+                             if (bSearchTypesMatch) {
+                               if (this.serviceFilteringBasedOn == TYPE.QuerySearch) {
+                                 bSearchTypesMatch = this.searchString.equals(s.getSearchString());
+                               }
+                               else {
+                                 bSearchTypesMatch = this.searchTags.equals(s.getSearchTags());
+                               }
+                             }
+                             if (bSearchTypesMatch) {
+                               if (this.filteringSettings != null) {
+                                 bSearchTypesMatch = this.filteringSettings.equals(s.getFilteringSettings());
+                               }
+                               else if (s.filteringSettings != null) {
+                                 // other isn't null, this one is - so 'false'
+                                 bSearchTypesMatch = false;
+                               }
+                               else {
+                                 // both could be null
+                                 bSearchTypesMatch = (this.filteringSettings == s.getFilteringSettings());
+                               }
+                             }
+                             break;
+          default: bSearchTypesMatch = false;
+        }
+      }
+      
+      return (bSearchTypesMatch &&
+              /* TODO re-enable this when limits are implemented -- this.iResultCountLimit == s.getResultCountLimit() && */
+              this.resourceTypeToSearchFor == s.getResourceTypeToSearchFor());
+    }
+    else
+      return (false);
+  }
+  
+  
+  public int compareTo(SearchInstance other)
+  {
+    if (this.equals(other)) return(0);
+    else
+    {
+      // this will return results in the descending order - which is
+      // fine, because the way this collection will be rendered will
+      // eventually traverse it from the rear end first; so results
+      // will be shown alphabetically
+      return (-1 * this.toString().compareTo(other.toString()));
+    }
+  }
+  
+  
+  /**
+   * See {@link SearchInstance#getDescriptionStringForSearchStatus(SearchInstance)}
+   */
+  public String getDescriptionStringForSearchStatus() {
+    return (getDescriptionStringForSearchStatus(this));
+  }
+  
+  
+  /**
+   * @param si {@link SearchInstance} for which the method is executed.
+   * @return String that can be used as a description of the provided {@link SearchInstance}
+   *         in the search status label. Returned strings may look like: <br/>
+   *         - <code>empty search string</code><br/>
+   *         - <code>query "[search_query]"</code><br/>
+   *         - <code>tag "[search_tag]"</code><br/>
+   *         - <code>tags "[tag1]", "[tag2]", "[tag3]"</code><br/>
+   *         - <code>query "[search_query]" and X filter(s)</code><br/>
+   *         - <code>tag "[search_tag]" and X filter(s)</code><br/>
+   *         - <code>tags "[tag1]", "[tag2]", "[tag3]" and X filter(s)</code><br/>
+   */
+  public static String getDescriptionStringForSearchStatus(SearchInstance si)
+  {
+    switch (si.searchType)
+    {
+      case QuerySearch: String searchQuery = si.getSearchTerm();
+                        return (searchQuery.length() == 0 ?
+                                "empty search string" :
+                                "query " + si.getSearchTerm());
+      
+      case TagSearch:   return (Util.pluraliseNoun("tag", si.getSearchTags().size()) + " " + si.getSearchTerm());
+      
+      case Filtering:   int filterNumber = si.getFilteringSettings().getNumberOfFilteringCriteria();
+      
+                        SearchInstance tempBaseSI = si.deepCopy();
+                        tempBaseSI.searchType = si.getServiceFilteringBasedOn();
+                        return getDescriptionStringForSearchStatus(tempBaseSI) + " and " + filterNumber + " " + Util.pluraliseNoun("filter", filterNumber);
+                        
+      default:          return ("unexpected type of search");
+    }
+  }
+  
+  
+  public String toString()
+  {
+    String out = "<html>";
+    
+    if (this.isQuerySearch() || this.isTagSearch()) {
+      out += (this.isTagSearch() ? "Tag s" : "S") + "earch: '" + getSearchTerm() + "' [" + this.detailsAsString() + "]";
+    }
+    else if (this.isServiceFilteringSearch()) {
+      out += "Filter:<br>" +
+             (getSearchTerm().length() > 0 ? ("- based on " + (this.isQuerySearch() ? "term" : "tag") + " '" + getSearchTerm() + "'<br>") : "") +
+             "- scope: " + detailsAsString() + "<br>" +
+             "- " + this.filteringSettings.detailsAsString();
+    }
+    
+    out += "</html>";
+    
+    return (out);
+  }
+  
+  
+  /**
+   * @return A string representation of search settings held in this object;
+   *         actual search value (string/tag) are ignored - this only affects
+   *         types to search and the number of objects to fetch.
+   */
+  public String detailsAsString()
+  {
+    // include the name of the resource type collection that is to be / was searched for
+    String str = this.getResourceTypeToSearchFor().getCollectionName();
+    
+    // add the rest to the string representation of the search instance
+    str = str /* TODO re-enable when limits are implemented -- "; limit: " + this.iResultCountLimit +*/;
+    
+    return (str);
+  }
+  
+  
+  
+  // ***** Getters for all fields *****
+  
+  /**
+   * @return Type of this search instance.
+   */
+  public TYPE getSearchType() {
+    return (this.searchType);
+  }
+  
+  
+  /**
+   * @return True if this search settings instance describes a search by tag.
+   */
+  public boolean isTagSearch() {
+    return (this.searchType == TYPE.TagSearch);
+  }
+  
+  
+  /**
+   * @return True if this search settings instance describes a search by query.
+   */
+  public boolean isQuerySearch() {
+    return (this.searchType == TYPE.QuerySearch);
+  }
+  
+  
+  /**
+   * @return True if this search settings instance describes service filtering operation.
+   */
+  public boolean isServiceFilteringSearch() {
+    return (this.searchType == TYPE.Filtering);
+  }
+  
+  
+  /**
+   * Allows to test which type of search this filtering operation is based on -- any filtering
+   * operation can be:
+   * <li>derived from an initial search by tag(s) or by free-text query</li>
+   * <li>or can be just a standalone filtering operation, where filtering criteria are
+   *     applied to all resources of the specified type, without prior search.</li> 
+   * 
+   * @return Value {@link TYPE#QuerySearch} or {@link TYPE#TagSearch} if this filtering operation has a known "parent",<br/>
+   *         <code>null</code> if this is a proper search (not a filtering!) operation, or
+   *         if this filtering operation was not based on any search. 
+   */
+  public TYPE getServiceFilteringBasedOn() {
+    return serviceFilteringBasedOn;
+  }
+  
+  
+  public Resource.TYPE getResourceTypeToSearchFor() {
+    return this.resourceTypeToSearchFor;
+  }
+  
+  
+  /**
+   * @return Search string; only valid when SearchSettings object holds data about a search by query, not a tag search.
+   */
+  public String getSearchString() {
+    return searchString;
+  }
+  
+  public List<Tag> getSearchTags() {
+    return searchTags;
+  }
+  
+  /**
+   * This method is to be used when the type of search is not checked - in
+   * case of query search the method returns the search string, otherwise
+   * the tag(s) that is to be searched.
+   * 
+   * @return The value will be returned in double quotes.
+   */
+  public String getSearchTerm()
+  {
+    if (this.searchType == TYPE.QuerySearch || this.serviceFilteringBasedOn == TYPE.QuerySearch) {
+      return (this.searchString.length() == 0 ?
+              "" :
+              "\"" + this.searchString + "\"");
+    }
+    else {
+      List<String> tagDisplayNames = new ArrayList<String>();
+      for (Tag t : this.searchTags) {
+        tagDisplayNames.add(t.getTagDisplayName());
+      }
+      return (Util.join(tagDisplayNames, "\"", "\"", ", "));
+    }
+  }
+  
+  
+  public ServiceFilteringSettings getFilteringSettings() {
+    return filteringSettings;
+  }
+  public void setFilteringSettings(ServiceFilteringSettings filteringSettings) {
+    this.filteringSettings = filteringSettings;
+  }
+  
+  
+  public SearchResults getSearchResults() {
+    return searchResults;
+  }
+  public void setSearchResults(SearchResults searchResults) {
+    this.searchResults = searchResults;
+  }
+  
+  /**
+   * @return True if search results are available;
+   *         False if no search results are available - probably search hasn't been carried out yet.
+   */
+  public boolean hasSearchResults() {
+    return (searchResults != null);
+  }
+  
+  /**
+   * @return True if this is a new search; false otherwise.
+   *         (Search is currently treated as new if there are no search results available yet.)
+   */
+  public boolean isNewSearch() {
+    return (!hasSearchResults());
+  }
+  
+  /**
+   * Removes any previous search results; after execution of
+   * this method this search instance is treated as "new search".
+   */
+  public void clearSearchResults() {
+    this.searchResults = null;
+  }
+
+  
+  
+  
+  // *** Methods that call SearchEngine in order to start new / resume result fetching for a previous search ***
+  //
+  // They are used to relay external calls to these methods to the underlying instance
+  // of SearchEngine which will perform the actual search operations for this search instance.
+  
+  /**
+   * @param activeSearchInstanceTracker Tracker of current search instances for different resource types -
+   *                                    aids in early termination of older searches.
+   * @param doneSignal Means of notifying the parentSeachThread of completing the requested search operation.
+   *                   The parent thread will block until doneSignal is activated.
+   * @param renderer   {@link SearchResultsRenderer} that will render results of this search.
+   */
+  public void startNewSearch(SearchInstanceTracker activeSearchInstanceTracker,
+                             CountDownLatch doneSignal, SearchResultsRenderer renderer)
+  {
+    new SearchEngine(this, activeSearchInstanceTracker, doneSignal, renderer).startNewSearch();
+  }
+  
+  
+  /**
+   * @param activeSearchInstanceTracker Tracker of current search instances for different resource types -
+   *                                    aids in early termination of older searches.
+   * @param doneSignal Means of notifying the parentSeachThread of completing the requested search operation.
+   *                   The parent thread will block until doneSignal is activated.
+   * @param renderer   {@link SearchResultsRenderer} that will render results of this search.
+   * @param resultPageNumber
+   */
+  public void fetchMoreResults(SearchInstanceTracker activeSearchInstanceTracker,
+                               CountDownLatch doneSignal, SearchResultsRenderer renderer, int resultPageNumber)
+  {
+    new SearchEngine(this, activeSearchInstanceTracker, doneSignal, renderer).fetchMoreResults(resultPageNumber);
+  }
+  
+  
+  
+  
+  /**
+   * Used in the plugin, for example, to transfer search results from Search tab to
+   * Filtering tab. This way both tabs will remain completely independent.
+   * 
+   * @return Deep copy of this SearchInstance object. If deep copying doesn't succeed,
+   *         <code>null</code> is returned.
+   */
+  public SearchInstance deepCopy() {
+    return (SearchInstance)Util.deepCopy(this);
+  }
+  
+  public boolean isEmptySearch() {
+	  return ((searchString == null) || searchString.isEmpty()) &&
+	  ((searchTags == null) || searchTags.isEmpty()) &&
+			  ((filteringSettings == null) || (filteringSettings.getNumberOfFilteringCriteria() == 0));
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstanceTracker.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstanceTracker.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstanceTracker.java
new file mode 100644
index 0000000..906ded1
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchInstanceTracker.java
@@ -0,0 +1,57 @@
+package net.sf.taverna.biocatalogue.model.search;
+
+import net.sf.taverna.biocatalogue.model.Resource;
+
+/**
+ * Implementations of this interface will keep track of
+ * current search instances for different resource types
+ * (under assumption that {@link SearchInstance} classes
+ * can only deal with one resource type per instance).
+ * 
+ * In the BioCatalogue plugin it is one of the UI components
+ * that needs to keep track of active search instances. This
+ * interface helps to decouple the search engine model from the
+ * UI.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public interface SearchInstanceTracker
+{
+  /**
+   * Clears all records of previous search instances.
+   */
+  public void clearPreviousSearchInstances();
+  
+  /**
+   * Registers an instance of {@link SearchInstance} class
+   * as a current one for a specific resource type.
+   * 
+   * Repeated calls to this method with the same parameter
+   * should overwrite old values.
+   * 
+   * @param searchType Resource type to associate the registered
+   *                   {@link SearchInstance} with.
+   */
+  public void registerSearchInstance(Resource.TYPE searchType, SearchInstance searchInstance);
+  
+  
+  /**
+   * Tests if provided {@link SearchInstance} is registered as the
+   * current one.
+   * 
+   * @param searchType Resource type to perform the test for.
+   * @param searchInstance {@link SearchInstance} object that is expected to be
+   *                       the current search instance for the specified resource type.
+   * @return <code>true</code> - if the provided <code>searchInstance</code> is indeed
+   *                       currently registered as the active one for the given resouce type;<br/>
+   *         <code>false</code> - otherwise.
+   */
+  public boolean isCurrentSearchInstance(Resource.TYPE searchType, SearchInstance searchInstance);
+  
+  
+  /**
+   * @param searchType
+   * @return Currently active {@link SearchInstance} object for the specified resource type.
+   */
+  public SearchInstance getCurrentSearchInstance(Resource.TYPE searchType);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchOptions.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchOptions.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchOptions.java
new file mode 100644
index 0000000..cf5e7db
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchOptions.java
@@ -0,0 +1,70 @@
+package net.sf.taverna.biocatalogue.model.search;
+
+import java.util.Collections;
+import java.util.List;
+
+import net.sf.taverna.biocatalogue.model.Tag;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.ui.SearchOptionsPanel;
+
+/**
+ * Instances of this class can store the state of the
+ * {@link SearchOptionsPanel} / {@link TagSelectionDialog} in
+ * order to help instantiate {@link SearchInstance} objects.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class SearchOptions
+{
+  private SearchInstance preconfiguredSearchInstance;
+  private SearchInstance.TYPE searchType;
+  private String searchString;
+  private List<Tag> searchTags;
+  private List<TYPE> resourceTypesToSearchFor;
+  
+  public SearchOptions(String searchString, List<TYPE> searchTypes) {
+    this.preconfiguredSearchInstance = null;
+    this.searchType = SearchInstance.TYPE.QuerySearch;
+    this.searchString = searchString;
+    this.searchTags = null;
+    this.resourceTypesToSearchFor = searchTypes;
+  }
+  
+  public SearchOptions(List<Tag> searchTags, List<TYPE> searchTypes) {
+    this.preconfiguredSearchInstance = null;
+    this.searchType = SearchInstance.TYPE.TagSearch;
+    this.searchString = null;
+    this.searchTags = searchTags;
+    this.resourceTypesToSearchFor = searchTypes;
+  }
+  
+  public SearchOptions(SearchInstance preconfiguredSearchInstance) {
+    this.preconfiguredSearchInstance = preconfiguredSearchInstance;
+    this.searchType = preconfiguredSearchInstance.getSearchType();
+    this.searchString = preconfiguredSearchInstance.getSearchString();
+    this.searchTags = preconfiguredSearchInstance.getSearchTags();
+    this.resourceTypesToSearchFor = Collections.singletonList(preconfiguredSearchInstance.getResourceTypeToSearchFor());
+  }
+  
+  
+  public SearchInstance getPreconfiguredSearchInstance() {
+    return preconfiguredSearchInstance;
+  }
+  
+  public SearchInstance.TYPE getSearchType() {
+    return searchType;
+  }
+  
+  public String getSearchString() {
+    return searchString;
+  }
+  
+  public List<Tag> getSearchTags() {
+    return searchTags;
+  }
+  
+  public List<TYPE> getResourceTypesToSearchFor() {
+    return resourceTypesToSearchFor;
+  }
+  
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchResults.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchResults.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchResults.java
new file mode 100644
index 0000000..d18076c
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/search/SearchResults.java
@@ -0,0 +1,214 @@
+package net.sf.taverna.biocatalogue.model.search;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.taverna.biocatalogue.model.LoadingResource;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI;
+import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI.ResourceIndex;
+import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI.ResourceLinkWithName;
+
+import org.apache.log4j.Logger;
+
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+
+
+/**
+ * Generic class for any kinds of search results.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class SearchResults implements Serializable
+{
+  private static final long serialVersionUID = 6994685875323246165L;
+  
+  private transient Logger logger; // don't want to serialise the logger...
+  
+  private final TYPE typeOfResourcesInTheResultSet;
+  private final int totalResultCount;
+  
+  // Data store for found items
+  protected ArrayList<ResourceLink> foundItems;
+  private int fullyFetchedItemCount;
+
+  
+  
+  public SearchResults(TYPE typeOfResourcesInTheResultSet, BeansForJSONLiteAPI.ResourceIndex resourceIndex)
+  {
+    this.typeOfResourcesInTheResultSet = typeOfResourcesInTheResultSet;
+    this.totalResultCount = resourceIndex.getResources().length;
+    this.fullyFetchedItemCount = 0;
+    
+    this.logger = Logger.getLogger(this.getClass());
+    
+    initialiseSearchResultCollection(resourceIndex);
+  }
+  
+  
+  /**
+   * The collection of results is initialised to cater for the expected number of
+   * values - placeholder with just a name and URL for each of the expected result entries is stored.
+   * 
+   * @param resourceIndex
+   */
+  protected void initialiseSearchResultCollection(ResourceIndex resourceIndex)
+  {
+    foundItems = new ArrayList<ResourceLink>();
+    foundItems.ensureCapacity(getTotalMatchingItemCount());
+    
+    ResourceLinkWithName resourceLink = null;
+    for (int i = 0; i < getTotalMatchingItemCount(); i++) {
+      resourceLink = resourceIndex.getResources()[i];
+      this.foundItems.add(new LoadingResource(resourceLink.getURL(), resourceLink.getName()));
+    }
+  }
+  
+  
+  public synchronized void addSearchResults(List<ResourceLink> searchResultsData, int positionToStartAddingResults)
+  {
+    // only update a specific portion of results
+    for (int i = 0; i < searchResultsData.size(); i++) {
+      this.foundItems.set(i + positionToStartAddingResults, searchResultsData.get(i));
+    }
+    
+    fullyFetchedItemCount += searchResultsData.size();
+  }
+  
+  
+  public TYPE getTypeOfResourcesInTheResultSet() {
+    return typeOfResourcesInTheResultSet;
+  }
+  
+  
+  /**
+   * @return List of resources that have matched the search query
+   *         and/or specified filtering criteria. 
+   */
+  public List<ResourceLink> getFoundItems() {
+    return (this.foundItems);
+  }
+  
+  
+  /**
+   * @return Number of resources that have matched the search query
+   *         (and/or specified filtering criteria) that have already been
+   *         fetched.
+   */
+  public int getFetchedItemCount() {
+    return (this.fullyFetchedItemCount);
+  }
+  
+  
+  /**
+   * @return Total number of resources that have matched the search query
+   *         (and/or specified filtering criteria) - most of these will
+   *         likely not be fetched yet.
+   */
+  public int getTotalMatchingItemCount() {
+    return (this.totalResultCount);
+  }
+  
+  
+  /**
+   * @return Total number of pages in the current result set.
+   */
+  public int getTotalResultPageNumber() {
+    int numberOfResourcesPerPageForThisResourceType = this.getTypeOfResourcesInTheResultSet().getApiResourceCountPerIndexPage();
+    return (int)(Math.ceil((double)getTotalMatchingItemCount() / numberOfResourcesPerPageForThisResourceType));
+  }
+  
+  
+  /**
+   * List of matching items will be partial and populated sequentially
+   * based on user actions. Therefore, this method helps to check
+   * which list entries are still not populated.
+   * 
+   * @param startIndex Beginning of the range to check.
+   * @param endIndex End of the range to check.
+   * @return Zero-based index of the first entry in the list of
+   *         matching resources that hasn't been fetched yet.
+   *         Will return <code>-1</code> if the provided range
+   *         parameters are incorrect or if all items in the
+   *         specified range are already available.
+   */
+  public int getFirstMatchingItemIndexNotYetFetched(int startIndex, int endIndex)
+  {
+    // check the specified range is correct
+    if (startIndex < 0 || endIndex > getTotalMatchingItemCount() - 1) {
+      return (-1);
+    }
+    
+    // go through the search results in the specified range
+    // in an attempt to find an item that hasn't been fetched
+    // just yet
+    for (int i = startIndex; i <= endIndex; i++) {
+      ResourceLink item = this.foundItems.get(i);
+      if (item != null && item instanceof LoadingResource && !((LoadingResource)item).isLoading()) {
+        return (i);
+      }
+    }
+    
+    // apparently, all items in the provided range are fetched
+    return (-1);
+  }
+  
+  
+  
+  /**
+   * @param matchingItemIndex Index of the matching item from search results.
+   * @return Index (starting from "1") of page in the search results, where
+   *         the matching item with a specified index is located. If the
+   *         <code>matchingItemIndex</code> is wrong, <code>-1</code> is returned.
+   */
+  public int getMatchingItemPageNumberFor(int matchingItemIndex)
+  {
+    // check the specified index is correct
+    if (matchingItemIndex < 0 || matchingItemIndex > getTotalMatchingItemCount() - 1) {
+      return (-1);
+    }
+    
+    int resultsPerPageForThisType = this.getTypeOfResourcesInTheResultSet().getApiResourceCountPerIndexPage();
+    return (matchingItemIndex / resultsPerPageForThisType + 1);
+  }
+  
+  
+  /**
+   * @param resultPageNumber Number of the page, for which the calculations are to be done.
+   * @return Index of the first result entry on the specified result page. If <code>resultPageNumber</code>
+   *         is less than <code>1</code> or greater than the total number of pages in the result set,
+   *         a value of <code>-1</code> will be returned.
+   */
+  public int getFirstItemIndexOn(int resultPageNumber)
+  {
+    // page number must be in a valid range - starting with 1..onwards
+    if (resultPageNumber < 1 || resultPageNumber > getTotalResultPageNumber()) {
+      return (-1);
+    }
+    
+    int numberOfResourcesPerPageForThisResourceType = this.getTypeOfResourcesInTheResultSet().getApiResourceCountPerIndexPage();
+    return ((resultPageNumber - 1) * numberOfResourcesPerPageForThisResourceType);
+  }
+  
+  
+  
+  /**
+   * Mainly for testing - outputs number of search results per item type.
+   */
+  public String toString()
+  {
+    // FIXME
+    
+//    StringBuilder out = new StringBuilder("Breakdown of item counts by type:\n");
+//    for (Map.Entry<Integer,String> itemTypeNamePair : Resource.ALL_SUPPORTED_RESOURCE_COLLECTION_NAMES.entrySet()) {
+//      out.append(itemTypeNamePair.getValue() + ": " +getFetchedItemCount(itemTypeNamePair.getKey()) +
+//                 "/" + getTotalItemCount(itemTypeNamePair.getKey()) + "\n");
+//    }
+//    
+//    return (out.toString());
+    
+    return ("search results... not implemented!!!");
+  }
+  
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/HistoryBrowserTabContentPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/HistoryBrowserTabContentPanel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/HistoryBrowserTabContentPanel.java
new file mode 100644
index 0000000..3dfd2db
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/HistoryBrowserTabContentPanel.java
@@ -0,0 +1,541 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.border.Border;
+
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Base64;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Tag;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Util;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.SearchEngine.QuerySearchInstance;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs, Emmanuel Tagarira
+ */
+public class HistoryBrowserTabContentPanel extends JPanel implements ActionListener {
+  // CONSTANTS
+  public static final int DOWNLOADED_ITEMS_HISTORY_LENGTH = 50;
+  public static final int OPENED_ITEMS_HISTORY_LENGTH = 50;
+  public static final int UPLOADED_ITEMS_HISTORY_LENGTH = 50;
+  public static final int COMMENTED_ON_ITEMS_HISTORY_LENGTH = 50;
+
+  public static final int PREVIEWED_ITEMS_HISTORY = 0;
+  public static final int DOWNLOADED_ITEMS_HISTORY = 1;
+  public static final int OPENED_ITEMS_HISTORY = 2;
+  public static final int UPLOADED_ITEMS_HISTORY = 4;
+  public static final int COMMENTED_ON_ITEMS_HISTORY = 3;
+
+  private final MainComponent pluginMainComponent;
+  private final MyExperimentClient myExperimentClient;
+  private final Logger logger;
+
+  // STORAGE
+  private ArrayList<Resource> lDownloadedItems;
+  private ArrayList<Resource> lOpenedItems;
+  private ArrayList<Resource> lUploadedItems;
+  private ArrayList<Resource> lCommentedOnItems;
+
+  // COMPONENTS
+  private JPanel jpPreviewHistory;
+  private JPanel jpSearchHistory;
+  private JPanel jpTagSearchHistory;
+  private JPanel jpDownloadedItemsHistory;
+  private JPanel jpOpenedItemsHistory;
+  private JPanel jpUploadedItemsHistory;
+  private JPanel jpCommentedOnHistory;
+  private JSplitPane spMain;
+  private JClickableLabel jclPreviewHistory;
+  private JClickableLabel jclSearchHistory;
+  private JClickableLabel jclTagSearchHistory;
+  private JClickableLabel jclDownloadedItemsHistory;
+  private JClickableLabel jclOpenedItemsHistory;
+  private JClickableLabel jclUploadedItemsHistory;
+  private JClickableLabel jclCommentedOnHistory;
+  private JPanel jpPreviewHistoryBox;
+  private JPanel jpSearchHistoryBox;
+  private JPanel jpTagSearchHistoryBox;
+  private JPanel jpDownloadedItemsHistoryBox;
+  private JPanel jpOpenedItemsHistoryBox;
+  private JPanel jpUploadedItemsHistoryBox;
+  private JPanel jpCommentedOnHistoryBox;
+
+  @SuppressWarnings("unchecked")
+  public HistoryBrowserTabContentPanel(MainComponent component, MyExperimentClient client, Logger logger) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	// initialise downloaded items history
+	String strDownloadedItemsHistory = (String) myExperimentClient.getSettings().get(MyExperimentClient.INI_DOWNLOADED_ITEMS_HISTORY);
+	if (strDownloadedItemsHistory != null) {
+	  Object oDownloadedItemsHistory = Base64.decodeToObject(strDownloadedItemsHistory);
+	  this.lDownloadedItems = (ArrayList<Resource>) oDownloadedItemsHistory;
+	} else {
+	  this.lDownloadedItems = new ArrayList<Resource>();
+	}
+
+	// initialise opened items history
+	String strOpenedItemsHistory = (String) myExperimentClient.getSettings().get(MyExperimentClient.INI_OPENED_ITEMS_HISTORY);
+	if (strOpenedItemsHistory != null) {
+	  Object oOpenedItemsHistory = Base64.decodeToObject(strOpenedItemsHistory);
+	  this.lOpenedItems = (ArrayList<Resource>) oOpenedItemsHistory;
+	} else {
+	  this.lOpenedItems = new ArrayList<Resource>();
+	}
+
+	// initialise uploaded items history
+	String strUploadedItemsHistory = (String) myExperimentClient.getSettings().get(MyExperimentClient.INI_UPLOADED_ITEMS_HISTORY);
+	if (strUploadedItemsHistory != null) {
+	  Object oUploadedItemsHistory = Base64.decodeToObject(strUploadedItemsHistory);
+	  this.lUploadedItems = (ArrayList<Resource>) oUploadedItemsHistory;
+	} else {
+	  this.lUploadedItems = new ArrayList<Resource>();
+	}
+
+	// initialise history of the items that were commented on
+	String strCommentedItemsHistory = (String) myExperimentClient.getSettings().get(MyExperimentClient.INI_COMMENTED_ITEMS_HISTORY);
+	if (strCommentedItemsHistory != null) {
+	  Object oCommentedItemsHistory = Base64.decodeToObject(strCommentedItemsHistory);
+	  this.lCommentedOnItems = (ArrayList<Resource>) oCommentedItemsHistory;
+	} else {
+	  this.lCommentedOnItems = new ArrayList<Resource>();
+	}
+
+	this.initialiseUI();
+	this.refreshAllData();
+  }
+
+  private void confirmHistoryDelete(final int id, String strBoxTitle) {
+	if (JOptionPane.showConfirmDialog(null, "This will the "
+		+ strBoxTitle.toLowerCase() + " list.\nDo you want to proceed?", "myExperiment Plugin - Confirmation Required", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
+	  switch (id) {
+		case 1:
+		  pluginMainComponent.getPreviewBrowser().clearPreviewHistory();
+		  break;
+		case 2:
+		  pluginMainComponent.getSearchTab().getSearchHistory().clear();
+		  pluginMainComponent.getSearchTab().updateSearchHistory();
+		  break;
+		case 3:
+		  pluginMainComponent.getTagBrowserTab().getTagSearchHistory().clear();
+		  break;
+		case 4:
+		  clearDownloadedItemsHistory();
+		  break;
+		case 5:
+		  clearOpenedItemsHistory();
+		  break;
+		case 6:
+		  clearCommentedOnItemsHistory();
+		  break;
+		case 7:
+		  clearUploadedItemsHistory();
+		  break;
+	  }
+	  refreshAllData();
+	}
+  }
+
+  private JPanel addSpecifiedPanel(final int id, final String strBoxTitle, JPanel jPanel) {
+	JPanel jpTemp = new JPanel();
+	jpTemp.setLayout(new BorderLayout());
+	jpTemp.add(generateContentBox(strBoxTitle, jPanel), BorderLayout.CENTER);
+	JButton bClear = new JButton("Clear " + strBoxTitle, WorkbenchIcons.deleteIcon);
+	bClear.addActionListener(new ActionListener() {
+	  public void actionPerformed(ActionEvent e) {
+		confirmHistoryDelete(id, strBoxTitle);
+	  }
+	});
+
+	jpTemp.add(bClear, BorderLayout.SOUTH);
+	jpTemp.setMinimumSize(new Dimension(500, 0));
+	return jpTemp;
+  }
+
+  private void initialiseUI() {
+	// create helper text
+	ShadedLabel lHelper = new ShadedLabel("All history sections are local to myExperiment plugin usage."
+		+ " Detailed history of your actions on myExperiment is available in your profile on myExperiment.", ShadedLabel.BLUE);
+
+	// create all individual content holder panels
+	this.jpPreviewHistory = new JPanel();
+	this.jpTagSearchHistory = new JPanel();
+	this.jpSearchHistory = new JPanel();
+	this.jpDownloadedItemsHistory = new JPanel();
+	this.jpOpenedItemsHistory = new JPanel();
+	this.jpUploadedItemsHistory = new JPanel();
+	this.jpCommentedOnHistory = new JPanel();
+
+	// create sidebar
+	JPanel jpSidebar = new JPanel();
+	jpSidebar.setLayout(new BoxLayout(jpSidebar, BoxLayout.Y_AXIS));
+	Border border = BorderFactory.createEmptyBorder(5, 3, 10, 3);
+	jclPreviewHistory = new JClickableLabel("Previewed Items", "preview_history", this, WorkbenchIcons.editIcon, SwingConstants.LEFT, "tooltip");
+	jclPreviewHistory.setBorder(border);
+	jpSidebar.add(jclPreviewHistory);
+
+	jclSearchHistory = new JClickableLabel("Search History", "search_history", this, WorkbenchIcons.editIcon, SwingConstants.LEFT, "tooltip");
+	jclSearchHistory.setBorder(border);
+	jpSidebar.add(jclSearchHistory);
+
+	jclTagSearchHistory = new JClickableLabel("Tag Searches Made", "tag_history", this, WorkbenchIcons.editIcon, SwingConstants.LEFT, "tooltip");
+	jclTagSearchHistory.setBorder(border);
+	jpSidebar.add(jclTagSearchHistory);
+
+	jclDownloadedItemsHistory = new JClickableLabel("Downloaded Items", "downloads_history", this, WorkbenchIcons.editIcon, SwingConstants.LEFT, "tooltip");
+	jclDownloadedItemsHistory.setBorder(border);
+	jpSidebar.add(jclDownloadedItemsHistory);
+
+	jclOpenedItemsHistory = new JClickableLabel("Opened Items", "opened_history", this, WorkbenchIcons.editIcon, SwingConstants.LEFT, "tooltip");
+	jclOpenedItemsHistory.setBorder(border);
+	jpSidebar.add(jclOpenedItemsHistory);
+
+	jclUploadedItemsHistory = new JClickableLabel("Updated Items", "uploaded_history", this, WorkbenchIcons.editIcon, SwingConstants.LEFT, "tooltip");
+	jclUploadedItemsHistory.setBorder(border);
+	jpSidebar.add(jclUploadedItemsHistory);
+
+	jclCommentedOnHistory = new JClickableLabel("Items Commented On", "comments_history", this, WorkbenchIcons.editIcon, SwingConstants.LEFT, "tooltip");
+	jclCommentedOnHistory.setBorder(border);
+	jpSidebar.add(jclCommentedOnHistory);
+
+	JPanel jpSidebarContainer = new JPanel();
+	jpSidebarContainer.add(jpSidebar, BorderLayout.NORTH);
+	JScrollPane spSidebar = new JScrollPane(jpSidebarContainer);
+	spSidebar.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+	spSidebar.setMinimumSize(new Dimension(245, 0));
+	spSidebar.setMaximumSize(new Dimension(300, 0));
+
+	// create standard boxes for each content holder panels
+	// only one of these will hold the right hand side of spMain at any given time
+	// arg 1: is the ID, which will be used by confirmHistoryDelete() to decide
+	// how to delete the history item
+	jpPreviewHistoryBox = addSpecifiedPanel(1, "Items you have previewed", jpPreviewHistory);
+	jpSearchHistoryBox = addSpecifiedPanel(2, "Terms you have searched for", jpSearchHistory);
+	jpTagSearchHistoryBox = addSpecifiedPanel(3, "Tags searches you have made", jpTagSearchHistory);
+	jpDownloadedItemsHistoryBox = addSpecifiedPanel(4, "Items you have downloaded", jpDownloadedItemsHistory);
+	jpOpenedItemsHistoryBox = addSpecifiedPanel(5, "Workflows you have opened in Taverna", jpOpenedItemsHistory);
+	jpCommentedOnHistoryBox = addSpecifiedPanel(6, "Items you have commented on in myExperiment", jpCommentedOnHistory);
+	jpUploadedItemsHistoryBox = addSpecifiedPanel(7, "Items you have updated on myExperiment", jpUploadedItemsHistory);
+
+	// put everything together
+	spMain = new JSplitPane();
+	spMain.setLeftComponent(spSidebar);
+	spMain.setRightComponent(jpPreviewHistoryBox);
+
+	spMain.setOneTouchExpandable(true);
+	spMain.setDividerLocation(247);
+	spMain.setDoubleBuffered(true);
+
+	// spMyStuff will be the only component in the Panel
+	this.setLayout(new BorderLayout());
+	this.add(spMain);
+	this.add(lHelper, BorderLayout.NORTH);
+
+  }
+
+  public ArrayList<Resource> getDownloadedItemsHistoryList() {
+	return (this.lDownloadedItems);
+  }
+
+  public void clearDownloadedItemsHistory() {
+	this.lDownloadedItems.clear();
+  }
+
+  public ArrayList<Resource> getOpenedItemsHistoryList() {
+	return (this.lOpenedItems);
+  }
+
+  public ArrayList<Resource> getUploadedItemsHistoryList() {
+	return (this.lUploadedItems);
+  }
+
+  public void clearOpenedItemsHistory() {
+	this.lOpenedItems.clear();
+  }
+
+  public void clearUploadedItemsHistory() {
+	this.lUploadedItems.clear();
+  }
+
+  public ArrayList<Resource> getCommentedOnItemsHistoryList() {
+	return (this.lCommentedOnItems);
+  }
+
+  public void clearCommentedOnItemsHistory() {
+	this.lCommentedOnItems.clear();
+  }
+
+  /**
+   * Used to refresh all boxes at a time (for example at launch time).
+   */
+  private void refreshAllData() {
+	this.refreshHistoryBox(PREVIEWED_ITEMS_HISTORY);
+	this.refreshHistoryBox(DOWNLOADED_ITEMS_HISTORY);
+	this.refreshHistoryBox(OPENED_ITEMS_HISTORY);
+	this.refreshHistoryBox(UPLOADED_ITEMS_HISTORY);
+	this.refreshHistoryBox(COMMENTED_ON_ITEMS_HISTORY);
+	this.refreshSearchHistory();
+	this.refreshTagSearchHistory();
+  }
+
+  /**
+   * This helper can be called externally to refresh the following history
+   * boxes: previewed items history, downloaded items history, opened items
+   * history and the history of items that were commented on.
+   * 
+   * Is used inside ResourcePreviewBrowser and MainComponent every time a
+   * relevant action occurs. Also useful, when an option to 'clear preview
+   * history' is used in the Preferences window for. a particular history type.
+   */
+  public void refreshHistoryBox(int historyType) {
+	switch (historyType) {
+	  case PREVIEWED_ITEMS_HISTORY:
+		this.jpPreviewHistory.removeAll();
+		populateHistoryBox(this.pluginMainComponent.getPreviewBrowser().getPreviewHistory(), this.jpPreviewHistory, "No items were previewed yet");
+		break;
+	  case DOWNLOADED_ITEMS_HISTORY:
+		this.jpDownloadedItemsHistory.removeAll();
+		populateHistoryBox(this.lDownloadedItems, this.jpDownloadedItemsHistory, "No items were downloaded");
+		break;
+	  case OPENED_ITEMS_HISTORY:
+		this.jpOpenedItemsHistory.removeAll();
+		populateHistoryBox(this.lOpenedItems, this.jpOpenedItemsHistory, "No items were opened");
+		break;
+	  case UPLOADED_ITEMS_HISTORY:
+		this.jpUploadedItemsHistory.removeAll();
+		populateHistoryBox(this.lUploadedItems, this.jpUploadedItemsHistory, "No items were updated");
+		break;
+	  case COMMENTED_ON_ITEMS_HISTORY:
+		this.jpCommentedOnHistory.removeAll();
+		populateHistoryBox(this.lCommentedOnItems, this.jpCommentedOnHistory, "You didn't comment on any items");
+		break;
+	}
+  }
+
+  /**
+   * Retrieves history data from a relevant list and populates the specified
+   * panel with it. All listed items will be resources that can be opened by
+   * Preview Browser.
+   */
+  private void populateHistoryBox(List<Resource> lHistory, JPanel jpPanelToPopulate, String strLabelIfNoItems) {
+	if (lHistory.size() > 0) {
+	  for (int i = lHistory.size() - 1; i >= 0; i--) {
+		Resource r = lHistory.get(i);
+		if (r != null) {
+		  JClickableLabel lResource = Util.generateClickableLabelFor(r, this.pluginMainComponent.getPreviewBrowser());
+		  jpPanelToPopulate.add(lResource);
+		}
+	  }
+	} else {
+	  jpPanelToPopulate.add(Util.generateNoneTextLabel(strLabelIfNoItems));
+	}
+
+	// make sure that the component is updated after population
+	jpPanelToPopulate.revalidate();
+	jpPanelToPopulate.repaint();
+  }
+
+  /**
+   * This helper can be called externally to refresh the search history. Is used
+   * inside SearchTabContentPanel every time a new item is added to search
+   * history.
+   */
+  public void refreshSearchHistory() {
+	this.jpSearchHistory.removeAll();
+	populateSearchHistory();
+  }
+
+  /**
+   * Retrieves search history data from SearchTabContentPanel and populates the
+   * relevant panel.
+   */
+  private void populateSearchHistory() {
+	List<QuerySearchInstance> lSearchHistory = this.pluginMainComponent.getSearchTab().getSearchHistory();
+
+	// prepare layout
+	this.jpSearchHistory.setLayout(new GridBagLayout());
+	GridBagConstraints c = new GridBagConstraints();
+	c.anchor = GridBagConstraints.NORTHWEST;
+
+	if (lSearchHistory.size() > 0) {
+	  for (int i = lSearchHistory.size() - 1; i >= 0; i--) {
+		QuerySearchInstance qsiCurrent = lSearchHistory.get(i);
+		JClickableLabel jclCurrentEntryLabel = new JClickableLabel(qsiCurrent.getSearchQuery(), SearchTabContentPanel.SEARCH_FROM_HISTORY
+			+ ":" + i, this, WorkbenchIcons.findIcon, SwingUtilities.LEFT, qsiCurrent.toString());
+		JLabel jlCurrentEntrySettings = new JLabel(qsiCurrent.detailsAsString());
+		jlCurrentEntrySettings.setBorder(BorderFactory.createEmptyBorder(3, 5, 0, 0));
+
+		JPanel jpCurrentSearchHistoryEntry = new JPanel();
+		jpCurrentSearchHistoryEntry.setLayout(new GridBagLayout());
+		c.gridx = 0;
+		c.gridy = 0;
+		c.weightx = 0;
+		jpCurrentSearchHistoryEntry.add(jclCurrentEntryLabel, c);
+		c.gridx = 1;
+		c.gridy = 0;
+		c.weightx = 1.0;
+		jpCurrentSearchHistoryEntry.add(jlCurrentEntrySettings, c);
+
+		c.gridy = lSearchHistory.size() - 1 - i;
+		if (i == 0)
+		  c.weighty = 1.0;
+		this.jpSearchHistory.add(jpCurrentSearchHistoryEntry, c);
+	  }
+	} else {
+	  c.weightx = 1.0;
+	  c.weighty = 1.0;
+	  this.jpSearchHistory.add(Util.generateNoneTextLabel(SearchResultsPanel.NO_SEARCHES_STATUS), c);
+	}
+
+	// make sure that the component is updated after population
+	this.jpSearchHistory.revalidate();
+	this.jpSearchHistory.repaint();
+  }
+
+  /**
+   * This helper can be called externally to refresh the tag search history. Is
+   * used inside TagBrowserTabContentPanel every time a new tag is searched for.
+   */
+  public void refreshTagSearchHistory() {
+	this.jpTagSearchHistory.removeAll();
+	populateTagSearchHistory();
+  }
+
+  /**
+   * Retrieves tag search history data from Tag Browser and populates the
+   * relevant panel.
+   */
+  private void populateTagSearchHistory() {
+	List<Tag> lTagSearchHistory = this.pluginMainComponent.getTagBrowserTab().getTagSearchHistory();
+
+	if (lTagSearchHistory.size() > 0) {
+	  for (int i = lTagSearchHistory.size() - 1; i >= 0; i--) {
+		Tag t = lTagSearchHistory.get(i);
+		JClickableLabel lTag = new JClickableLabel(t.getTagName(), "tag:"
+			+ t.getTagName(), this, new ImageIcon(MyExperimentPerspective.getLocalIconURL(Resource.TAG)), // HACK: after deserialization t.getItemType() return "Unknown" type
+		SwingConstants.LEFT, "Tag: " + t.getTagName());
+		this.jpTagSearchHistory.add(lTag);
+	  }
+	} else {
+	  this.jpTagSearchHistory.add(Util.generateNoneTextLabel("No searches by tags have been made yet"));
+	}
+
+	// make sure that the component is updated after population
+	this.jpTagSearchHistory.revalidate();
+	this.jpTagSearchHistory.repaint();
+  }
+
+  /**
+   * @param strBoxTitle
+   *          Title of the content box.
+   * @param jpContentPanel
+   *          JPanel which will be populated with history listing.
+   * @return Prepared JPanel with a border, title and jpContentPanel wrapped
+   *         into a scroll pane.
+   */
+  private static JPanel generateContentBox(String strBoxTitle, JPanel jpContentPanel) {
+	// set layout for the content panel
+	jpContentPanel.setLayout(new BoxLayout(jpContentPanel, BoxLayout.Y_AXIS));
+
+	// wrap content panel into a standard scroll pane
+	JScrollPane spContent = new JScrollPane(jpContentPanel);
+	spContent.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
+	spContent.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+	spContent.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+	// create the actual box stub with a border which will contain the scroll pane
+	JPanel jpBox = new JPanel();
+	jpBox.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2), BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " "
+		+ strBoxTitle + " ")));
+
+	jpBox.setLayout(new GridBagLayout());
+	GridBagConstraints c = new GridBagConstraints();
+	c.anchor = GridBagConstraints.NORTHWEST;
+	c.fill = GridBagConstraints.BOTH;
+	c.weightx = 1.0;
+	c.weighty = 1.0;
+	jpBox.add(spContent, c);
+
+	return (jpBox);
+  }
+
+  // *** Callback for ActionListener interface ***
+  public void actionPerformed(ActionEvent e) {
+	if (e.getSource() instanceof JClickableLabel) {
+	  if (e.getActionCommand().startsWith(SearchTabContentPanel.SEARCH_FROM_HISTORY)) {
+		// open search tab and start the chosen search
+		this.pluginMainComponent.getSearchTab().actionPerformed(e);
+		this.pluginMainComponent.getMainTabs().setSelectedComponent(this.pluginMainComponent.getSearchTab());
+	  } else if (e.getActionCommand().startsWith("tag:")) {
+		// open tag browser tab and start the chosen tag search
+		this.pluginMainComponent.getTagBrowserTab().actionPerformed(e);
+		this.pluginMainComponent.getMainTabs().setSelectedComponent(this.pluginMainComponent.getTagBrowserTab());
+	  } else if (e.getActionCommand().contains("history")) {
+		if (e.getActionCommand() == "preview_history")
+		  spMain.setRightComponent(jpPreviewHistoryBox);
+		else if (e.getActionCommand() == "search_history")
+		  spMain.setRightComponent(jpSearchHistoryBox);
+		else if (e.getActionCommand() == "tag_history")
+		  spMain.setRightComponent(jpTagSearchHistoryBox);
+		else if (e.getActionCommand() == "downloads_history")
+		  spMain.setRightComponent(jpDownloadedItemsHistoryBox);
+		else if (e.getActionCommand() == "opened_history")
+		  spMain.setRightComponent(jpOpenedItemsHistoryBox);
+		else if (e.getActionCommand() == "uploaded_history")
+		  spMain.setRightComponent(jpUploadedItemsHistoryBox);
+		else if (e.getActionCommand() == "comments_history")
+		  spMain.setRightComponent(jpCommentedOnHistoryBox);
+	  }
+	}
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/JClickableLabel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/JClickableLabel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/JClickableLabel.java
new file mode 100644
index 0000000..53f901f
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/JClickableLabel.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.EventListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.Icon;
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Util;
+
+/**
+ * @author Sergejs Aleksejevs, Jiten Bhagat
+ */
+
+public class JClickableLabel extends JLabel implements MouseListener
+{
+  // This will hold the data which is relevant to processing the 'click' event on this label
+  private String strData;
+  
+  // This will hold a reference to ResourcePreviewBrowser instance that is supposed to process the clicks
+  // on JClickableLabels
+  private ActionListener clickHandler;
+  
+  
+  public JClickableLabel(String strLabel, String strDataForAction, EventListener eventHandler)
+  {
+    this(strLabel, strDataForAction, eventHandler, null);
+  }
+  
+  public JClickableLabel(String strLabel, String strDataForAction, EventListener eventHandler, Icon icon)
+  {
+    this(strLabel, strDataForAction, eventHandler, icon, SwingUtilities.LEFT);
+  }
+  
+  public JClickableLabel(String strLabel, String strDataForAction, EventListener eventHandler, Icon icon, int horizontalAlignment)
+  {
+    this(strLabel, strDataForAction, eventHandler, icon, horizontalAlignment, null);
+  }
+  
+  /**
+   * 
+   * @param strLabel Textual label that will be visible in the UI.
+   * @param strDataForAction Data that will be passed to eventHandler when click on the label is made.
+   * @param eventHandler ActionListener that will process clicks on this label.
+   * @param icon Icon to display in the label.
+   * @param horizontalAlignment This is one of SwingConstants: LEFT, CENTER, RIGHT, LEADING or TRAILING
+   * @param strTooltip Tooltip to show over the label - if none is provided (e.g. null value), the strLabel will be used as a tooltip.
+   */
+  public JClickableLabel(String strLabel, String strDataForAction, EventListener eventHandler, Icon icon, int horizontalAlignment, String strTooltip)
+  {
+    super(strLabel, icon, horizontalAlignment);
+    
+    this.strData = strDataForAction;
+    this.clickHandler = (ActionListener)eventHandler;
+    
+    // empty border at the top and bottom will simulate "line-spacing"
+    // (this is only needed when an icon is displayed)
+    if (icon != null) {
+      this.setBorder(BorderFactory.createEmptyBorder(3, 0, 3, 0));
+    }
+    
+    // the tooltip for now only shows the full label text
+    this.setToolTipText(strTooltip == null ? strLabel : strTooltip);
+    this.setForeground(Color.BLUE);
+    this.addMouseListener(this);
+  }
+  
+  
+  /* This class extends JLabel, so it can't extend MouseAdapter;
+   * therefore, empty methods will be added for not useful callbacks
+   * from the MouseListener interface.
+   */
+  public void mouseClicked(MouseEvent e) 
+  {
+    // call 'actionPerfermed' method on the clickHandler instance that was supplied
+    // on creation of the JClickableLabel instance
+    this.clickHandler.actionPerformed(new ActionEvent(this, e.getID(), this.strData));
+  }
+  
+  public void mouseEntered(MouseEvent e) 
+  {
+    this.setCursor( Cursor.getPredefinedCursor( Cursor.HAND_CURSOR ) ) ;
+  }
+  
+  public void mouseExited(MouseEvent e) 
+  {
+    this.setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) ) ;
+  }
+  
+  public void mousePressed(MouseEvent e) 
+  {
+    // do nothing
+  }
+  
+  public void mouseReleased(MouseEvent e) 
+  {
+    // do nothing
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponent.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponent.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponent.java
new file mode 100644
index 0000000..f4b6ace
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponent.java
@@ -0,0 +1,645 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.io.ByteArrayInputStream;
+import java.awt.Desktop;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.ImageIcon;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.StyleSheet;
+
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.perspectives.PerspectiveRegistry;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Util;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Workflow;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+import net.sf.taverna.t2.workbench.file.importworkflow.gui.ImportWorkflowWizard;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+import net.sf.taverna.t2.workflowmodel.Dataflow;
+import net.sf.taverna.t2.workflowmodel.serialization.xml.impl.XMLSerializationConstants;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs, Emmanuel Tagarira, Jiten Bhagat
+ */
+public final class MainComponent extends JPanel implements UIComponentSPI, ChangeListener {
+	// myExperiment client, logger and the stylesheet will be made available
+	// throughout the whole perspective
+	private MyExperimentClient myExperimentClient;
+	private final Logger logger = Logger.getLogger(MainComponent.class);
+	private final StyleSheet css;
+	private final ResourcePreviewFactory previewFactory;
+	private final ResourcePreviewBrowser previewBrowser;
+
+	// components of the perspective
+	private JTabbedPane tpMainTabs;
+	private MyStuffTabContentPanel pMyStuffContainer;
+	private ExampleWorkflowsPanel pExampleWorkflows;
+	private TagBrowserTabContentPanel pTagBrowser;
+	private SearchTabContentPanel pSearchTab;
+	private HistoryBrowserTabContentPanel pHistoryBrowserTab;
+	private PluginStatusBar pStatusBar;
+	private PluginPreferencesDialog jdPreferences;
+
+	public static MainComponent MAIN_COMPONENT;
+	public static MyExperimentClient MY_EXPERIMENT_CLIENT;
+	public static Logger LOGGER;
+	private final EditManager editManager;
+	private final FileManager fileManager;
+
+	public MainComponent(EditManager editManager, FileManager fileManager) {
+		super();
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+
+		// create and initialise myExperiment client
+		try {
+			this.myExperimentClient = new MyExperimentClient(logger);
+		} catch (Exception e) {
+			this.logger.error("Couldn't initialise myExperiment client");
+		}
+
+		// x, y, z ARE NOT USED ANYWHERE ELSE
+		// HACK TO BE ABLE TO GET THE REFS FROM TAVERNA'S PREFERENCE PANEL
+		// TODO: refactor code for all the other classes to utilise the class
+		// vars
+		MainComponent x = this;
+		MAIN_COMPONENT = x;
+
+		MyExperimentClient y = this.myExperimentClient;
+		MY_EXPERIMENT_CLIENT = y;
+
+		Logger z = this.logger;
+		LOGGER = z;
+
+		// components to generate and display previews
+		previewFactory = new ResourcePreviewFactory(this, myExperimentClient, logger);
+		previewBrowser = new ResourcePreviewBrowser(this, myExperimentClient, logger, fileManager);
+
+		this.css = new StyleSheet();
+		this.css.importStyleSheet(MyExperimentPerspective.getLocalResourceURL("css_stylesheet"));
+		logger.debug("Stylesheet loaded: \n" + this.css.toString());
+
+		// check if values for default tabs are set, if not - set defaults;
+		// NB! This has to be done before initialising UI
+		if (myExperimentClient.getSettings().getProperty(
+				MyExperimentClient.INI_DEFAULT_ANONYMOUS_TAB) == null)
+			myExperimentClient.getSettings().put(MyExperimentClient.INI_DEFAULT_ANONYMOUS_TAB, "3"); // SEARCH
+		if (myExperimentClient.getSettings().getProperty(
+				MyExperimentClient.INI_DEFAULT_LOGGED_IN_TAB) == null)
+			myExperimentClient.getSettings().put(MyExperimentClient.INI_DEFAULT_LOGGED_IN_TAB, "0"); // STUFF
+
+		initialisePerspectiveUI();
+
+		// HACK for a weird stylesheet bug (where the first thing to use the
+		// stylesheet doesn't actually get the styles)
+		// NB! This has to be located after all ShadedLabels were initialized to
+		// prevent bad layout in them
+		HTMLEditorKit kit = new StyledHTMLEditorKit(this.css);
+
+		// determine which shutdown operations to use
+		if (Util.isRunningInTaverna()) {
+			// register the current instance of main component with the
+			// myExperiment
+			// perspective; this will be used later on when shutdown operation
+			// needs
+			// to be performed - e.g. this aids ShutdownSPI to find the running
+			// instance of the plugin
+			for (PerspectiveSPI perspective : PerspectiveRegistry.getInstance().getPerspectives()) {
+				if (perspective.getText().equals(MyExperimentPerspective.PERSPECTIVE_NAME)) {
+					((MyExperimentPerspective) perspective).setMainComponent(this);
+					break;
+				}
+			}
+		}
+
+		// Do the rest in a separate thread to avoid hanging the GUI.
+		// Remember to use SwingUtilities.invokeLater to update the GUI
+		// directly.
+		new Thread("Data initialisation for Taverna 2 - myExperiment plugin") {
+			@Override
+			public void run() {
+				// load the data into the plugin
+				initialiseData();
+			}
+		}.start();
+
+	}
+
+	public ImageIcon getIcon() {
+		return WorkbenchIcons.databaseIcon;
+	}
+
+	@Override
+	public String getName() {
+		return "myExperiment Perspective Main Component";
+	}
+
+	public void onDisplay() {
+	}
+
+	public void onDispose() {
+	}
+
+	public MyExperimentClient getMyExperimentClient() {
+		return this.myExperimentClient;
+	}
+
+	public Logger getLogger() {
+		return this.logger;
+	}
+
+	public StyleSheet getStyleSheet() {
+		return this.css;
+	}
+
+	public PluginStatusBar getStatusBar() {
+		return this.pStatusBar;
+	}
+
+	public PluginPreferencesDialog getPreferencesDialog() {
+		return this.jdPreferences;
+	}
+
+	public ResourcePreviewFactory getPreviewFactory() {
+		return this.previewFactory;
+	}
+
+	public ResourcePreviewBrowser getPreviewBrowser() {
+		return this.previewBrowser;
+	}
+
+	public HistoryBrowserTabContentPanel getHistoryBrowser() {
+		return this.pHistoryBrowserTab;
+	}
+
+	public JTabbedPane getMainTabs() {
+		return (this.tpMainTabs);
+	}
+
+	public MyStuffTabContentPanel getMyStuffTab() {
+		return (this.pMyStuffContainer);
+	}
+
+	public ExampleWorkflowsPanel getExampleWorkflowsTab() {
+		return (this.pExampleWorkflows);
+	}
+
+	public TagBrowserTabContentPanel getTagBrowserTab() {
+		return (this.pTagBrowser);
+	}
+
+	public SearchTabContentPanel getSearchTab() {
+		return (this.pSearchTab);
+	}
+
+	private void initialisePerspectiveUI() {
+		// HACK: this is required to prevent some labels from having white
+		// non-transparent background
+		ShadedLabel testLabel = new ShadedLabel("test", ShadedLabel.BLUE);
+
+		// create instances of individual components
+		// (NB! Status bar needs to be initialised first, so that it is
+		// available to
+		// other components immediately!)
+		this.pStatusBar = new PluginStatusBar(this, myExperimentClient, logger);
+		this.pMyStuffContainer = new MyStuffTabContentPanel(this, myExperimentClient, logger, fileManager);
+		this.pExampleWorkflows = new ExampleWorkflowsPanel(this, myExperimentClient, logger);
+		this.pTagBrowser = new TagBrowserTabContentPanel(this, myExperimentClient, logger);
+		this.pSearchTab = new SearchTabContentPanel(this, myExperimentClient, logger);
+		this.pHistoryBrowserTab = new HistoryBrowserTabContentPanel(this, myExperimentClient,
+				logger);
+
+		// add the required ones into the main tabs
+		this.tpMainTabs = new JTabbedPane();
+		this.tpMainTabs.add("My Stuff", this.pMyStuffContainer);
+		// TODO: implement the starter pack
+		this.tpMainTabs.add("Starter Pack", this.pExampleWorkflows);
+		this.tpMainTabs.add("Tag Browser", this.pTagBrowser);
+		this.tpMainTabs.add("Search", this.pSearchTab);
+		this.tpMainTabs.add("Local History", this.pHistoryBrowserTab);
+
+		// add main tabs and the status bar into the perspective
+		this.setLayout(new BorderLayout());
+		this.add(this.tpMainTabs, BorderLayout.CENTER);
+		this.add(this.pStatusBar, BorderLayout.SOUTH);
+
+		// add listener to TabbedPane, so that the app "knows" when some tab was
+		// opened
+		this.tpMainTabs.addChangeListener(this);
+
+		// initialise the preferences dialog
+		/*
+		 * NB! this has to be done after all tabs were created (Preview Browser
+		 * is used as an owner of the preferences dialog because Preview Browser
+		 * is the only JFrame in the application - in Java 1.5 it is only
+		 * possible to set an icon to a JFrame and all 'children' dialog of it
+		 * get the same icon - so essentially, this is only to set the
+		 * myExperiment logo as an icon of preferences dialog.)
+		 */
+		this.jdPreferences = new PluginPreferencesDialog(this.getPreviewBrowser(), this,
+				myExperimentClient, logger);
+	}
+
+	private void initialiseData() {
+		this.logger.debug("Initialising myExperiment Perspective data");
+
+		// check if 'auto-login' is required (NB! This requires the BASE_URL to
+		// be
+		// set correctly!)
+		Object oAutoLogin = this.myExperimentClient.getSettings().get(
+				MyExperimentClient.INI_AUTO_LOGIN);
+		if (oAutoLogin != null && oAutoLogin.equals("true")) {
+			this.getStatusBar().setStatus(this.getMyStuffTab().getClass().getName(),
+					"Performing autologin");
+			this.myExperimentClient.doLoginFromStoredCredentials();
+			this.getStatusBar().setStatus(this.getMyStuffTab().getClass().getName(),
+					"Autologin finished. Fetching user data");
+		}
+
+		// NB! This should only be done if the user is logged in -
+		// otherwise this component simply doesn't exist
+		// this.pMyStuffContainer.spMyStuff.setDividerLocation(0.3);
+
+		// load data into all tabs
+		this.pMyStuffContainer.createAndInitialiseInnerComponents();
+		if (this.myExperimentClient.isLoggedIn()) {
+			// set the default tab for logged in user (e.g. as a consequence of
+			// auto-login)
+			tpMainTabs.setSelectedIndex(Integer.parseInt(myExperimentClient.getSettings()
+					.getProperty(MyExperimentClient.INI_DEFAULT_LOGGED_IN_TAB)));
+
+			// auto-login was successful - can display user tags
+			// (no need to refresh this cloud on its own, because the whole tab
+			// is refreshed immediately after)
+			this.pTagBrowser.setMyTagsShown(true);
+		} else {
+			// set the default tab for anonymous user (auto-login failed or
+			// wasn't
+			// chosen)
+			tpMainTabs.setSelectedIndex(Integer.parseInt(myExperimentClient.getSettings()
+					.getProperty(MyExperimentClient.INI_DEFAULT_ANONYMOUS_TAB)));
+		}
+
+		this.pExampleWorkflows.refresh();
+		this.pTagBrowser.refresh();
+	}
+
+	public void stateChanged(ChangeEvent e) {
+		// invoked when a tab is opened
+		if (e.getSource().equals(this.tpMainTabs)) {
+			this.getStatusBar().displayStatus(
+					this.getMainTabs().getSelectedComponent().getClass().getName());
+		}
+	}
+
+	// ************** ACTIONS ***************
+	public class PreviewResourceAction extends AbstractAction {
+		private int iResourceType = Resource.UNKNOWN;
+		private String strResourceURI = "";
+
+		public PreviewResourceAction(int iResourceType, String strResourceURI) {
+			putValue(SMALL_ICON, WorkbenchIcons.zoomIcon);
+			putValue(NAME, "Preview");
+			putValue(SHORT_DESCRIPTION,
+					"Preview this " + Resource.getResourceTypeName(iResourceType).toLowerCase()
+							+ " in the Preview Browser window");
+
+			this.iResourceType = iResourceType;
+			this.strResourceURI = strResourceURI;
+		}
+
+		public void actionPerformed(ActionEvent actionEvent) {
+			getPreviewBrowser()
+					.preview("preview:" + this.iResourceType + ":" + this.strResourceURI);
+		}
+	}
+
+	public class DownloadResourceAction extends AbstractAction {
+		private Resource resource = null;
+
+		public DownloadResourceAction(Resource resource) {
+			this(resource, true);
+		}
+
+		public DownloadResourceAction(Resource resource, boolean bShowButtonLabel) {
+			this.resource = resource;
+			String strResourceType = resource.getItemTypeName().toLowerCase();
+
+			// in either case the icon is the same; label might be displayed -
+			// based
+			// on the parameter
+			putValue(SMALL_ICON, WorkbenchIcons.saveIcon);
+			if (bShowButtonLabel)
+				putValue(NAME, "Download");
+
+			String strTooltip = "Downloading " + strResourceType + "s is currently not possible";
+			boolean bDownloadAllowed = false;
+			if (resource.isDownloadable()) {
+				if (resource.isDownloadAllowed()) {
+					strTooltip = "Download this " + strResourceType + " and store it locally";
+					bDownloadAllowed = true;
+				} else {
+					strTooltip = "You don't have permissions to download this " + strResourceType;
+				}
+			}
+
+			setEnabled(bDownloadAllowed);
+			putValue(SHORT_DESCRIPTION, strTooltip);
+		}
+
+		public void actionPerformed(ActionEvent actionEvent) {
+			try {
+				Desktop.getDesktop().browse(new URI(resource.getResource() + "/download"));
+
+				// update downloaded items history making sure that:
+				// - there's only one occurrence of this item in the history;
+				// - if this item was in the history before, it is moved to the
+				// 'top'
+				// now;
+				// - predefined history size is not exceeded
+				getHistoryBrowser().getDownloadedItemsHistoryList().remove(resource);
+				getHistoryBrowser().getDownloadedItemsHistoryList().add(resource);
+				if (getHistoryBrowser().getDownloadedItemsHistoryList().size() > HistoryBrowserTabContentPanel.DOWNLOADED_ITEMS_HISTORY_LENGTH) {
+					getHistoryBrowser().getDownloadedItemsHistoryList().remove(0);
+				}
+
+				// now update the downloaded items history panel in 'History'
+				// tab
+				if (getHistoryBrowser() != null) {
+					getHistoryBrowser().refreshHistoryBox(
+							HistoryBrowserTabContentPanel.DOWNLOADED_ITEMS_HISTORY);
+				}
+			} catch (Exception ex) {
+				logger.error("Failed while trying to open download URL in a standard browser; URL was: "
+						+ resource.getURI() + "\nException was: " + ex);
+			}
+		}
+	}
+
+	public class LoadResourceInTavernaAction extends AbstractAction {
+		private final Resource resource;
+
+		public LoadResourceInTavernaAction(Resource resource) {
+			this(resource, true);
+		}
+
+		public LoadResourceInTavernaAction(Resource resource, boolean bShowButtonLabel) {
+			this.resource = resource;
+			String strResourceType = resource.getItemTypeName().toLowerCase();
+
+			putValue(SMALL_ICON, WorkbenchIcons.openIcon);
+			if (bShowButtonLabel)
+				putValue(NAME, "Open");
+
+			boolean bLoadingAllowed = false;
+			String strTooltip = "Loading " + strResourceType
+					+ "s into Taverna Workbench is currently not possible";
+			if (resource.getItemType() == Resource.WORKFLOW) {
+				if (((Workflow) resource).isTavernaWorkflow()) {
+					if (resource.isDownloadAllowed()) {
+						// Taverna workflow and download allowed - can load in
+						// Taverna
+						bLoadingAllowed = true;
+						strTooltip = "Download and load this workflow in Design mode of Taverna Workbench";
+					} else {
+						strTooltip = "You don't have permissions to download this workflow, and thus to load into Taverna Workbench";
+					}
+				} else {
+					strTooltip = "Loading workflow of unsupported type into Taverna Workbench is not possible.";
+				}
+			}
+
+			setEnabled(bLoadingAllowed);
+			putValue(SHORT_DESCRIPTION, strTooltip);
+
+		}
+
+		public void actionPerformed(ActionEvent actionEvent) {
+			// if the preview browser window is opened, hide it beneath the main
+			// window
+			if (getPreviewBrowser().isActive())
+				getPreviewBrowser().toBack();
+
+			final String strCallerTabClassName = getMainTabs().getSelectedComponent().getClass()
+					.getName();
+			getStatusBar().setStatus(strCallerTabClassName, "Downloading and opening workflow...");
+			logger.debug("Downloading and opening workflow from URI: " + resource.getURI());
+
+			new Thread("Download and open workflow") {
+				@Override
+				public void run() {
+					try {
+						Workflow w = myExperimentClient.fetchWorkflowBinary(resource.getURI());
+						ByteArrayInputStream workflowDataInputStream = new ByteArrayInputStream(
+								w.getContent());
+
+						FileType fileTypeType = (w.isTaverna1Workflow() ? new ScuflFileType()
+								: new T2FlowFileType());
+						Dataflow openDataflow = fileManager.openDataflow(fileTypeType,
+								workflowDataInputStream);
+					} catch (Exception e) {
+						javax.swing.JOptionPane.showMessageDialog(null,
+								"An error has occurred while trying to load a workflow from myExperiment.\n\n"
+										+ e, "Error", JOptionPane.ERROR_MESSAGE);
+						logger.error(
+								"Failed to open connection to URL to download and open workflow, from myExperiment.",
+								e);
+					}
+
+					getStatusBar().setStatus(strCallerTabClassName, null);
+
+					// update opened items history making sure that:
+					// - there's only one occurrence of this item in the
+					// history;
+					// - if this item was in the history before, it is moved to
+					// the 'top'
+					// now;
+					// - predefined history size is not exceeded
+					getHistoryBrowser().getOpenedItemsHistoryList().remove(resource);
+					getHistoryBrowser().getOpenedItemsHistoryList().add(resource);
+					if (getHistoryBrowser().getOpenedItemsHistoryList().size() > HistoryBrowserTabContentPanel.OPENED_ITEMS_HISTORY_LENGTH) {
+						getHistoryBrowser().getOpenedItemsHistoryList().remove(0);
+					}
+
+					// now update the opened items history panel in 'History'
+					// tab
+					if (getHistoryBrowser() != null) {
+						getHistoryBrowser().refreshHistoryBox(
+								HistoryBrowserTabContentPanel.OPENED_ITEMS_HISTORY);
+					}
+				}
+			}.start();
+		}
+	}
+
+	public class ImportIntoTavernaAction extends AbstractAction {
+		private final Resource resource;
+		private boolean importAsNesting;
+
+		public ImportIntoTavernaAction(Resource r) {
+			this.resource = r;
+
+			String strResourceType = resource.getItemTypeName().toLowerCase();
+
+			putValue(SMALL_ICON, WorkbenchIcons.importIcon);
+			putValue(NAME, "Import");
+
+			boolean bLoadingAllowed = false;
+			String strTooltip = "Loading " + strResourceType
+					+ "s into Taverna Workbench is currently not possible";
+			if (resource.getItemType() == Resource.WORKFLOW) {
+				if (((Workflow) resource).isTavernaWorkflow()) {
+					if (resource.isDownloadAllowed()) {
+						// Taverna workflow and download allowed - can load in
+						// Taverna
+						bLoadingAllowed = true;
+						strTooltip = "Import this workflow into one that is currently open in the Design mode of Taverna Workbench";
+					} else {
+						strTooltip = "You don't have permissions to download this workflow, and thus to load into Taverna Workbench";
+					}
+				} else {
+					strTooltip = "Loading workflow of unsupported type into Taverna Workbench is not possible.";
+				}
+			}
+
+			setEnabled(bLoadingAllowed);
+			putValue(SHORT_DESCRIPTION, strTooltip);
+		}
+
+		public void actionPerformed(ActionEvent actionEvent) {
+			// if the preview browser window is opened, hide it beneath the main
+			// window
+			if (getPreviewBrowser().isActive())
+				getPreviewBrowser().toBack();
+
+			ImportWorkflowWizard importWorkflowDialog = new ImportWorkflowWizard(
+					getPreviewBrowser(), editManager, fileManager);
+
+			Workflow w;
+			try {
+				w = MY_EXPERIMENT_CLIENT.fetchWorkflowBinary(resource.getURI());
+			} catch (Exception e) {
+				JOptionPane.showMessageDialog(null, "An error has occurred "
+						+ "while trying to load a " + "workflow from myExperiment.\n\n" + e,
+						"Error", JOptionPane.ERROR_MESSAGE);
+				LOGGER.error("Failed to open connection to URL to "
+						+ "download and open workflow, from myExperiment.", e);
+				return;
+			}
+			ByteArrayInputStream workflowDataInputStream = new ByteArrayInputStream(w.getContent());
+			FileType fileTypeType = (w.isTaverna1Workflow() ? new MainComponent.ScuflFileType()
+					: new MainComponent.T2FlowFileType());
+			Dataflow toBeImported;
+			try {
+				toBeImported = fileManager.openDataflowSilently(fileTypeType,
+						workflowDataInputStream).getDataflow();
+			} catch (OpenException e) {
+				JOptionPane.showMessageDialog(null, "An error has occurred"
+						+ " while trying to load a " + "workflow from myExperiment.\n\n" + e,
+						"Error", JOptionPane.ERROR_MESSAGE);
+				LOGGER.error("Failed to" + " open connection to URL "
+						+ "to download and open workflow, from myExperiment.", e);
+				return;
+			}
+			importWorkflowDialog.setCustomSourceDataflow(toBeImported, "From myExperiment: "
+					+ resource.getTitle());
+			importWorkflowDialog.setSourceEnabled(false);
+			importWorkflowDialog.setVisible(true);
+
+			// update opened items history making sure that:
+			// - there's only one occurrence of this item in the history;
+			// - if this item was in the history before, it is moved to the
+			// 'top' now;
+			// - predefined history size is not exceeded
+			getHistoryBrowser().getOpenedItemsHistoryList().remove(resource);
+			getHistoryBrowser().getOpenedItemsHistoryList().add(resource);
+			if (getHistoryBrowser().getOpenedItemsHistoryList().size() > HistoryBrowserTabContentPanel.OPENED_ITEMS_HISTORY_LENGTH) {
+				getHistoryBrowser().getOpenedItemsHistoryList().remove(0);
+			}
+
+			// now update the opened items history panel in 'History' tab
+			if (getHistoryBrowser() != null)
+				getHistoryBrowser().refreshHistoryBox(
+						HistoryBrowserTabContentPanel.OPENED_ITEMS_HISTORY);
+		}
+
+	}
+
+	// *** FileTypes for opening workflows inside Taverna
+
+	public static class ScuflFileType extends FileType {
+
+		@Override
+		public String getDescription() {
+			return "Taverna 1 SCUFL workflow";
+		}
+
+		@Override
+		public String getExtension() {
+			return "xml";
+		}
+
+		@Override
+		public String getMimeType() {
+			return "application/vnd.taverna.scufl+xml";
+		}
+	}
+
+	public static class T2FlowFileType extends FileType {
+		@Override
+		public String getDescription() {
+			return "Taverna 2 workflow";
+		}
+
+		@Override
+		public String getExtension() {
+			return "t2flow";
+		}
+
+		@Override
+		public String getMimeType() {
+			// "application/vnd.taverna.t2flow+xml";
+			return XMLSerializationConstants.WORKFLOW_DOCUMENT_MIMETYPE;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponentFactory.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponentFactory.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponentFactory.java
new file mode 100644
index 0000000..65f8875
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponentFactory.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+/**
+ * @author Sergejs Aleksejevs, Jiten Bhagat
+ */
+
+import javax.swing.ImageIcon;
+
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+
+public class MainComponentFactory implements UIComponentFactorySPI {
+
+	private EditManager editManager;
+	private FileManager fileManager;
+
+	public UIComponentSPI getComponent() {
+		return new MainComponent(editManager, fileManager);
+	}
+
+	public ImageIcon getIcon() {
+		return WorkbenchIcons.databaseIcon;
+	}
+
+	public String getName() {
+		return "myExperiment Main Component Factory";
+	}
+
+	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-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponentShutdownHook.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponentShutdownHook.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponentShutdownHook.java
new file mode 100644
index 0000000..2214d5e
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MainComponentShutdownHook.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import net.sf.taverna.t2.ui.perspectives.PerspectiveRegistry;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.workbench.ShutdownSPI;
+import net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs, Jiten Bhagat
+ */
+
+public class MainComponentShutdownHook implements ShutdownSPI {
+  private MainComponent pluginMainComponent;
+  private MyExperimentClient myExperimentClient;
+  private Logger logger;
+
+  public int positionHint() {
+	// all custom plugins are suggested to return a value of > 100;
+	// this affects when in the termination process will this plugin
+	// be shutdown;
+	return 100;
+  }
+
+  public boolean shutdown() {
+	// find instance of main component of the running myExperiment perspective
+	MainComponent mainComponent = null;
+	for (PerspectiveSPI perspective : PerspectiveRegistry.getInstance().getPerspectives()) {
+	  if (perspective instanceof MyExperimentPerspective) {		  
+		mainComponent = ((MyExperimentPerspective) perspective).getMainComponent();
+		break;
+	  }
+	}
+
+	// if myExperiment perspective wasn't initialised, no shutdown operations are required / possible
+	if (mainComponent != null) {
+	  this.setLinks(mainComponent, mainComponent.getMyExperimentClient(), mainComponent.getLogger());
+	  logger.debug("Starting shutdown operations for myExperiment plugin");
+
+	  try {
+		myExperimentClient.storeHistoryAndSettings();
+	  } catch (Exception e) {
+		logger.error("Failed while serializing myExperiment plugin settings:\n"
+			+ e);
+	  }
+
+	  logger.debug("myExperiment plugin shutdown is completed; terminated...");
+	}
+
+	// "true" means that shutdown operations are complete and Taverna can terminate
+	return true;
+  }
+
+  /**
+   * Sets up links of this class with the rest of the plugin.
+   */
+  public void setLinks(MainComponent component, MyExperimentClient client, Logger logger) {
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyExperimentPerspective.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyExperimentPerspective.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyExperimentPerspective.java
new file mode 100644
index 0000000..1982b75
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyExperimentPerspective.java
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.swing.ImageIcon;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI;
+
+import org.jdom.Element;
+
+/**
+ * @author Sergejs Aleksejevs, Jiten Bhagat
+ */
+public class MyExperimentPerspective implements PerspectiveSPI {
+	// CONSTANTS
+	// this is where all icons, stylesheet, etc are located
+	private static final String BASE_RESOURCE_PATH = "/net/sf/taverna/t2/ui/perspectives/myexperiment/";
+	public static final String PERSPECTIVE_NAME = "myExperiment";
+	public static final String PLUGIN_VERSION = "0.2beta";
+
+	// COMPONENTS
+	private MainComponent perspectiveMainComponent;
+	private boolean visible = true;
+
+	public ImageIcon getButtonIcon() {
+		URL iconURL = MyExperimentPerspective.getLocalResourceURL("myexp_icon16x16");
+		if (iconURL == null) {
+			return null;
+		} else {
+			return new ImageIcon(iconURL);
+		}
+	}
+
+	public InputStream getLayoutInputStream() {
+		return getClass().getResourceAsStream("myexperiment-perspective.xml");
+	}
+
+	public String getText() {
+		return PERSPECTIVE_NAME;
+	}
+
+	public boolean isVisible() {
+		return visible;
+	}
+
+	public int positionHint() {
+		// this determines position of myExperiment perspective in the
+		// bar with perspective buttons (currently makes it the last in the
+		// list)
+		return 30;
+	}
+
+	public void setVisible(boolean visible) {
+		this.visible = visible;
+
+	}
+
+	public void update(Element layoutElement) {
+		// Not sure what to do here
+	}
+
+	public void setMainComponent(MainComponent component) {
+		this.perspectiveMainComponent = component;
+	}
+
+	/**
+	 * Returns the instance of the main component of this perspective.
+	 */
+	public MainComponent getMainComponent() {
+		return this.perspectiveMainComponent;
+	}
+
+	// a single point in the plugin where all resources are referenced
+	public static URL getLocalResourceURL(String strResourceName) {
+		String strResourcePath = MyExperimentPerspective.BASE_RESOURCE_PATH;
+
+		if (strResourceName.equals("not_authorized_icon"))
+			strResourcePath += "denied.png";
+		if (strResourceName.equals("failure_icon"))
+			strResourcePath += "denied.png";
+		else if (strResourceName.equals("success_icon"))
+			strResourcePath += "tick.png";
+		else if (strResourceName.equals("spinner"))
+			strResourcePath += "ajax-loader.gif";
+		else if (strResourceName.equals("spinner_stopped"))
+			strResourcePath += "ajax-loader-still.gif";
+		else if (strResourceName.equals("external_link_small_icon"))
+			strResourcePath += "external_link_listing_small.png";
+		else if (strResourceName.equals("back_icon"))
+			strResourcePath += "arrow_left.png";
+		else if (strResourceName.equals("forward_icon"))
+			strResourcePath += "arrow_right.png";
+		else if (strResourceName.equals("refresh_icon"))
+			strResourcePath += "arrow_refresh.png";
+		else if (strResourceName.equals("favourite_icon"))
+			strResourcePath += "star.png";
+		else if (strResourceName.equals("add_favourite_icon"))
+			strResourcePath += "favourite_add.png";
+		else if (strResourceName.equals("delete_favourite_icon"))
+			strResourcePath += "favourite_delete.png";
+		else if (strResourceName.equals("destroy_icon"))
+			strResourcePath += "cross.png";
+		else if (strResourceName.equals("add_comment_icon"))
+			strResourcePath += "comment_add.png";
+		else if (strResourceName.equals("myexp_icon"))
+			strResourcePath += "myexp_icon.png";
+		else if (strResourceName.equals("myexp_icon16x16"))
+			strResourcePath += "myexp_icon16x16.png";
+		else if (strResourceName.equals("open_in_my_experiment_icon"))
+			strResourcePath += "open_in_myExperiment.png";
+		else if (strResourceName.equals("login_icon"))
+			strResourcePath += "login.png";
+		else if (strResourceName.equals("logout_icon"))
+			strResourcePath += "logout.png";
+		else if (strResourceName.equals("css_stylesheet"))
+			strResourcePath += "styles.css";
+		else {
+			throw new java.lang.IllegalArgumentException(
+					"Unknown myExperiment plugin resource requested; requested resource name was: "
+							+ strResourceName);
+		}
+
+		// no exception was thrown, therefore the supplied resource name was
+		// recognised;
+		// return the local URL of that resource
+		return (MyExperimentPerspective.class.getResource(strResourcePath));
+	}
+
+	// a single point in the plugin where all resources' icons are referenced
+	public static URL getLocalIconURL(int iResourceType) {
+		String strResourcePath = MyExperimentPerspective.BASE_RESOURCE_PATH;
+
+		switch (iResourceType) {
+		case Resource.WORKFLOW:
+			strResourcePath += "workflow.png";
+			break;
+		case Resource.FILE:
+			strResourcePath += "file.png";
+			break;
+		case Resource.PACK:
+			strResourcePath += "pack.png";
+			break;
+		case Resource.PACK_EXTERNAL_ITEM:
+			strResourcePath += "remote_resource.png";
+			break;
+		case Resource.USER:
+			strResourcePath += "user.png";
+			break;
+		case Resource.GROUP:
+			strResourcePath += "group.png";
+			break;
+		case Resource.TAG:
+			strResourcePath += "tag_blue.png";
+			break;
+		default:
+			throw new java.lang.IllegalArgumentException(
+					"Unknown myExperiment plugin resource requested; requested resource name was: "
+							+ Resource.getResourceTypeName(iResourceType));
+		}
+
+		// no exception was thrown, therefore the supplied resource name was
+		// recognised;
+		// return the local URL of that resource
+		return (MyExperimentPerspective.class.getResource(strResourcePath));
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffContributionsPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffContributionsPanel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffContributionsPanel.java
new file mode 100644
index 0000000..e7ef972
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffContributionsPanel.java
@@ -0,0 +1,370 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.File;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Pack;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Workflow;
+
+import org.apache.log4j.Logger;
+import org.jdom.Document;
+import org.jdom.Element;
+
+
+/**
+ * @author Sergejs Aleksejevs, Emmanuel Tagarira, Jiten Bhagat
+ */
+public class MyStuffContributionsPanel extends JPanel implements ActionListener {
+  // CONSTANTS
+  private static final int SHADED_LABEL_HEIGHT = 20;
+  private static final int SECTION_VSPACING = 5;
+  private static final int SCROLL_PANE_PADDING = 3;
+  private static final int TOTAL_SECTION_VSPACING = SHADED_LABEL_HEIGHT
+	  + SECTION_VSPACING + SCROLL_PANE_PADDING + 5; // the last literal is to cover all paddings / margins / etc
+
+  private MainComponent pluginMainComponent;
+  private MyExperimentClient myExperimentClient;
+  private Logger logger;
+
+  // MAIN COMPONENTS of the view
+  JPanel jpMyWorkflows;
+  JPanel jpMyFiles;
+  JPanel jpMyPacks;
+
+  // HELPER COMPONENTS
+  // these are the individual content listings for 'my workfows', 'my files' and 'my packs' ..
+  ResourceListPanel jpMyWorkflowsContent = null;
+  ResourceListPanel jpMyFilesContent = null;
+  ResourceListPanel jpMyPacksContent = null;
+
+  // .. these will be wrapped into individual scroll panes to ensure correct sizes
+  JScrollPane spMyWorkflowsContent = null;
+  JScrollPane spMyFilesContent = null;
+  JScrollPane spMyPacksContent = null;
+
+  // STORAGE
+  private ArrayList<JPanel> alVisiblePanels;
+  private ArrayList<JComponent[]> alVisiblePanelsWithHelperElements;
+
+  public MyStuffContributionsPanel(MainComponent component, MyExperimentClient client, Logger logger) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	alVisiblePanels = new ArrayList<JPanel>();
+	alVisiblePanelsWithHelperElements = new ArrayList<JComponent[]>();
+
+	// check that settings for this panel were set correctly in the INI file
+	// (if any record is missing - assume that this section is visible)
+	// (this will ensure that these values can be used with no further validity checks
+	//  across the plugin; this is because this method will be executed at start of the plugin)
+	if (myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_MY_STUFF_WORKFLOWS) == null) {
+	  myExperimentClient.getSettings().put(MyExperimentClient.INI_MY_STUFF_WORKFLOWS, new Boolean(true).toString());
+	}
+	if (myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_MY_STUFF_FILES) == null) {
+	  myExperimentClient.getSettings().put(MyExperimentClient.INI_MY_STUFF_FILES, new Boolean(true).toString());
+	}
+	if (myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_MY_STUFF_PACKS) == null) {
+	  myExperimentClient.getSettings().put(MyExperimentClient.INI_MY_STUFF_PACKS, new Boolean(true).toString());
+	}
+
+	// create and initialise the UI of MyStuff tab
+	initialiseUI();
+	initialiseData();
+  }
+
+  private void initialiseUI() {
+	JPanel jpMyWorkflowsContainer = new JPanel();
+	jpMyWorkflowsContainer.setBorder(BorderFactory.createEmptyBorder());
+	if (Boolean.parseBoolean(myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_MY_STUFF_WORKFLOWS))) {
+	  // "My Workflows" panel
+	  jpMyWorkflowsContainer.setBorder(BorderFactory.createEtchedBorder());
+	  jpMyWorkflowsContainer.setLayout(new BorderLayout());
+
+	  ShadedLabel l0 = new ShadedLabel("My Workflows", ShadedLabel.BLUE);
+	  jpMyWorkflowsContainer.add(l0, BorderLayout.NORTH);
+
+	  jpMyWorkflows = new JPanel();
+	  jpMyWorkflows.setLayout(new BorderLayout());
+	  jpMyWorkflows.setBackground(Color.WHITE);
+	  jpMyWorkflows.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
+	  jpMyWorkflows.add(new JLabel("Loading...", new ImageIcon(MyExperimentPerspective.getLocalResourceURL("spinner")), SwingConstants.CENTER));
+
+	  jpMyWorkflowsContainer.add(jpMyWorkflows, BorderLayout.CENTER);
+	  alVisiblePanels.add(jpMyWorkflows);
+	}
+
+	JPanel jpMyFilesContainer = new JPanel();
+	if (Boolean.parseBoolean(myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_MY_STUFF_FILES))) {
+	  // "My Files" panel
+	  jpMyFilesContainer.setBorder(BorderFactory.createEtchedBorder());
+	  jpMyFilesContainer.setLayout(new BorderLayout());
+
+	  ShadedLabel l1 = new ShadedLabel("My Files", ShadedLabel.BLUE);
+	  jpMyFilesContainer.add(l1, BorderLayout.NORTH);
+
+	  jpMyFiles = new JPanel();
+	  jpMyFiles.setLayout(new BorderLayout());
+	  jpMyFiles.setBackground(Color.WHITE);
+	  jpMyFiles.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
+	  jpMyFiles.add(new JLabel("Loading...", new ImageIcon(MyExperimentPerspective.getLocalResourceURL("spinner")), SwingConstants.CENTER));
+
+	  jpMyFilesContainer.add(jpMyFiles, BorderLayout.CENTER);
+	  alVisiblePanels.add(jpMyFiles);
+	}
+
+	JPanel jpMyPacksContainer = new JPanel();
+	if (Boolean.parseBoolean(myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_MY_STUFF_PACKS))) {
+	  // "My Packs" panel
+	  jpMyPacksContainer.setBorder(BorderFactory.createEtchedBorder());
+	  jpMyPacksContainer.setLayout(new BorderLayout());
+
+	  ShadedLabel l2 = new ShadedLabel("My Packs", ShadedLabel.BLUE);
+	  jpMyPacksContainer.add(l2, BorderLayout.NORTH);
+
+	  jpMyPacks = new JPanel();
+	  jpMyPacks.setLayout(new BorderLayout());
+	  jpMyPacks.setBackground(Color.WHITE);
+	  jpMyPacks.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
+	  jpMyPacks.add(new JLabel("Loading...", new ImageIcon(MyExperimentPerspective.getLocalResourceURL("spinner")), SwingConstants.CENTER));
+
+	  jpMyPacksContainer.add(jpMyPacks, BorderLayout.CENTER);
+	  alVisiblePanels.add(jpMyPacks);
+	}
+
+	// ..putting everything together    
+	JPanel jpEverything = new JPanel();
+	jpEverything.setLayout(new GridBagLayout());
+
+	GridBagConstraints gbConstraints = new GridBagConstraints();
+	gbConstraints.anchor = GridBagConstraints.NORTHWEST;
+	gbConstraints.fill = GridBagConstraints.BOTH;
+	gbConstraints.gridx = 0;
+	gbConstraints.weightx = 1;
+	gbConstraints.weighty = 1;
+	int index = 0;
+
+	gbConstraints.gridy = index++;
+	jpEverything.add(jpMyWorkflowsContainer, gbConstraints);
+
+	gbConstraints.gridy = index++;
+	jpEverything.add(jpMyFilesContainer, gbConstraints);
+
+	gbConstraints.gridy = index++;
+	jpEverything.add(jpMyPacksContainer, gbConstraints);
+
+	this.setLayout(new BorderLayout());
+	this.add(jpEverything, BorderLayout.NORTH);
+  }
+
+  private void initialiseData() {
+	// Make call to myExperiment API in a different thread
+	// (then use SwingUtilities.invokeLater to update the UI when ready).
+	new Thread("Loading data about contributions of current user.") {
+	  @SuppressWarnings("unchecked")
+	  public void run() {
+		logger.debug("Loading contributions data for current user");
+
+		try {
+		  final ArrayList<Workflow> alWorkflowInstances = new ArrayList<Workflow>();
+		  if (alVisiblePanels.contains(jpMyWorkflows)) {
+			boolean anyMore = true;
+			for (int page = 1; anyMore; page++) {
+				// fetch all user workflows
+				Document doc = myExperimentClient.getUserContributions(myExperimentClient.getCurrentUser(), Resource.WORKFLOW, Resource.REQUEST_SHORT_LISTING, page);
+				if (doc != null) {
+					List<Element> foundElements = doc.getRootElement().getChildren();
+					anyMore = !foundElements.isEmpty();
+					for (Element e : foundElements) {
+						Workflow wfCurrent = Workflow.buildFromXML(e, logger);
+						alWorkflowInstances.add(wfCurrent);
+					}
+				}
+				}
+		  }
+
+		  final ArrayList<File> alFileInstances = new ArrayList<File>();
+		  if (alVisiblePanels.contains(jpMyFiles)) {
+				boolean anyMore = true;
+				for (int page = 1; anyMore; page++) {
+			// fetch all user files
+			Document doc = myExperimentClient.getUserContributions(myExperimentClient.getCurrentUser(), Resource.FILE, Resource.REQUEST_SHORT_LISTING, page);
+			if (doc != null) {
+			  List<Element> foundElements = doc.getRootElement().getChildren();
+				anyMore = !foundElements.isEmpty();
+			  for (Element e : foundElements) {
+				File fCurrent = File.buildFromXML(e, logger);
+				alFileInstances.add(fCurrent);
+			  }
+			}
+				}
+		  }
+
+		  final ArrayList<Pack> alPackInstances = new ArrayList<Pack>();
+		  if (alVisiblePanels.contains(jpMyPacks)) {
+				boolean anyMore = true;
+				for (int page = 1; anyMore; page++) {
+			// fetch all user packs
+			Document doc = myExperimentClient.getUserContributions(myExperimentClient.getCurrentUser(), Resource.PACK, Resource.REQUEST_SHORT_LISTING, page);
+			if (doc != null) {
+			  List<Element> foundElements = doc.getRootElement().getChildren();
+				anyMore = !foundElements.isEmpty();
+			  for (Element e : foundElements) {
+				Pack pCurrent = Pack.buildFromXML(e, myExperimentClient, logger);
+				alPackInstances.add(pCurrent);
+			  }
+			}
+				}
+		  }
+
+		  SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+			  // now create views for all user contributions
+			  if (alVisiblePanels.contains(jpMyWorkflows)) {
+				// .. workflows ..
+				jpMyWorkflowsContent = new ResourceListPanel(pluginMainComponent, myExperimentClient, logger);
+				jpMyWorkflowsContent.setFullSizeItemsList(false);
+				jpMyWorkflowsContent.setListItems(new ArrayList<Resource>(alWorkflowInstances));
+
+				spMyWorkflowsContent = new JScrollPane(jpMyWorkflowsContent);
+				spMyWorkflowsContent.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+				jpMyWorkflows.removeAll();
+				jpMyWorkflows.setBackground(null); // return background to default colour
+				jpMyWorkflows.setBorder(BorderFactory.createEmptyBorder()); // remove border that was added prior to loading
+				jpMyWorkflows.add(spMyWorkflowsContent);
+
+				alVisiblePanelsWithHelperElements.add(new JComponent[] { jpMyWorkflows, spMyWorkflowsContent, jpMyWorkflowsContent });
+			  }
+
+			  if (alVisiblePanels.contains(jpMyFiles)) {
+				// .. files ..
+				jpMyFilesContent = new ResourceListPanel(pluginMainComponent, myExperimentClient, logger);
+				jpMyFilesContent.setFullSizeItemsList(false);
+				jpMyFilesContent.setListItems(new ArrayList<Resource>(alFileInstances));
+
+				spMyFilesContent = new JScrollPane(jpMyFilesContent);
+				spMyFilesContent.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+				jpMyFiles.removeAll();
+				jpMyFiles.setBackground(null); // return background to default colour
+				jpMyFiles.setBorder(BorderFactory.createEmptyBorder()); // remove border that was added prior to loading
+				jpMyFiles.add(spMyFilesContent);
+
+				alVisiblePanelsWithHelperElements.add(new JComponent[] { jpMyFiles, spMyFilesContent, jpMyFilesContent });
+			  }
+
+			  if (alVisiblePanels.contains(jpMyPacks)) {
+				// .. packs ..
+				jpMyPacksContent = new ResourceListPanel(pluginMainComponent, myExperimentClient, logger);
+				jpMyPacksContent.setFullSizeItemsList(false);
+				jpMyPacksContent.setListItems(new ArrayList<Resource>(alPackInstances));
+
+				spMyPacksContent = new JScrollPane(jpMyPacksContent);
+				spMyPacksContent.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+				jpMyPacks.removeAll();
+				jpMyPacks.setBackground(null); // return background to default colour
+				jpMyPacks.setBorder(BorderFactory.createEmptyBorder()); // remove border that was added prior to loading
+				jpMyPacks.add(spMyPacksContent);
+
+				alVisiblePanelsWithHelperElements.add(new JComponent[] { jpMyPacks, spMyPacksContent, jpMyPacksContent });
+			  }
+
+			  // now work out correct sizes for each section - the goal is to maximize the usage of the space on the page
+
+			  int iFullAvailableHeight = getSize().height;
+			  ArrayList<Integer> alIndexesToScale = new ArrayList<Integer>();
+			  for (int i = 0; i < alVisiblePanelsWithHelperElements.size(); i++) {
+				JScrollPane spContent = (JScrollPane) alVisiblePanelsWithHelperElements.get(i)[1];
+				JPanel jpContent = (JPanel) alVisiblePanelsWithHelperElements.get(i)[2];
+
+				if ((jpContent.getPreferredSize().height + TOTAL_SECTION_VSPACING) < (iFullAvailableHeight / alVisiblePanels.size())) {
+				  Dimension d = jpContent.getPreferredSize();
+				  d.height += SCROLL_PANE_PADDING;
+				  spContent.setPreferredSize(d);
+
+				  iFullAvailableHeight -= (jpContent.getPreferredSize().height)
+					  + TOTAL_SECTION_VSPACING;
+				} else
+				  alIndexesToScale.add(i);
+			  }
+
+			  if (alIndexesToScale.size() > 0) {
+				Dimension d = new Dimension();
+
+				for (Integer i : alIndexesToScale) {
+				  d.height = (iFullAvailableHeight / alIndexesToScale.size())
+					  - TOTAL_SECTION_VSPACING;
+				  if (d.height > ((JPanel) alVisiblePanelsWithHelperElements.get(i)[2]).getPreferredSize().height
+					  + SCROLL_PANE_PADDING)
+					d.height = ((JPanel) alVisiblePanelsWithHelperElements.get(i)[2]).getPreferredSize().height
+						+ SCROLL_PANE_PADDING;
+
+				  JScrollPane spCurrent = (JScrollPane) alVisiblePanelsWithHelperElements.get(i)[1];
+				  spCurrent.setPreferredSize(d);
+				}
+			  }
+
+			  // report that this component has been loaded
+			  pluginMainComponent.getMyStuffTab().cdlComponentLoadingDone.countDown();
+
+			  validate();
+			  repaint();
+			}
+		  });
+		} catch (Exception ex) {
+		  logger.error("Failed to populate some panel in My Stuff tab (User's files, workflows or packs)", ex);
+		}
+	  }
+	}.start();
+  }
+
+  public void actionPerformed(ActionEvent arg0) {
+	javax.swing.JOptionPane.showMessageDialog(null, "button clicked");
+  }
+
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..6938308
--- /dev/null
+++ b/taverna-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,6 @@
+net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuSection
+net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuAction
+net.sf.taverna.t2.workbench.edits.impl.menu.RedoMenuAction
+net.sf.taverna.t2.workbench.edits.impl.toolbar.EditToolbarSection
+net.sf.taverna.t2.workbench.edits.impl.toolbar.UndoToolbarAction
+net.sf.taverna.t2.workbench.edits.impl.toolbar.RedoToolbarAction

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.edits.EditManager
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.edits.EditManager b/taverna-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.edits.EditManager
new file mode 100644
index 0000000..92ee088
--- /dev/null
+++ b/taverna-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.edits.EditManager
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.edits.impl.EditManagerImpl

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/resources/META-INF/spring/edits-impl-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/resources/META-INF/spring/edits-impl-context-osgi.xml b/taverna-edits-impl/src/main/resources/META-INF/spring/edits-impl-context-osgi.xml
new file mode 100644
index 0000000..8eb7041
--- /dev/null
+++ b/taverna-edits-impl/src/main/resources/META-INF/spring/edits-impl-context-osgi.xml
@@ -0,0 +1,20 @@
+<?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="UndoMenuSection" auto-export="interfaces" />
+	<service ref="UndoMenuAction" auto-export="interfaces" />
+	<service ref="RedoMenuAction" auto-export="interfaces" />
+	<service ref="EditToolbarSection" auto-export="interfaces" />
+	<service ref="UndoToolbarAction" auto-export="interfaces" />
+	<service ref="RedoToolbarAction" auto-export="interfaces" />
+
+	<service ref="EditManagerImpl" interface="net.sf.taverna.t2.workbench.edits.EditManager" />
+
+	<reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" cardinality="0..1" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/resources/META-INF/spring/edits-impl-context.xml
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/resources/META-INF/spring/edits-impl-context.xml b/taverna-edits-impl/src/main/resources/META-INF/spring/edits-impl-context.xml
new file mode 100644
index 0000000..33f0b7b
--- /dev/null
+++ b/taverna-edits-impl/src/main/resources/META-INF/spring/edits-impl-context.xml
@@ -0,0 +1,33 @@
+<?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="UndoMenuSection" class="net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuSection" />
+	<bean id="UndoMenuAction" class="net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuAction">
+		<constructor-arg name="editManager">
+			<ref local="EditManagerImpl" />
+		</constructor-arg>
+		<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="RedoMenuAction" class="net.sf.taverna.t2.workbench.edits.impl.menu.RedoMenuAction">
+		<constructor-arg name="editManager">
+			<ref local="EditManagerImpl" />
+		</constructor-arg>
+		<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="EditToolbarSection" class="net.sf.taverna.t2.workbench.edits.impl.toolbar.EditToolbarSection" />
+	<bean id="UndoToolbarAction" class="net.sf.taverna.t2.workbench.edits.impl.toolbar.UndoToolbarAction">
+		<constructor-arg>
+			<ref local="UndoMenuAction" />
+		</constructor-arg>
+	</bean>
+	<bean id="RedoToolbarAction" class="net.sf.taverna.t2.workbench.edits.impl.toolbar.RedoToolbarAction">
+		<constructor-arg>
+			<ref local="RedoMenuAction" />
+		</constructor-arg>
+	</bean>
+
+	<bean id="EditManagerImpl" class="net.sf.taverna.t2.workbench.edits.impl.EditManagerImpl" />
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/test/java/net/sf/taverna/t2/workbench/edits/impl/TestEditManagerImpl.java
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/test/java/net/sf/taverna/t2/workbench/edits/impl/TestEditManagerImpl.java b/taverna-edits-impl/src/test/java/net/sf/taverna/t2/workbench/edits/impl/TestEditManagerImpl.java
new file mode 100644
index 0000000..9123671
--- /dev/null
+++ b/taverna-edits-impl/src/test/java/net/sf/taverna/t2/workbench/edits/impl/TestEditManagerImpl.java
@@ -0,0 +1,258 @@
+/*******************************************************************************
+ * 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.edits.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.EditManager.DataFlowRedoEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.DataFlowUndoEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.DataflowEditEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+import net.sf.taverna.t2.workflow.edits.AddProcessorEdit;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public class TestEditManagerImpl {
+
+	private Workflow dataflow;
+
+	private EditManagerObserver editManagerObserver = new EditManagerObserver();
+
+	private Processor processor;
+
+	@Test
+	public void addProcessor() throws Exception {
+		EditManager editManager = new EditManagerImpl();
+		editManager.addObserver(editManagerObserver);
+
+		Edit<Workflow> edit = new AddProcessorEdit(dataflow, processor);
+		assertFalse("Edit was already applied", edit.isApplied());
+		assertTrue("Did already add processor", dataflow.getProcessors()
+				.isEmpty());
+
+		editManager.doDataflowEdit(dataflow.getParent(), edit);
+		assertTrue("Edit was not applied", edit.isApplied());
+		assertEquals("Did not add processor", processor, dataflow.getProcessors().first());
+
+		// Should have received the edit event
+		assertEquals("Incorrect number of events", 1,
+				editManagerObserver.events.size());
+		EditManagerEvent event = editManagerObserver.events.get(0);
+		assertTrue("Event was not a DataflowEditEvent",
+				event instanceof DataflowEditEvent);
+		DataflowEditEvent dataEditEvent = (DataflowEditEvent) event;
+		assertEquals("Event did not have correct workflow", dataflow,
+				dataEditEvent.getDataFlow().getWorkflows().first());
+		assertEquals("Event did not have correct edit", edit, dataEditEvent
+				.getEdit());
+
+	}
+
+	@Test
+	public void undoAddProcessor() throws Exception {
+		EditManager editManager = new EditManagerImpl();
+		editManager.addObserver(editManagerObserver);
+
+		Edit<Workflow> edit = new AddProcessorEdit(dataflow, processor);
+		editManager.doDataflowEdit(dataflow.getParent(), edit);
+
+		assertFalse("Did not add processor", dataflow.getProcessors().isEmpty());
+		editManager.undoDataflowEdit(dataflow.getParent());
+		assertTrue("Did not undo add processor", dataflow.getProcessors()
+				.isEmpty());
+
+		// Should have received the undo event
+		assertEquals("Incorrect number of events", 2,
+				editManagerObserver.events.size());
+		EditManagerEvent event = editManagerObserver.events.get(1);
+		assertTrue("Event was not a DataflowEditEvent",
+				event instanceof DataFlowUndoEvent);
+		DataFlowUndoEvent dataEditEvent = (DataFlowUndoEvent) event;
+		assertEquals("Event did not have correct workflow", dataflow,
+				dataEditEvent.getDataFlow().getWorkflows().first());
+		assertEquals("Event did not have correct edit", edit, dataEditEvent
+				.getEdit());
+		assertFalse("Edit was still applied", edit.isApplied());
+	}
+
+	@Test
+	public void multipleUndoesRedoes() throws Exception {
+		EditManager editManager = new EditManagerImpl();
+		editManager.addObserver(editManagerObserver);
+
+		Workflow dataflowA = createDataflow();
+		Workflow dataflowB = createDataflow();
+		Workflow dataflowC = createDataflow();
+
+		Processor processorA1 = createProcessor();
+		Processor processorA2 = createProcessor();
+		Processor processorA3 = createProcessor();
+		Processor processorB1 = createProcessor();
+		Processor processorC1 = createProcessor();
+
+		Edit<Workflow> edit = new AddProcessorEdit(dataflowA, processorA1);
+		editManager.doDataflowEdit(dataflowA.getParent(), edit);
+
+		edit = new AddProcessorEdit(dataflowB, processorB1);
+		editManager.doDataflowEdit(dataflowB.getParent(), edit);
+
+		edit = new AddProcessorEdit(dataflowA, processorA2);
+		editManager.doDataflowEdit(dataflowA.getParent(), edit);
+
+		edit = new AddProcessorEdit(dataflowC, processorC1);
+		editManager.doDataflowEdit(dataflowC.getParent(), edit);
+
+		edit = new AddProcessorEdit(dataflowA, processorA3);
+		editManager.doDataflowEdit(dataflowA.getParent(), edit);
+
+
+
+		assertFalse("Did not add processors", dataflowA.getProcessors().isEmpty());
+		assertEquals(3, dataflowA.getProcessors().size());
+		editManager.undoDataflowEdit(dataflowA.getParent());
+		assertEquals(2, dataflowA.getProcessors().size());
+		editManager.undoDataflowEdit(dataflowA.getParent());
+		assertEquals(1, dataflowA.getProcessors().size());
+		editManager.undoDataflowEdit(dataflowA.getParent());
+		assertEquals(0, dataflowA.getProcessors().size());
+
+		assertEquals(1, dataflowB.getProcessors().size());
+		assertEquals(1, dataflowC.getProcessors().size());
+
+		assertTrue(editManager.canUndoDataflowEdit(dataflowC.getParent()));
+		editManager.undoDataflowEdit(dataflowC.getParent());
+		assertFalse(editManager.canUndoDataflowEdit(dataflowC.getParent()));
+		editManager.undoDataflowEdit(dataflowC.getParent()); // extra one
+		assertFalse(editManager.canUndoDataflowEdit(dataflowC.getParent()));
+
+
+		assertEquals(1, dataflowB.getProcessors().size());
+		assertEquals(0, dataflowC.getProcessors().size());
+
+		editManager.undoDataflowEdit(dataflowB.getParent());
+		assertEquals(0, dataflowA.getProcessors().size());
+		assertEquals(0, dataflowB.getProcessors().size());
+		assertEquals(0, dataflowC.getProcessors().size());
+
+		editManager.redoDataflowEdit(dataflowA.getParent());
+		assertEquals(1, dataflowA.getProcessors().size());
+
+		editManager.redoDataflowEdit(dataflowA.getParent());
+		assertEquals(2, dataflowA.getProcessors().size());
+
+		editManager.redoDataflowEdit(dataflowA.getParent());
+		assertEquals(3, dataflowA.getProcessors().size());
+
+		// does not affect it
+		editManager.redoDataflowEdit(dataflowA.getParent());
+		assertEquals(3, dataflowA.getProcessors().size());
+		assertEquals(0, dataflowB.getProcessors().size());
+		assertEquals(0, dataflowC.getProcessors().size());
+	}
+
+	@Test
+	public void emptyUndoDoesNotFail() throws Exception {
+		EditManager editManager = new EditManagerImpl();
+		editManager.addObserver(editManagerObserver);
+		editManager.undoDataflowEdit(dataflow.getParent());
+	}
+
+	@Test
+	public void extraUndoesDoesNotFail() throws Exception {
+		EditManager editManager = new EditManagerImpl();
+		editManager.addObserver(editManagerObserver);
+
+		Edit<Workflow> edit = new AddProcessorEdit(dataflow, processor);
+		editManager.doDataflowEdit(dataflow.getParent(), edit);
+
+		assertFalse("Did not add processor", dataflow.getProcessors().isEmpty());
+		editManager.undoDataflowEdit(dataflow.getParent());
+		assertTrue("Did not undo add processor", dataflow.getProcessors()
+				.isEmpty());
+		editManager.undoDataflowEdit(dataflow.getParent());
+	}
+
+	@Before
+	public void makeDataflow() {
+		dataflow = createDataflow();
+	}
+
+	protected Workflow createDataflow() {
+		WorkflowBundle workflowBundle = new WorkflowBundle();
+		Workflow workflow = new Workflow();
+		workflow.setParent(workflowBundle);
+		return workflow;
+	}
+
+	protected Processor createProcessor() {
+		Processor processor = new Processor();
+		processor.setName("proc-" + UUID.randomUUID());
+		return processor;
+	}
+
+	@Before
+	public void makeProcessor() {
+		processor = createProcessor();
+	}
+
+	private class EditManagerObserver implements Observer<EditManagerEvent> {
+
+		public List<EditManagerEvent> events = new ArrayList<>();
+
+		@Override
+		public void notify(Observable<EditManagerEvent> sender,
+				EditManagerEvent message) throws Exception {
+			events.add(message);
+			if (message instanceof DataflowEditEvent) {
+				DataflowEditEvent dataflowEdit = (DataflowEditEvent) message;
+				assertTrue("Edit was not applied on edit event", dataflowEdit
+						.getEdit().isApplied());
+			} else if (message instanceof DataFlowUndoEvent) {
+				DataFlowUndoEvent dataflowUndo = (DataFlowUndoEvent) message;
+				assertFalse("Edit was applied on undo event", dataflowUndo
+						.getEdit().isApplied());
+			} else if (message instanceof DataFlowRedoEvent) {
+				DataFlowRedoEvent dataflowEdit = (DataFlowRedoEvent) message;
+				assertTrue("Edit was not applied on edit event", dataflowEdit
+						.getEdit().isApplied());
+			} else {
+				fail("Unknown event: " + message);
+			}
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-file-api/pom.xml b/taverna-file-api/pom.xml
new file mode 100644
index 0000000..a70ca9f
--- /dev/null
+++ b/taverna-file-api/pom.xml
@@ -0,0 +1,49 @@
+<?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>taverna-file-api</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna File opening API</name>
+	<description>
+		API for doing file (ie. workflow) open/save in the workbench.
+	</description>
+
+	<dependencies>
+		<dependency>
+			<groupId>${project.groupId.version}</groupId>
+			<artifactId>taverna-ui</artifactId>
+			<version>${project.version.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-observer</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/AbstractDataflowPersistenceHandler.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/AbstractDataflowPersistenceHandler.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/AbstractDataflowPersistenceHandler.java
new file mode 100644
index 0000000..8dc34e8
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/AbstractDataflowPersistenceHandler.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * 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.file;
+
+import java.util.Collections;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+import net.sf.taverna.t2.workbench.file.exceptions.SaveException;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+public abstract class AbstractDataflowPersistenceHandler implements
+		DataflowPersistenceHandler {
+	@Override
+	public List<FileType> getOpenFileTypes() {
+		return Collections.emptyList();
+	}
+
+	@Override
+	public List<FileType> getSaveFileTypes() {
+		return Collections.emptyList();
+	}
+
+	@Override
+	public List<Class<?>> getOpenSourceTypes() {
+		return Collections.emptyList();
+	}
+
+	@Override
+	public List<Class<?>> getSaveDestinationTypes() {
+		return Collections.emptyList();
+	}
+
+	@Override
+	public DataflowInfo openDataflow(FileType fileType, Object source)
+			throws OpenException {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public DataflowInfo saveDataflow(WorkflowBundle workflowBundle, FileType fileType,
+			Object destination) throws SaveException {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public boolean wouldOverwriteDataflow(WorkflowBundle workflowBundle, FileType fileType,
+			Object destination, DataflowInfo lastDataflowInfo) {
+		throw new UnsupportedOperationException();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowInfo.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowInfo.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowInfo.java
new file mode 100644
index 0000000..c5eaa26
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowInfo.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * 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.file;
+
+import java.util.Date;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * Information about a WorkflowBundle that has been opened by the
+ * {@link FileManager}.
+ * <p>
+ * This class, or a subclass of it, is used by
+ * {@link DataflowPersistenceHandler}s to keep information about where a
+ * {@link WorkflowBundle} came from or where it was saved to.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class DataflowInfo {
+	private final FileType fileType;
+	private final WorkflowBundle worflowBundle;
+	private final Date lastModified;
+	private final Object canonicalSource;
+
+	public DataflowInfo(FileType fileType, Object canonicalSource,
+			WorkflowBundle worflowBundle, Date lastModified) {
+		this.fileType = fileType;
+		this.canonicalSource = canonicalSource;
+		this.worflowBundle = worflowBundle;
+		this.lastModified = lastModified;
+	}
+
+	public DataflowInfo(FileType fileType, Object canonicalSource,
+			WorkflowBundle worflowBundle) {
+		this(fileType, canonicalSource, worflowBundle, null);
+	}
+
+	/**
+	 * Return the canonical source of where the WorkflowBundle was opened from
+	 * or saved to.
+	 * <p>
+	 * This is not necessarily the source provided to
+	 * {@link FileManager#openDataflow(FileType, Object)} or
+	 * {@link FileManager#saveDataflow(WorkflowBundle, FileType, Object, boolean)}
+	 * , but it's canonical version.
+	 * <p>
+	 * For instance, if a WorkflowBundle was opened from a
+	 * File("relative/something.wfbundle) this canonical source would resolve
+	 * the relative path.
+	 * 
+	 * @return
+	 */
+	public Object getCanonicalSource() {
+		return canonicalSource;
+	}
+
+	/**
+	 * Return the WorkflowBundle that is open.
+	 * 
+	 * @return The open WorkflowBundle
+	 */
+	public WorkflowBundle getDataflow() {
+		return worflowBundle;
+	}
+
+	/**
+	 * Get the last modified {@link Date} of the source at the time when it was
+	 * opened/saved.
+	 * <p>
+	 * It is important that this value is checked on creation time, and not on
+	 * demand.
+	 * 
+	 * @return The {@link Date} of the source/destination's last modified
+	 *         timestamp, or <code>null</code> if unknown.
+	 */
+	public Date getLastModified() {
+		return lastModified;
+	}
+
+	/**
+	 * The {@link FileType} of this {@link WorkflowBundle} serialisation used
+	 * for opening/saving.
+	 * 
+	 * @return The {@link FileType}, for instance
+	 *         {@link net.sf.taverna.t2.workbench.file.impl.WorkflowBundleFileType}
+	 */
+	public FileType getFileType() {
+		return fileType;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowPersistenceHandler.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowPersistenceHandler.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowPersistenceHandler.java
new file mode 100644
index 0000000..f8d2ddb
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowPersistenceHandler.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * 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.file;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Collection;
+
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+import net.sf.taverna.t2.workbench.file.exceptions.SaveException;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * A handler for opening or saving {@link WorkflowBundle} from the
+ * {@link FileManager}.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public interface DataflowPersistenceHandler {
+	/**
+	 * A collection of supported file types for
+	 * {@link #openDataflow(FileType, Object)}, or an empty collection if
+	 * opening is not supported by this handler.
+	 * 
+	 * @return A collection of supported {@link FileType}s for opening.
+	 */
+	Collection<FileType> getOpenFileTypes();
+
+	/**
+	 * A collection of supported source classes for
+	 * {@link #openDataflow(FileType, Object)}, or an empty collection if
+	 * opening is not supported by this handler.
+	 * <p>
+	 * For example, a handler that supports sources opened from a {@link File}
+	 * and {@link URL} could return
+	 * <code>Arrays.asList(File.class, URL.class)</code>
+	 * 
+	 * @return A collection of supported {@link Class}es of the open source
+	 *         types.
+	 */
+	Collection<Class<?>> getOpenSourceTypes();
+
+	/**
+	 * A collection of supported destination classes for
+	 * {@link #saveDataflow(Dataflow, FileType, Object)}, or an empty collection
+	 * if saving is not supported by this handler.
+	 * <p>
+	 * For example, a handler that supports saving to destinations that are
+	 * instances of a {@link File} could return
+	 * <code>Arrays.asList(File.class)</code>
+	 * 
+	 * @return A collection of supported {{@link Class}es of the save
+	 *         destination types.
+	 */
+	Collection<Class<?>> getSaveDestinationTypes();
+
+	/**
+	 * A collection of supported file types for
+	 * {@link #saveDataflow(WorkflowBundle, FileType, Object)}, or an empty
+	 * collection if saving is not supported by this handler.
+	 * 
+	 * @return A collection of supported {@link FileType}s for saving.
+	 */
+	Collection<FileType> getSaveFileTypes();
+
+	/**
+	 * Open a dataflow from a source containing a dataflow of the given
+	 * {@link FileType}.
+	 * <p>
+	 * The {@link FileType} will be equal to one of the types from
+	 * {@link #getOpenFileTypes()}, and the source class will be one that is
+	 * assignable to one of the classes from {@link #getOpenSourceTypes()}.
+	 * 
+	 * @param fileType
+	 *            {@link FileType} determining which serialisation method has
+	 *            been used
+	 * @param source
+	 *            Source for reading the WorkflowBundle
+	 * @return {@link DataflowInfo} describing the opened WorkflowBundle,
+	 *         including the WorkflowBundle itself
+	 * @throws OpenException
+	 *             If the WorkflowBundle could not be read, parsed or opened for
+	 *             any reason.
+	 */
+	DataflowInfo openDataflow(FileType fileType, Object source)
+			throws OpenException;
+
+	/**
+	 * Save a WorkflowBundle to a destination of the given {@link FileType}.
+	 * <p>
+	 * The {@link FileType} will be equal to one of the types from
+	 * {@link #getSaveFileTypes()}, and the destination class will be one that
+	 * is assignable to one of the classes from
+	 * {@link #getSaveDestinationTypes()}.
+	 * 
+	 * @param dataflow
+	 *            {@link WorkflowBundle} to be saved
+	 * @param fileType
+	 *            {@link FileType} determining which serialisation method to use
+	 * @param destination
+	 *            Destination for writing the WorkflowBundle
+	 * @return {@link DataflowInfo} describing the saved WorkflowBundle,
+	 *         including the WorkflowBundle itself
+	 * @throws OpenException
+	 *             If the WorkflowBundle could not be read, parsed or opened for
+	 *             any reason.
+	 */
+	DataflowInfo saveDataflow(WorkflowBundle dataflow, FileType fileType,
+			Object destination) throws SaveException;
+
+	/**
+	 * Return <code>true</code> if a call to
+	 * {@link #saveDataflow(WorkflowBundle, FileType, Object)} would overwrite
+	 * the destination, and the destination is different from last
+	 * {@link #openDataflow(FileType, Object)} or
+	 * {@link #saveDataflow(WorkflowBundle, FileType, Object)} of the given
+	 * dataflow.
+	 * 
+	 * @param dataflow
+	 *            {@link WorkflowBundle} that is to be saved
+	 * @param fileType
+	 *            {@link FileType} for saving WorkflowBundle
+	 * @param destination
+	 *            destination for writing WorkflowBundle
+	 * @param lastDataflowInfo
+	 *            last provided {@link DataflowInfo} returned by
+	 *            {@link #openDataflow(FileType, Object)} or
+	 *            {@link #saveDataflow(WorkflowBundle, FileType, Object)}. (but
+	 *            not necessarily from this handler)
+	 * @return <code>true</code> if the save would overwrite
+	 */
+	boolean wouldOverwriteDataflow(WorkflowBundle dataflow, FileType fileType,
+			Object destination, DataflowInfo lastDataflowInfo);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileManager.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileManager.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileManager.java
new file mode 100644
index 0000000..f449bb5
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileManager.java
@@ -0,0 +1,573 @@
+/*******************************************************************************
+ * 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.file;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.List;
+
+import javax.swing.filechooser.FileFilter;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.file.events.OpenedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.SavedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+import net.sf.taverna.t2.workbench.file.exceptions.OverwriteException;
+import net.sf.taverna.t2.workbench.file.exceptions.SaveException;
+import net.sf.taverna.t2.workbench.file.exceptions.UnsavedException;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * Manager of open files (WorkflowBundleBundles) in the workbench.
+ * <p>
+ * A {@link WorkflowBundle} can be opened for the workbench using
+ * {@link #openDataflow(FileType, Object)} or {@link #openDataflow(WorkflowBundle)}.
+ * {@link Observer}s of the FileManager gets notified with an
+ * {@link OpenedDataflowEvent}. The opened workflow is also
+ * {@link #setCurrentDataflow(WorkflowBundle) made the current dataflow}, available
+ * through {@link #getCurrentDataflow()} or by observing the
+ * {@link net.sf.taverna.t2.lang.ui.ModelMap} for the model name
+ * {@link net.sf.taverna.t2.workbench.ModelMapConstants#CURRENT_DATAFLOW}.
+ * <p>
+ * A dataflow can be saved using
+ * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)}. Observers will be
+ * presented a {@link SavedDataflowEvent}.
+ * <p>
+ * If a dataflow was previously opened from a saveable destination or previously
+ * saved using {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)},
+ * {@link #saveDataflow(WorkflowBundle, boolean)} can be used to resave to that
+ * destination.
+ * <p>
+ * You can get the last opened/saved source and type using
+ * {@link #getDataflowSource(WorkflowBundle)} and {@link #getDataflowType(WorkflowBundle)}.
+ * <p>
+ * If the save methods are used with failOnOverwrite=true, an
+ * {@link OverwriteException} will be thrown if the destination file already
+ * exists and was not last written by a previous save on that dataflow. (This is
+ * typically checked using timestamps on the file).
+ * <p>
+ * A dataflow can be closed using {@link #closeDataflow(WorkflowBundle, boolean)}. A
+ * closed dataflow is no longer monitored for changes and can no longer be used
+ * with the other operations, except {@link #openDataflow(WorkflowBundle)}.
+ * <p>
+ * If a dataflow has been changed using the {@link EditManager},
+ * {@link #isDataflowChanged(WorkflowBundle)} will return true until the next save. If
+ * the close methods are used with failOnUnsaved=true, an
+ * {@link UnsavedException} will be thrown if the dataflow has been changed.
+ * <p>
+ * The implementation of this interface is an OSGi Service.
+ *
+ * @author Stian Soiland-Reyes
+ */
+public interface FileManager extends Observable<FileManagerEvent> {
+	/**
+	 * True if {@link #saveDataflow(WorkflowBundle, boolean)} can save the
+	 * workflow, i.e., that there exists an SPI implementation of
+	 * {@link DataflowPersistenceHandler} that can save to
+	 * {@link #getDataflowSource(WorkflowBundle)} using
+	 * {@link #getDataflowType(WorkflowBundle)}.
+	 * 
+	 * @see #saveDataflow(WorkflowBundle, boolean)
+	 * @param dataflow
+	 *            The dataflow to check
+	 * @return <code>true</code> if the given dataflow can be saved without
+	 *         providing a destination and filetype
+	 */
+	boolean canSaveWithoutDestination(WorkflowBundle dataflow);
+
+	/**
+	 * Close the specified dataflow.
+	 * <p>
+	 * A closed dataflow can no longer be used with the save methods, and will
+	 * disappear from the UI's list of open dataflows.
+	 * <p>
+	 * If no more dataflows would be open after the close, a new empty dataflow
+	 * is opened as through {@link #newDataflow()}.
+	 * <p>
+	 * If the failOnUnsaved parameters is <code>true</code>, and
+	 * {@link #isDataflowChanged(WorkflowBundle)} is <code>true</code>, an
+	 * {@link UnsavedException} will be thrown, typically because the workflow
+	 * has been changed using the {@link EditManager} since the last change.
+	 * <p>
+	 * Listeners registered using {@link Observable#addObserver(Observer)} will
+	 * be notified with an {@link ClosedDataflowEvent}.
+	 *
+	 * @param dataflow
+	 *            {@link WorkflowBundle} to close
+	 * @param failOnUnsaved
+	 *            If <code>true</code>, fail on unsaved changes
+	 * @throws UnsavedException
+	 *             If failOnUnsaved was <code>true</code> and there has been
+	 *             changes to the dataflow since the last save
+	 */
+	boolean closeDataflow(WorkflowBundle dataflow, boolean failOnUnsaved)
+			throws UnsavedException;
+
+	/**
+	 * Get the current dataflow.
+	 * <p>
+	 * The current workflow is typically the one currently showed on the screen,
+	 * and is also in {@link #getOpenDataflows()}.
+	 * <p>
+	 * The current dataflow is set through {@link #setCurrentDataflow(WorkflowBundle)}
+	 * or the {@link net.sf.taverna.t2.lang.ui.ModelMap} using the key
+	 * {@link net.sf.taverna.t2.workbench.ModelMapConstants#CURRENT_DATAFLOW}.
+	 *
+	 * @return The current dataflow, or <code>null</code> if no dataflow is
+	 *         current
+	 */
+	WorkflowBundle getCurrentDataflow();
+
+	/**
+	 * Get the dataflow that was opened from or last saved to the given source.
+	 *
+	 * @param source
+	 *            The source as opened with or saved to
+	 *            {@link #openDataflow(FileType, Object)}
+	 * @return The opened {@link WorkflowBundle} or <code>null</code> if no matching
+	 *         dataflow found.
+	 */
+	WorkflowBundle getDataflowBySource(Object source);
+
+	/**
+	 * Get a name to represent this dataflow.
+	 * <p>
+	 * The name will primarily be deduced from the source of where the workflow
+	 * is opened from, unless {@link Object#toString()} is not overridden (for
+	 * instance opened from an InputStream) or if the source is unknown, in
+	 * which case the dataflow's internal name {@link WorkflowBundle#getName()}
+	 * is returned.
+	 * <p>
+	 * The returned name can be used in listings like the WorkflowBundles menu, but is
+	 * not guaranteed to be unique. (For instance a workflow could be opened
+	 * twice from the same source).
+	 *
+	 * @param dataflow
+	 *            WorkflowBundle to get the name for
+	 * @return The deduced workflow name
+	 */
+	String getDataflowName(WorkflowBundle dataflow);
+
+	/**
+	 * Returns the default name to use when creating new workflows.
+	 *
+	 * @return the default name to use when creating new workflows
+	 */
+	String getDefaultWorkflowName();
+
+	/**
+	 * Get the last opened/saved source/destination for the given dataflow.
+	 * <p>
+	 * The source is the last source used with
+	 * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)} for the given
+	 * dataflow, or {@link #openDataflow(FileType, Object)} if it has not yet
+	 * been saved.
+	 * <p>
+	 * If the given dataflow's last opened/saved location was unknown (opened
+	 * with {@link #newDataflow()} or {@link #openDataflow(WorkflowBundle)}), return
+	 * <code>null</code>.
+	 *
+	 * @param dataflow
+	 *            {@link WorkflowBundle} which file is to be returned
+	 * @return The last opened/saved source for the given dataflow, or
+	 *         <code>null</code> if unknown.
+	 */
+	Object getDataflowSource(WorkflowBundle dataflow);
+
+	/**
+	 * Get the last opened/saved source/destination FileType for the given
+	 * dataflow.
+	 * <p>
+	 * The type is the last {@link FileType} used with
+	 * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)} for the given
+	 * dataflow, or {@link #openDataflow(FileType, Object)} if it has not yet
+	 * been saved.
+	 * <p>
+	 * If the given dataflow's last opened/saved file type was unknown (opened
+	 * with {@link #newDataflow()} or {@link #openDataflow(WorkflowBundle)}), return
+	 * <code>null</code>.
+	 *
+	 * @param dataflow
+	 *            {@link WorkflowBundle} which file is to be returned
+	 * @return The last opened/saved {@link FileType} for the given dataflow, or
+	 *         <code>null</code> if unknown.
+	 */
+	FileType getDataflowType(WorkflowBundle dataflow);
+
+	/**
+	 * Get the list of currently open dataflows. This list of dataflows are
+	 * typically displayed in the UI in the "WorkflowBundles" menu to allow switching
+	 * the {@link #getCurrentDataflow() current dataflow}.
+	 *
+	 * @return A copy of the {@link List} of open {@link WorkflowBundle}s
+	 */
+	List<WorkflowBundle> getOpenDataflows();
+
+	/**
+	 * Get a list of {@link FileFilter}s for supported {@link FileType}s that
+	 * can be opened with any source class.
+	 *
+	 * @return A {@link List} of {@link FileFilter}s supported by
+	 *         {@link #openDataflow(FileType, Object)}
+	 */
+	List<FileFilter> getOpenFileFilters();
+
+	/**
+	 * Get a list of {@link FileFilter}s for supported {@link FileType}s that
+	 * can be opened with given source class.
+	 *
+	 * @param sourceClass
+	 *            Source class that can be opened from
+	 * @return A {@link List} of {@link FileFilter}s supported by
+	 *         {@link #openDataflow(FileType, Object)}
+	 */
+	List<FileFilter> getOpenFileFilters(Class<?> sourceClass);
+
+	/**
+	 * Get a list of {@link FileFilter}s for supported {@link FileType}s that
+	 * can be saved to any destination class.
+	 *
+	 * @return A {@link List} of {@link FileFilter}s supported by
+	 *         {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)}
+	 */
+	List<FileFilter> getSaveFileFilters();
+
+	/**
+	 * Get a list of {@link FileFilter}s for supported {@link FileType}s that
+	 * can be saved to the given destination class.
+	 *
+	 * @param destinationClass
+	 *            Destination class that can be saved to
+	 * @return A {@link List} of {@link FileFilter}s supported by
+	 *         {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)}
+	 */
+	List<FileFilter> getSaveFileFilters(Class<?> destinationClass);
+
+	/**
+	 * Return <code>true</code> if the dataflow has been changed (through the
+	 * {@link EditManager} or {@link #setDataflowChanged(WorkflowBundle, boolean)})
+	 * since last save.
+	 *
+	 * @param dataflow
+	 *            WorkflowBundle which changed status is to be checked
+	 * @return <code>true</code> if the dataflow has been changed since last
+	 *         save.
+	 */
+	boolean isDataflowChanged(WorkflowBundle dataflow);
+
+	/**
+	 * True if the given dataflow has been opened and is in
+	 * {@link #getOpenDataflows()}.
+	 *
+	 * @param dataflow
+	 *            Dataflow to check
+	 * @return <code>true</code> if dataflow is open
+	 */
+	boolean isDataflowOpen(WorkflowBundle dataflow);
+
+	/**
+	 * Create and open a new, blank dataflow. The dataflow will not initially be
+	 * marked as changed.
+	 * <p>
+	 * Listeners registered using {@link Observable#addObserver(Observer)} will
+	 * be notified with an {@link OpenedDataflowEvent}.
+	 * <p>
+	 * Note, if the dataflow is later changed, it will not be possible to save
+	 * it to any original location using
+	 * {@link #saveDataflow(WorkflowBundle, boolean)}, only
+	 * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)}.
+	 *
+	 * @return The newly opened blank {@link WorkflowBundle}
+	 */
+	WorkflowBundle newDataflow();
+
+	/**
+	 * Open a {@link WorkflowBundle} instance that has been created outside the
+	 * {@link FileManager}. The dataflow will not initially be marked as
+	 * changed.
+	 * <p>
+	 * Listeners registered using {@link Observable#addObserver(Observer)} will
+	 * be notified with an {@link OpenedDataflowEvent}.
+	 * <p>
+	 * Note, if the dataflow is later changed, it will not be possible to save
+	 * it to its original location using
+	 * {@link #saveDataflow(WorkflowBundle, boolean)}, only
+	 * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)}.
+	 * <p>
+	 * Instead of using this option it is recommended to create your own
+	 * {@link FileType} and/or source type and a
+	 * {@link DataflowPersistenceHandler} to implement save and/or reopen
+	 * (revert).
+	 * <p>
+	 * If there is only one workflow open before opening this workflow, and it
+	 * is an unchanged blank workflow, the blank workflow will be closed.
+	 *
+	 * @param dataflow
+	 *            {@link WorkflowBundle} instance that is to be added as an open
+	 *            dataflow
+	 */
+	void openDataflow(WorkflowBundle dataflow);
+
+	/**
+	 * Open a dataflow from a source. The dataflow will not initially be marked
+	 * as changed, and will be set as the new current workflow.
+	 * <p>
+	 * The file manager will find implementations of the SPI
+	 * {@link DataflowPersistenceHandler} to perform the opening for the given file
+	 * type and destination class.
+	 * <p>
+	 * Listeners registered using {@link Observable#addObserver(Observer)} will
+	 * be notified with an {@link OpenedDataflowEvent}.
+	 * <p>
+	 * If there is only one workflow open before opening this workflow, and it
+	 * is an unchanged blank workflow, the blank workflow will be closed.
+	 *
+	 * @param fileType
+	 *            The filetype, for instance
+	 *            {@link net.sf.taverna.t2.workbench.file.impl.T2FlowFileType}.
+	 *            The file type must be supported by an implementation of the
+	 *            SPI DataflowPersistenceHandler.
+	 * @param source
+	 *            The source, for instance a {@link File} or {@link URL}. The
+	 *            source type must be supported by an implementation of
+	 *            DataflowPersistenceHandler.
+	 * @return The opened {@link WorkflowBundle}.
+	 * @throws OpenException
+	 *             If there was no matching DataflowPersistenceHandler found or
+	 *             the source could not be opened for any other reason, such as
+	 *             IO errors or syntax errors.
+	 */
+	WorkflowBundle openDataflow(FileType fileType, Object source)
+			throws OpenException;
+
+	/**
+	 * Open a dataflow from a source silently. The dataflow will not be listed
+	 * as open, and will not be made the current workflow.
+	 * <p>
+	 * The file manager will find implementations of the SPI
+	 * {@link DataflowPersistenceHandler} to perform the opening for the given file
+	 * type and destination class.
+	 * <p>
+	 * Listeners will <strong>not</strong> be notified.
+	 *
+	 * @param fileType
+	 *            The filetype, for instance
+	 *            {@link net.sf.taverna.t2.workbench.file.impl.T2FlowFileType}.
+	 *            The file type must be supported by an implementation of the
+	 *            SPI DataflowPersistenceHandler.
+	 * @param source
+	 *            The source, for instance a {@link File} or {@link URL}. The
+	 *            source type must be supported by an implementation of
+	 *            DataflowPersistenceHandler.
+	 * @return The {@link DataflowInfo} describing the opened dataflow.
+	 * @throws OpenException
+	 *             If there was no matching DataflowPersistenceHandler found or
+	 *             the source could not be opened for any other reason, such as
+	 *             IO errors or syntax errors.
+	 */
+	DataflowInfo openDataflowSilently(FileType fileType, Object source)
+			throws OpenException;
+
+	/**
+	 * Save the dataflow to the last saved destination and FileType from
+	 * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)} or the last
+	 * opened source and FileType from {@link #openDataflow(FileType, Object)}.
+	 * <p>
+	 * Listeners registered using {@link Observable#addObserver(Observer)} will
+	 * be notified with an {@link SavedDataflowEvent}.
+	 *
+	 * @param dataflow
+	 *            Dataflow to save. Dataflow must have been opened with
+	 *            {@link #openDataflow(FileType, Object)} or saved using
+	 *            {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)}.
+	 * @param failOnOverwrite
+	 *            If <code>true</code>, an {@link OverwriteException} is thrown
+	 *            if a save would overwrite the destination because it has been
+	 *            changed since last open/save.
+	 * @throws OverwriteException
+	 *             if failOnOverwrite was true, and a save would overwrite the
+	 *             destination because it has been changed since last open/save.
+	 *             The save was not performed.
+	 * @throws SaveException
+	 *             If any other error occurs during saving, including the case
+	 *             that a dataflow is not connected to a source or destination,
+	 *             that there are no handlers (some source types can't be saved
+	 *             to, such as HTTP URLs), or any other IO error occurring while
+	 *             saving.
+	 */
+	void saveDataflow(WorkflowBundle dataflow, boolean failOnOverwrite)
+			throws SaveException, OverwriteException;
+
+	/**
+	 * Save the dataflow to the given destination using the given filetype.
+	 * <p>
+	 * The file manager will find implementations of the SPI
+	 * {@link DataflowPersistenceHandler} to perform the save for the given file
+	 * type and destination class.
+	 * <p>
+	 * Listeners registered using {@link Observable#addObserver(Observer)} will
+	 * be notified with an {@link SavedDataflowEvent}.
+	 *
+	 * @param dataflow
+	 *            {@link Dataflow} to be saved
+	 * @param fileType
+	 *            {@link FileType} to save dataflow as, for instance
+	 *            {@link net.sf.taverna.t2.workbench.file.impl.T2FlowFileType}.
+	 *            The file type must be supported by an SPI implementation of
+	 *            {@link DataflowPersistenceHandler}.
+	 * @param destination
+	 *            Destination to save dataflow to, for instance a {@link File}
+	 * @param failOnOverwrite
+	 *            If <code>true</code>, an {@link OverwriteException} is thrown
+	 *            if a save would overwrite the destination because it already
+	 *            exists, but was not opened or save to using the file manager
+	 *            for the given dataflow. (ie. a repeated call to this function
+	 *            should not throw an OverwriteException unless someone outside
+	 *            has modified the file)
+	 * @throws OverwriteException
+	 *             if failOnOverwrite was true, and a save would overwrite the
+	 *             destination because it already existed, and was not last
+	 *             written to by a previous save. The save was not performed.
+	 * @throws SaveException
+	 *             If any other error occurs during saving, including the case
+	 *             that a dataflow is not connected to a source or destination,
+	 *             that there are no handlers (some source types can't be saved
+	 *             to, such as HTTP URLs), or any other IO error occurring while
+	 *             saving.
+	 */
+	void saveDataflow(WorkflowBundle dataflow, FileType fileType,
+			Object destination, boolean failOnOverwrite) throws SaveException,
+			OverwriteException;
+
+	/**
+	 * Silently save the dataflow to the given destination using the given
+	 * filetype.
+	 * <p>
+	 * The file manager will find implementations of the SPI
+	 * {@link DataflowPersistenceHandler} to perform the save for the given file
+	 * type and destination class.
+	 * <p>
+	 * Listeners will <strong>not</strong> be notified, and the dataflow does
+	 * not previously have to be opened. getDataflowSource(),
+	 * isDataflowChanged() etc will not be affected - as if the silent save
+	 * never happened.
+	 * 
+	 * @param dataflow
+	 *            {@link WorkflowBundle} to be saved
+	 * @param fileType
+	 *            {@link FileType} to save dataflow as, for instance
+	 *            {@link net.sf.taverna.t2.workbench.file.impl.T2FlowFileType}.
+	 *            The file type must be supported by an SPI implementation of
+	 *            {@link DataflowPersistenceHandler}.
+	 * @param destination
+	 *            Destination to save dataflow to, for instance a {@link File}
+	 * @param failOnOverwrite
+	 *            If <code>true</code>, an {@link OverwriteException} is thrown
+	 *            if a save would overwrite the destination because it already
+	 *            exists, but was not opened or save to using the file manager
+	 *            for the given dataflow. (ie. a repeated call to this function
+	 *            should not throw an OverwriteException unless someone outside
+	 *            has modified the file)
+	 * @return The {@link DataflowInfo} describing where the workflow was saved
+	 * @throws OverwriteException
+	 *             if failOnOverwrite was true, and a save would overwrite the
+	 *             destination because it already existed, and was not last
+	 *             written to by a previous save. The save was not performed.
+	 * @throws SaveException
+	 *             If any other error occurs during saving, including the case
+	 *             that a dataflow is not connected to a source or destination,
+	 *             that there are no handlers (some source types can't be saved
+	 *             to, such as HTTP URLs), or any other IO error occurring while
+	 *             saving.
+	 */
+	DataflowInfo saveDataflowSilently(WorkflowBundle dataflow, FileType fileType,
+			Object destination, boolean failOnOverwrite) throws SaveException,
+			OverwriteException;
+
+	/**
+	 * Set the current dataflow to the one provided.
+	 * <p>
+	 * The current dataflow can be retrieved using {@link #getCurrentDataflow()}
+	 * . Note that opening a dataflow will normally also set it as the current
+	 * dataflow.
+	 * <p>
+	 * Listeners registered using {@link Observable#addObserver(Observer)} will
+	 * be notified with an {@link SetCurrentDataflowEvent}.
+	 * <p>
+	 * Note, the dataflow must already be open. If this is not the case, use one
+	 * of the openDataflow() methods or
+	 * {@link #setCurrentDataflow(WorkflowBundle, boolean)}.
+	 *
+	 * @see #setCurrentDataflow(WorkflowBundle, boolean)
+	 * @param dataflow
+	 *            {@link WorkflowBundle} to be made current
+	 */
+	void setCurrentDataflow(WorkflowBundle dataflow);
+
+	/**
+	 * Set the current dataflow to the one provided.
+	 * <p>
+	 * The current dataflow can be retrieved using {@link #getCurrentDataflow()}
+	 * . Note that opening a dataflow will normally also set it as the current
+	 * dataflow.
+	 * <p>
+	 * Listeners registered using {@link Observable#addObserver(Observer)} will
+	 * be notified with an {@link SetCurrentDataflowEvent}.
+	 * <p>
+	 * Unless <code>openIfNeeded</code> is <code>true</code>, the dataflow must
+	 * already be open.
+	 *
+	 * @see #setCurrentDataflow(WorkflowBundle, boolean)
+	 * @param dataflow
+	 *            {@link WorkflowBundle} to be made current
+	 * @param openIfNeeded
+	 *            If <code>true</code>, open the dataflow if needed
+	 */
+	void setCurrentDataflow(WorkflowBundle dataflow, boolean openIfNeeded);
+
+	/**
+	 * Set a dataflow as changed or not. This changes the value returned by
+	 * {@link #isDataflowChanged(WorkflowBundle)}.
+	 * <p>
+	 * This method can be used if the dataflow has been changed outside the
+	 * {@link EditManager}.
+	 *
+	 * @param dataflow
+	 *            Dataflow which is to be marked
+	 * @param isChanged
+	 *            <code>true</code> if the dataflow is to be marked as changed,
+	 *            <code>false</code> if it is to be marked as not changed.
+	 */
+	void setDataflowChanged(WorkflowBundle dataflow, boolean isChanged);
+
+	/**
+	 * Returns the canonical form of the source where the dataflow was opened
+	 * from or saved to. The code for this method was devised based on
+	 * {@link net.sf.taverna.t2.workbench.file.impl.T2DataflowOpener#openDataflow(FileType fileType, Object source)}.
+	 */
+	Object getCanonical(Object source) throws IllegalArgumentException,
+			URISyntaxException, IOException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileType.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileType.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileType.java
new file mode 100644
index 0000000..001d82c
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileType.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * 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.file;
+
+/**
+ * A filetype to identify a way to (de)serialise a {@link WorkflowBundle} with
+ * the {@link FileManager}.
+ * <p>
+ * Two filetypes are considered equal if they share an extension or mime type or
+ * are the same instance.
+ * 
+ * @see net.sf.taverna.t2.workbench.file.impl.WorkflowBundleFileType
+ * @author Stian Soiland-Reyes
+ */
+public abstract class FileType {
+	public abstract String getExtension();
+
+	public abstract String getMimeType();
+
+	public abstract String getDescription();
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (!(obj instanceof FileType))
+			return false;
+		FileType other = (FileType) obj;
+		if (getMimeType() != null && other.getMimeType() != null)
+			return getMimeType().equalsIgnoreCase(other.getMimeType());
+		if (getExtension() != null && other.getExtension() != null)
+			return getExtension().equalsIgnoreCase(other.getExtension());
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		int hash = 7;
+		hash = 31 * hash + getExtension().hashCode();
+		hash = 31 * hash + getMimeType().hashCode();
+		return hash;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/AbstractDataflowEvent.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/AbstractDataflowEvent.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/AbstractDataflowEvent.java
new file mode 100644
index 0000000..942e097
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/AbstractDataflowEvent.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.file.events;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * Abstract FileManagerEvent that relates to a {@link WorkflowBundle}
+ * 
+ * @see AbstractDataflowEvent
+ * @see ClosedDataflowEvent
+ * @see OpenedDataflowEvent
+ * @see SavedDataflowEvent
+ * @see SetCurrentDataflowEvent
+ * @author Stian Soiland-Reyes
+ */
+public abstract class AbstractDataflowEvent extends FileManagerEvent {
+	private final WorkflowBundle workflowBundle;
+
+	public AbstractDataflowEvent(WorkflowBundle workflowBundle) {
+		this.workflowBundle = workflowBundle;
+	}
+
+	public WorkflowBundle getDataflow() {
+		return workflowBundle;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosedDataflowEvent.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosedDataflowEvent.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosedDataflowEvent.java
new file mode 100644
index 0000000..5e7e884
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosedDataflowEvent.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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.file.events;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * {@link FileManagerEvent} that means a {@link WorkflowBundle} has been closed.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class ClosedDataflowEvent extends AbstractDataflowEvent {
+	public ClosedDataflowEvent(WorkflowBundle workflowBundle) {
+		super(workflowBundle);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosingDataflowEvent.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosingDataflowEvent.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosingDataflowEvent.java
new file mode 100644
index 0000000..094ea10
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosingDataflowEvent.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.file.events;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * {@link FileManagerEvent} that means a {@link WorkflowBundle} is being closed.
+ * <i>This event is abortable;</i> if aborted, the close will not occur.
+ * 
+ * @author Alan R Williams
+ */
+public class ClosingDataflowEvent extends AbstractDataflowEvent {
+	private boolean abortClose = false;
+
+	public boolean isAbortClose() {
+		return abortClose;
+	}
+
+	public void setAbortClose(boolean abortClose) {
+		this.abortClose = abortClose;
+	}
+
+	public ClosingDataflowEvent(WorkflowBundle workflowBundle) {
+		super(workflowBundle);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/FileManagerEvent.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/FileManagerEvent.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/FileManagerEvent.java
new file mode 100644
index 0000000..84c3886
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/FileManagerEvent.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.file.events;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.workbench.file.FileManager;
+
+/**
+ * An event given to {@link FileManager} observers registered using
+ * {@link Observable#addObserver(net.sf.taverna.t2.lang.observer.Observer)}.
+ * 
+ * @see AbstractDataflowEvent
+ * @see ClosedDataflowEvent
+ * @see OpenedDataflowEvent
+ * @see SavedDataflowEvent
+ * @see SetCurrentDataflowEvent
+ * @author Stian Soiland-Reyes
+ */
+public class FileManagerEvent {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/OpenedDataflowEvent.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/OpenedDataflowEvent.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/OpenedDataflowEvent.java
new file mode 100644
index 0000000..b479a83
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/OpenedDataflowEvent.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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.file.events;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * {@link FileManagerEvent} that means a dataflow has been opened
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class OpenedDataflowEvent extends AbstractDataflowEvent {
+	public OpenedDataflowEvent(WorkflowBundle workflowBundle) {
+		super(workflowBundle);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SavedDataflowEvent.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SavedDataflowEvent.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SavedDataflowEvent.java
new file mode 100644
index 0000000..f0f22ae
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SavedDataflowEvent.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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.file.events;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * {@link FileManagerEvent} that means a {@link WorkflowBundle} has been saved.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class SavedDataflowEvent extends AbstractDataflowEvent {
+	public SavedDataflowEvent(WorkflowBundle workflowBundle) {
+		super(workflowBundle);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SetCurrentDataflowEvent.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SetCurrentDataflowEvent.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SetCurrentDataflowEvent.java
new file mode 100644
index 0000000..ff44cf2
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SetCurrentDataflowEvent.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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.file.events;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * {@link FileManagerEvent} that means a {@link WorkflowBundle} has been made
+ * current.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class SetCurrentDataflowEvent extends AbstractDataflowEvent {
+	public SetCurrentDataflowEvent(WorkflowBundle workflowBundle) {
+		super(workflowBundle);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/FileException.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/FileException.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/FileException.java
new file mode 100644
index 0000000..a2cb9ce
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/FileException.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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.file.exceptions;
+
+import net.sf.taverna.t2.workbench.file.FileManager;
+
+/**
+ * Superclass of exceptions thrown by the {@link FileManager}.
+ */
+@SuppressWarnings("serial")
+public class FileException extends Exception {
+	public FileException() {
+	}
+
+	public FileException(String message) {
+		super(message);
+	}
+
+	public FileException(Throwable cause) {
+		super(cause);
+	}
+
+	public FileException(String message, Throwable cause) {
+		super(message, cause);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OpenException.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OpenException.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OpenException.java
new file mode 100644
index 0000000..057679b
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OpenException.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.file.exceptions;
+
+/** Indicate that something went wrong during opening a file */
+@SuppressWarnings("serial")
+public class OpenException extends FileException {
+	public OpenException() {
+	}
+
+	public OpenException(String message) {
+		super(message);
+	}
+
+	public OpenException(Throwable cause) {
+		super(cause);
+	}
+
+	public OpenException(String message, Throwable cause) {
+		super(message, cause);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OverwriteException.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OverwriteException.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OverwriteException.java
new file mode 100644
index 0000000..6d410a3
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OverwriteException.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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.file.exceptions;
+
+/** Indicate that something could not be overwritten. */
+@SuppressWarnings("serial")
+public class OverwriteException extends SaveException {
+	private final Object destination;
+
+	public OverwriteException(Object destination) {
+		super("Save would overwrite existing destination " + destination);
+		this.destination = destination;
+	}
+
+	public Object getDestination() {
+		return destination;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/SaveException.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/SaveException.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/SaveException.java
new file mode 100644
index 0000000..8c4266f
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/SaveException.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.file.exceptions;
+
+/** Indicate that a workflow could not be saved. */
+@SuppressWarnings("serial")
+public class SaveException extends FileException {
+	public SaveException() {
+	}
+
+	public SaveException(String message) {
+		super(message);
+	}
+
+	public SaveException(Throwable cause) {
+		super(cause);
+	}
+
+	public SaveException(String message, Throwable cause) {
+		super(message, cause);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/UnsavedException.java
----------------------------------------------------------------------
diff --git a/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/UnsavedException.java b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/UnsavedException.java
new file mode 100644
index 0000000..41c01f8
--- /dev/null
+++ b/taverna-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/UnsavedException.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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.file.exceptions;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/** Indicate that a workflow bundle is not saved. */
+@SuppressWarnings("serial")
+public class UnsavedException extends FileException {
+	private final WorkflowBundle workflowBundle;
+
+	public UnsavedException(WorkflowBundle workflowBundle) {
+		super("WorkflowBundle was not saved: " + workflowBundle);
+		this.workflowBundle = workflowBundle;
+	}
+
+	public WorkflowBundle getDataflow() {
+		return workflowBundle;
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/xsd/schema-v1.xsd
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/xsd/schema-v1.xsd b/taverna-perspective-biocatalogue/src/main/xsd/schema-v1.xsd
new file mode 100644
index 0000000..6e57dd1
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/xsd/schema-v1.xsd
@@ -0,0 +1,3557 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema targetNamespace="http://www.biocatalogue.org/2009/xml/rest"
+	xml:lang="en" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.biocatalogue.org/2009/xml/rest"
+	elementFormDefault="qualified" xmlns:xlink="http://www.w3.org/1999/xlink"
+	xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/">
+
+	<xsd:import namespace="http://purl.org/dc/elements/1.1/"
+		schemaLocation="dc.xsd" />
+	<xsd:import namespace="http://purl.org/dc/terms/"
+		schemaLocation="dcterms.xsd" />
+	<xsd:import namespace="http://www.w3.org/1999/xlink"
+		schemaLocation="xlink.xsd" />
+
+	<xsd:simpleType name="SearchScopeUrlValue">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="all"></xsd:enumeration>
+			<xsd:enumeration value="service_providers"></xsd:enumeration>
+
+			<xsd:enumeration value="services"></xsd:enumeration>
+			<xsd:enumeration value="users"></xsd:enumeration>
+			<xsd:enumeration value="registries"></xsd:enumeration>
+			<xsd:enumeration value="soap_operations"></xsd:enumeration>
+			<xsd:enumeration value="rest_methods"></xsd:enumeration>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:element name="search" type="Search"></xsd:element>
+
+	<xsd:complexType name="Search">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" type="SearchParameters"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="statistics" type="SearchStatistics"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="results" type="SearchResults"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" type="SearchRelatedLinks"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="SearchParameters">
+		<xsd:sequence>
+
+			<xsd:element name="query" type="SearchQueryParameter"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="scope" type="SearchScopeParameter"
+				maxOccurs="unbounded" minOccurs="1">
+			</xsd:element>
+			<xsd:element ref="page" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="pageSize" maxOccurs="1" minOccurs="1"></xsd:element>
+
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="SearchStatistics">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreStatistics">
+				<xsd:sequence>
+					<xsd:element name="scopedResults" minOccurs="0"
+						maxOccurs="unbounded">
+						<xsd:complexType>
+
+							<xsd:simpleContent>
+								<xsd:extension base="xsd:nonNegativeInteger">
+									<xsd:attribute name="scope" type="SearchScopeName">
+									</xsd:attribute>
+								</xsd:extension>
+							</xsd:simpleContent>
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:element name="biocatalogue" type="BioCatalogue" />
+
+	<xsd:complexType name="BioCatalogue">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+
+					<xsd:element name="documentation" maxOccurs="1"
+						minOccurs="1" type="ResourceLink">
+					</xsd:element>
+					<xsd:element name="collections" maxOccurs="1"
+						minOccurs="1">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="agents"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="annotationAttributes"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+
+								<xsd:element name="annotations"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="categories"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="registries"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="restMethods"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="restResources"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+
+								</xsd:element>
+								<xsd:element name="restServices"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="search"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="serviceProviders"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="services"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+
+								<xsd:element name="soapOperations"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="soapServices"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="tags"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="testResults"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="users"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+
+								</xsd:element>
+								<xsd:element name="filters"
+									maxOccurs="1" minOccurs="1">
+									<xsd:complexType>
+										<xsd:sequence>
+											<xsd:element
+												name="annotations" type="ResourceLink" maxOccurs="1"
+												minOccurs="1">
+											</xsd:element>
+											<xsd:element
+												name="restMethods" type="ResourceLink" maxOccurs="1"
+												minOccurs="1">
+											</xsd:element>
+											<xsd:element name="services"
+												type="ResourceLink" maxOccurs="1" minOccurs="1">
+
+											</xsd:element>
+											<xsd:element
+												name="soapOperations" type="ResourceLink" maxOccurs="1"
+												minOccurs="1">
+											</xsd:element>
+										</xsd:sequence>
+									</xsd:complexType>
+								</xsd:element>
+
+							</xsd:sequence>
+						</xsd:complexType>
+
+					</xsd:element>
+				</xsd:sequence>
+				<xsd:attribute name="version" type="xsd:string"
+					use="required">
+				</xsd:attribute>
+				<xsd:attribute name="apiVersion" type="xsd:string" use="required"></xsd:attribute>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="SearchRelatedLinks">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreRelatedLinks">
+				<xsd:sequence>
+					<xsd:element name="searches" minOccurs="1" maxOccurs="1">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="scoped" type="ScopedSearch"
+									minOccurs="0" maxOccurs="unbounded">
+								</xsd:element>
+
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="CollectionCoreRelatedLinks">
+
+		<xsd:sequence>
+			<xsd:element name="previous" type="ResourceLink"
+				maxOccurs="1" minOccurs="0">
+			</xsd:element>
+			<xsd:element name="next" type="ResourceLink" maxOccurs="1"
+				minOccurs="0">
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="ResourceLink">
+
+		<xsd:attribute ref="xlink:href" use="required"></xsd:attribute>
+		<xsd:attribute ref="xlink:title" use="optional"></xsd:attribute>
+		<xsd:attribute name="resourceType" type="ResourceType"
+			use="optional"></xsd:attribute>
+		<xsd:attribute name="resourceName" type="xsd:string"
+			use="optional">
+		</xsd:attribute>
+	</xsd:complexType>
+
+
+	<xsd:complexType name="ScopedSearch">
+		<xsd:complexContent>
+
+			<xsd:extension base="ResourceLink">
+				<xsd:attribute name="scope" type="SearchScopeName"></xsd:attribute>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:simpleType name="SearchScopeName">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="All"></xsd:enumeration>
+
+			<xsd:enumeration value="Services"></xsd:enumeration>
+			<xsd:enumeration value="Service Providers"></xsd:enumeration>
+			<xsd:enumeration value="Users"></xsd:enumeration>
+			<xsd:enumeration value="Registries"></xsd:enumeration>
+			<xsd:enumeration value="SOAP Operations"></xsd:enumeration>
+			<xsd:enumeration value="REST Endpoints"></xsd:enumeration>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:complexType name="SearchScopeParameter">
+		<xsd:simpleContent>
+			<xsd:extension base="SearchScopeName">
+				<xsd:attribute name="urlKey" type="xsd:string"></xsd:attribute>
+				<xsd:attribute name="urlValue" type="SearchScopeUrlValue"
+					use="required">
+				</xsd:attribute>
+			</xsd:extension>
+		</xsd:simpleContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="SearchResults">
+		<xsd:sequence>
+			<xsd:choice maxOccurs="unbounded" minOccurs="0">
+				<xsd:element name="service" type="Service"
+					maxOccurs="unbounded" minOccurs="0">
+				</xsd:element>
+				<xsd:element name="soapOperation" type="SoapOperation"
+					maxOccurs="unbounded" minOccurs="0">
+				</xsd:element>
+				<xsd:element name="serviceProvider"
+					type="ServiceProvider" maxOccurs="unbounded" minOccurs="0">
+
+				</xsd:element>
+				<xsd:element name="user" type="User"
+					maxOccurs="unbounded" minOccurs="0">
+				</xsd:element>
+				<xsd:element name="registry" type="Registry"
+					maxOccurs="unbounded" minOccurs="0">
+				</xsd:element>
+				<xsd:element name="restMethod" type="RestMethod"
+					maxOccurs="unbounded" minOccurs="0">
+				</xsd:element>
+			</xsd:choice>
+
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:element name="service" type="Service"></xsd:element>
+
+	<xsd:complexType name="Service">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element ref="dc:title" maxOccurs="1"
+						minOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="name" type="xsd:string"
+						minOccurs="1" maxOccurs="1">
+					</xsd:element>
+					<xsd:element name="originalSubmitter"
+						type="ResourceLink" minOccurs="1" maxOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dc:description" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="serviceTechnologyTypes"
+						maxOccurs="1" minOccurs="1">
+						<xsd:complexType>
+
+							<xsd:sequence>
+								<xsd:element name="type"
+									type="ServiceTechnologyType" maxOccurs="unbounded"
+									minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="latestMonitoringStatus"
+						type="MonitoringStatus" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dcterms:created" minOccurs="1"
+						maxOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="archived" type="xsd:dateTime"
+						maxOccurs="1" minOccurs="0">
+					</xsd:element>
+					<xsd:element name="summary" minOccurs="0"
+						maxOccurs="1" type="ServiceSummary">
+					</xsd:element>
+					<xsd:element name="deployments" minOccurs="0"
+						maxOccurs="1">
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="ResourceLink">
+
+									<xsd:sequence>
+										<xsd:element
+											name="serviceDeployment" type="ServiceDeployment"
+											minOccurs="1" maxOccurs="unbounded">
+										</xsd:element>
+									</xsd:sequence>
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="variants" minOccurs="0"
+						maxOccurs="1">
+
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="ResourceLink">
+									<xsd:sequence>
+										<xsd:choice
+											maxOccurs="unbounded" minOccurs="1">
+											<xsd:element
+												name="soapService" type="SoapService" maxOccurs="unbounded"
+												minOccurs="0">
+											</xsd:element>
+											<xsd:element
+												name="restService" type="RestService" maxOccurs="unbounded"
+												minOccurs="0">
+											</xsd:element>
+
+										</xsd:choice>
+									</xsd:sequence>
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="monitoring" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:complexContent>
+
+								<xsd:extension base="ResourceLink">
+									<xsd:sequence>
+										<xsd:element name="tests"
+											maxOccurs="1" minOccurs="1">
+											<xsd:complexType>
+												<xsd:sequence>
+													<xsd:element
+														name="serviceTest" type="ServiceTest"
+														maxOccurs="unbounded" minOccurs="0">
+													</xsd:element>
+												</xsd:sequence>
+											</xsd:complexType>
+
+										</xsd:element>
+									</xsd:sequence>
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="related"
+						type="ServiceRelatedLinks" minOccurs="0" maxOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="ServiceSummary">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="counts" minOccurs="1" maxOccurs="1">
+
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="deployments" type="xsd:positiveInteger"
+									minOccurs="1" maxOccurs="1">
+								</xsd:element>
+								<xsd:element name="variants" type="xsd:positiveInteger"
+									minOccurs="1" maxOccurs="1">
+								</xsd:element>
+								<xsd:element name="metadata" type="MetadataCount"
+									minOccurs="0" maxOccurs="unbounded">
+								</xsd:element>
+								<xsd:element name="favourites" type="xsd:nonNegativeInteger"
+									maxOccurs="1" minOccurs="1">
+
+								</xsd:element>
+								<xsd:element name="views" type="xsd:nonNegativeInteger"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="alternativeName" minOccurs="0"
+						maxOccurs="unbounded" type="xsd:string">
+					</xsd:element>
+					<xsd:element name="category" minOccurs="0" maxOccurs="unbounded"
+						type="ResourceLinkWithString">
+
+					</xsd:element>
+					<xsd:element name="provider" minOccurs="1" maxOccurs="unbounded">
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="ResourceLink">
+									<xsd:sequence>
+										<xsd:element name="name" type="xsd:string"
+											maxOccurs="1" minOccurs="1">
+										</xsd:element>
+									</xsd:sequence>
+
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="endpoint" minOccurs="1" maxOccurs="unbounded"
+						type="xsd:anyURI">
+					</xsd:element>
+					<xsd:element name="wsdl" minOccurs="0" maxOccurs="unbounded"
+						type="xsd:anyURI">
+					</xsd:element>
+					<xsd:element name="location" minOccurs="0" maxOccurs="unbounded"
+						type="Location">
+
+					</xsd:element>
+					<xsd:element name="documentationUrl" maxOccurs="unbounded"
+						minOccurs="0" type="xsd:string">
+					</xsd:element>
+					<xsd:element ref="dc:description" maxOccurs="unbounded"
+						minOccurs="0">
+					</xsd:element>
+					<xsd:element name="tag" minOccurs="0" maxOccurs="unbounded"
+						type="ResourceLinkWithString">
+					</xsd:element>
+					<xsd:element name="cost" maxOccurs="unbounded"
+						minOccurs="0" type="xsd:string">
+					</xsd:element>
+
+					<xsd:element name="license" type="xsd:string"
+						maxOccurs="unbounded" minOccurs="0">
+					</xsd:element>
+					<xsd:element name="usageCondition" maxOccurs="unbounded"
+						minOccurs="0" type="xsd:string">
+					</xsd:element>
+					<xsd:element name="contact" maxOccurs="unbounded"
+						minOccurs="0" type="xsd:string">
+					</xsd:element>
+					<xsd:element name="publication" maxOccurs="unbounded"
+						minOccurs="0" type="xsd:string">
+					</xsd:element>
+					<xsd:element name="citation" maxOccurs="unbounded"
+						minOccurs="0" type="xsd:string">
+
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:element name="serviceProvider" type="ServiceProvider"></xsd:element>
+
+	<xsd:complexType name="ServiceProvider">
+		<xsd:complexContent>
+
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element ref="dc:title" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="name" type="xsd:string" minOccurs="1"
+						maxOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dc:description" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dcterms:created" maxOccurs="1"
+						minOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="hostnames" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="hostname" type="xsd:string"
+									maxOccurs="unbounded" minOccurs="0">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+
+					<xsd:element name="related" type="ServiceProviderRelatedLinks"
+						maxOccurs="1" minOccurs="0">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:element name="user" type="User"></xsd:element>
+
+	<xsd:complexType name="User">
+
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element ref="dc:title" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="name" type="xsd:string"
+						minOccurs="1" maxOccurs="1">
+					</xsd:element>
+					<xsd:element name="affiliation" type="xsd:string"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="location" type="Location"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="publicEmail" type="xsd:string"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="joined" type="xsd:dateTime"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="savedSearches" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:complexContent>
+
+								<xsd:extension base="ResourceLink">
+									<xsd:sequence>
+										<xsd:element name="savedSearch"
+											type="SavedSearch" maxOccurs="unbounded" minOccurs="0">
+										</xsd:element>
+									</xsd:sequence>
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+
+					<xsd:element name="related" type="UserRelatedLinks"
+						maxOccurs="1" minOccurs="0">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:element name="registry" type="Registry"></xsd:element>
+
+	<xsd:complexType name="Registry">
+
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element ref="dc:title" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="name" type="xsd:string" minOccurs="1"
+						maxOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dc:description" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="homepage" type="xsd:string"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dcterms:created" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" type="RegistryRelatedLinks"
+						maxOccurs="1" minOccurs="0">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+
+	</xsd:complexType>
+
+	<xsd:element name="services" type="Services"></xsd:element>
+
+	<xsd:complexType name="Services">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" type="ServicesParameters"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="statistics" type="ServicesStatistics"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="results" type="ServicesResults"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" type="ServicesRelatedLinks"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+
+	</xsd:complexType>
+
+	<xsd:complexType name="ServicesParameters">
+		<xsd:sequence>
+			<xsd:element name="filters" maxOccurs="1" minOccurs="1"
+				type="FiltersParameters">
+
+			</xsd:element>
+			<xsd:element name="query" maxOccurs="1" minOccurs="1"
+				type="SearchQueryParameter">
+			</xsd:element>
+			<xsd:element ref="sortBy" maxOccurs="1" minOccurs="1"></xsd:element>
+
+			<xsd:element ref="sortOrder" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="page" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="pageSize" maxOccurs="1" minOccurs="1"></xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="FiltersParameters">
+		<xsd:sequence>
+			<xsd:element name="group" type="FilterGroupParameter"
+				minOccurs="0" maxOccurs="unbounded">
+
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="ServicesStatistics">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreStatistics">
+			</xsd:extension>
+		</xsd:complexContent>
+
+	</xsd:complexType>
+
+	<xsd:complexType name="ServicesResults">
+		<xsd:sequence>
+			<xsd:element name="service" type="Service" maxOccurs="unbounded"
+				minOccurs="0">
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="ServicesRelatedLinks">
+
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreRelatedLinks">
+				<xsd:sequence>
+					<xsd:element name="filters" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="filtersOnCurrentResults" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="withSummaries" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="withDeployments" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="withVariants" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="withMonitoring" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="withAllSections" type="ResourceLink"
+						maxOccurs="1" minOccurs="1"></xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="Filters">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="group" type="FilterGroup" maxOccurs="unbounded"
+						minOccurs="0">
+					</xsd:element>
+
+				</xsd:sequence>
+				<xsd:attribute name="for" type="ResourceType" use="required"></xsd:attribute>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:element name="filters" type="Filters"></xsd:element>
+
+	<xsd:complexType name="FilterType">
+		<xsd:sequence>
+
+			<xsd:element name="filter" type="Filter" maxOccurs="unbounded"
+				minOccurs="0">
+			</xsd:element>
+		</xsd:sequence>
+		<xsd:attribute name="name" type="FilterTypeName" use="required">
+		</xsd:attribute>
+		<xsd:attribute name="urlKey" type="FilterTypeUrlKey"
+			use="required">
+		</xsd:attribute>
+		<xsd:attribute name="description" type="xsd:string" use="required"></xsd:attribute>
+	</xsd:complexType>
+
+	<xsd:simpleType name="FilterTypeName">
+		<xsd:restriction base="xsd:string">
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="FilterTypeUrlKey">
+		<xsd:restriction base="xsd:string">
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:complexType name="Filter">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="filter" type="Filter" maxOccurs="unbounded"
+						minOccurs="0">
+					</xsd:element>
+				</xsd:sequence>
+				<xsd:attribute name="urlValue" type="xsd:string" use="required">
+
+				</xsd:attribute>
+				<xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
+				<xsd:attribute name="count" type="xsd:nonNegativeInteger"
+					use="required">
+				</xsd:attribute>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="CollectionCoreStatistics">
+
+		<xsd:sequence>
+			<xsd:element name="pages" type="xsd:nonNegativeInteger"
+				maxOccurs="1" minOccurs="1">
+				<xsd:annotation>
+					<xsd:documentation>The total number of pages available for this
+						result set</xsd:documentation>
+				</xsd:annotation>
+			</xsd:element>
+			<xsd:element name="results" type="xsd:nonNegativeInteger"
+				maxOccurs="1" minOccurs="1">
+				<xsd:annotation>
+
+					<xsd:documentation>The total number of results available for this
+						result set</xsd:documentation>
+				</xsd:annotation>
+			</xsd:element>
+			<xsd:element name="total" type="xsd:nonNegativeInteger"
+				maxOccurs="1" minOccurs="0">
+				<xsd:annotation>
+					<xsd:documentation>When present, this gives the total number of
+						resources of the particular resource type that the result set is
+						referring to. Eg: for /services, this would be the total number of
+						services available regardless of how many are in the result set
+						(which may have been filtered down).</xsd:documentation>
+				</xsd:annotation>
+			</xsd:element>
+
+		</xsd:sequence>
+	</xsd:complexType>
+
+
+
+	<xsd:simpleType name="SortBy">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="created"></xsd:enumeration>
+			<xsd:enumeration value="activated"></xsd:enumeration>
+			<xsd:enumeration value="name"></xsd:enumeration>
+
+			<xsd:enumeration value="modified"></xsd:enumeration>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="SortOrder">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="asc"></xsd:enumeration>
+			<xsd:enumeration value="desc"></xsd:enumeration>
+		</xsd:restriction>
+
+	</xsd:simpleType>
+
+	<xsd:element name="tags" type="Tags"></xsd:element>
+
+	<xsd:complexType name="Tags">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" type="TagsParameters"
+						minOccurs="1" maxOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="statistics" type="TagsStatistics"
+						minOccurs="1" maxOccurs="1">
+					</xsd:element>
+					<xsd:element name="results" type="TagsResults"
+						minOccurs="1" maxOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" type="TagsRelated"
+						minOccurs="1" maxOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+
+	</xsd:complexType>
+
+	<xsd:complexType name="TagsParameters">
+		<xsd:sequence>
+			<xsd:element name="sort" maxOccurs="1" minOccurs="1">
+				<xsd:complexType>
+					<xsd:simpleContent>
+						<xsd:extension base="xsd:string">
+							<xsd:attribute name="urlKey" use="required">
+
+								<xsd:simpleType>
+									<xsd:restriction base="xsd:string">
+										<xsd:enumeration value="sort">
+										</xsd:enumeration>
+									</xsd:restriction>
+								</xsd:simpleType>
+							</xsd:attribute>
+							<xsd:attribute name="urlValue">
+								<xsd:simpleType>
+
+									<xsd:restriction base="xsd:string">
+										<xsd:enumeration value="counts">
+										</xsd:enumeration>
+										<xsd:enumeration value="name">
+										</xsd:enumeration>
+									</xsd:restriction>
+								</xsd:simpleType>
+							</xsd:attribute>
+						</xsd:extension>
+
+					</xsd:simpleContent>
+				</xsd:complexType>
+			</xsd:element>
+			<xsd:element ref="page" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="pageSize" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element name="limit" type="xsd:nonNegativeInteger"
+				maxOccurs="1" minOccurs="1" nillable="true"></xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="TagsStatistics">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreStatistics">
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="TagsResults">
+		<xsd:sequence>
+
+			<xsd:element name="tag" type="Tag" minOccurs="0"
+				maxOccurs="unbounded">
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="TagsRelated">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreRelatedLinks">
+			</xsd:extension>
+
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="Tag">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element ref="dc:title" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="name" type="xsd:string" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="displayName" type="xsd:string"
+						maxOccurs="1" minOccurs="1"></xsd:element>
+					<xsd:element name="totalItemsCount" type="xsd:nonNegativeInteger"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" type="TagRelatedLinks"
+						maxOccurs="1" minOccurs="0">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:simpleType name="SubmitterType">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="User"></xsd:enumeration>
+			<xsd:enumeration value="Registry"></xsd:enumeration>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="AnnotationSourceType">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="User"></xsd:enumeration>
+			<xsd:enumeration value="ServiceProvider"></xsd:enumeration>
+			<xsd:enumeration value="Registry"></xsd:enumeration>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+
+	<xsd:complexType name="ServiceDeployment">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="endpoint" type="xsd:anyURI"
+						minOccurs="1" maxOccurs="1">
+					</xsd:element>
+					<xsd:element name="serviceProvider" type="ServiceProvider"
+						minOccurs="1" maxOccurs="1">
+					</xsd:element>
+					<xsd:element name="location" minOccurs="1" maxOccurs="1"
+						type="Location">
+
+					</xsd:element>
+					<xsd:element name="submitter" type="ResourceLink"
+						minOccurs="1" maxOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dcterms:created" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="providedVariant" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:choice>
+								<xsd:element name="soapService" type="SoapService"
+									maxOccurs="1" minOccurs="0">
+
+								</xsd:element>
+								<xsd:element name="restService" type="RestService"
+									maxOccurs="1" minOccurs="0">
+								</xsd:element>
+							</xsd:choice>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="ancestors" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+
+								<xsd:element name="service"
+									type="Service" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="related" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="annotations" type="ResourceLink"
+									maxOccurs="1" minOccurs="1">
+
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+
+	<xsd:simpleType name="ServiceTechnologyType">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="SOAP"></xsd:enumeration>
+			<xsd:enumeration value="REST"></xsd:enumeration>
+			<xsd:enumeration value="Soaplab"></xsd:enumeration>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:complexType name="Annotation">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="annotatable" type="ResourceLink"
+						maxOccurs="1" minOccurs="0">
+					</xsd:element>
+					<xsd:element name="source" type="ResourceLink"
+						maxOccurs="1" minOccurs="0">
+					</xsd:element>
+					<xsd:element name="version" type="xsd:positiveInteger"
+						maxOccurs="1" minOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="annotationAttribute" type="AnnotationAttribute"
+						minOccurs="1" maxOccurs="1">
+					</xsd:element>
+					<xsd:element name="value" minOccurs="1" maxOccurs="1">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="resource" type="ResourceLink"
+									maxOccurs="1" minOccurs="0">
+								</xsd:element>
+								<xsd:element name="type" type="xsd:string"
+									maxOccurs="1" minOccurs="1">
+
+								</xsd:element>
+								<xsd:element name="content" type="xsd:string"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+
+					<xsd:element ref="dcterms:created" minOccurs="1"
+						maxOccurs="1">
+					</xsd:element>
+
+					<xsd:element ref="dcterms:modified" minOccurs="0"
+						maxOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" maxOccurs="1" minOccurs="0">
+						<xsd:complexType></xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+
+			</xsd:extension>
+		</xsd:complexContent>
+
+	</xsd:complexType>
+
+	<xsd:complexType name="AnnotationAttribute">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element ref="dc:title" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="name" type="xsd:string" maxOccurs="1"
+						minOccurs="1">
+
+					</xsd:element>
+					<xsd:element ref="dc:identifier" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="annotations" type="ResourceLink"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+
+
+
+	<xsd:complexType name="ServiceRelatedLinks">
+		<xsd:sequence>
+			<xsd:element name="withSummary" type="ResourceLink"
+				minOccurs="1" maxOccurs="1">
+			</xsd:element>
+			<xsd:element name="withMonitoring" type="ResourceLink"
+				minOccurs="1" maxOccurs="1">
+			</xsd:element>
+			<xsd:element name="withAllSections" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="summary" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+
+			</xsd:element>
+			<xsd:element name="deployments" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="variants" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="monitoring" type="ResourceLink"
+				maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element name="annotations" type="ResourceLink"
+				minOccurs="1" maxOccurs="1">
+			</xsd:element>
+		</xsd:sequence>
+
+	</xsd:complexType>
+
+	<xsd:simpleType name="MetadataBy">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="all"></xsd:enumeration>
+			<xsd:enumeration value="users"></xsd:enumeration>
+			<xsd:enumeration value="providers"></xsd:enumeration>
+			<xsd:enumeration value="registries"></xsd:enumeration>
+		</xsd:restriction>
+
+	</xsd:simpleType>
+
+	<xsd:complexType name="MetadataCount">
+		<xsd:simpleContent>
+			<xsd:extension base="xsd:nonNegativeInteger">
+				<xsd:attribute name="by" type="MetadataBy"></xsd:attribute>
+			</xsd:extension>
+		</xsd:simpleContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="SearchQueryParameter">
+		<xsd:simpleContent>
+			<xsd:extension base="xsd:string">
+				<xsd:attribute name="urlKey" type="xsd:string" use="required">
+				</xsd:attribute>
+			</xsd:extension>
+		</xsd:simpleContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="PageParameter">
+		<xsd:simpleContent>
+			<xsd:extension base="xsd:nonNegativeInteger">
+				<xsd:attribute name="urlKey" use="required">
+					<xsd:simpleType>
+						<xsd:restriction base="xsd:string">
+							<xsd:enumeration value="page"></xsd:enumeration>
+						</xsd:restriction>
+
+					</xsd:simpleType>
+				</xsd:attribute>
+			</xsd:extension>
+		</xsd:simpleContent>
+	</xsd:complexType>
+
+
+
+
+	<xsd:complexType name="SortByParameter">
+		<xsd:simpleContent>
+
+			<xsd:extension base="xsd:string">
+				<xsd:attribute name="urlKey" use="required">
+					<xsd:simpleType>
+						<xsd:restriction base="xsd:string">
+							<xsd:enumeration value="sort_by"></xsd:enumeration>
+						</xsd:restriction>
+					</xsd:simpleType>
+				</xsd:attribute>
+				<xsd:attribute name="urlValue" type="SortBy" use="required">
+
+				</xsd:attribute>
+			</xsd:extension>
+		</xsd:simpleContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="SortOrderParameter">
+		<xsd:simpleContent>
+			<xsd:extension base="xsd:string">
+				<xsd:attribute name="urlKey" use="required">
+
+					<xsd:simpleType>
+						<xsd:restriction base="xsd:string">
+							<xsd:enumeration value="sort_order"></xsd:enumeration>
+						</xsd:restriction>
+					</xsd:simpleType>
+				</xsd:attribute>
+				<xsd:attribute name="urlValue" type="SortOrder" use="required"></xsd:attribute>
+			</xsd:extension>
+		</xsd:simpleContent>
+
+	</xsd:complexType>
+
+	<xsd:element name="categories" type="Categories"></xsd:element>
+
+	<xsd:element name="category" type="Category"></xsd:element>
+
+	<xsd:complexType name="Category">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+
+					<xsd:element ref="dc:title" maxOccurs="1" minOccurs="1"></xsd:element>
+					<xsd:element name="name" type="xsd:string" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="totalItemsCount" type="xsd:nonNegativeInteger"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="broader" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="category" type="Category"
+									maxOccurs="unbounded" minOccurs="0">
+
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="narrower" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="category" type="Category"
+									maxOccurs="unbounded" minOccurs="0">
+								</xsd:element>
+
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="related" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="services" type="ResourceLink"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="Categories">
+		<xsd:complexContent>
+
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" type="CategoriesParameters"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="statistics" type="CategoriesStatistics"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="results" type="CategoriesResults"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" type="CategoriesRelatedLinks"
+						maxOccurs="1" minOccurs="1">
+
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="CategoriesParameters">
+		<xsd:sequence>
+			<xsd:element name="rootsOnly" maxOccurs="1" minOccurs="1">
+
+				<xsd:complexType>
+					<xsd:simpleContent>
+						<xsd:extension base="xsd:boolean">
+							<xsd:attribute name="urlKey" type="xsd:string" use="required">
+							</xsd:attribute>
+						</xsd:extension>
+					</xsd:simpleContent>
+				</xsd:complexType>
+			</xsd:element>
+
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="CategoriesStatistics">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreStatistics">
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="CategoriesResults">
+		<xsd:sequence>
+			<xsd:element name="category" type="Category" maxOccurs="unbounded"
+				minOccurs="0">
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="CategoriesRelatedLinks">
+		<xsd:sequence>
+
+			<xsd:element name="serviceFilters" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="ResourceLinkWithString">
+		<xsd:simpleContent>
+			<xsd:extension base="xsd:string">
+				<xsd:attribute ref="xlink:href" use="required"></xsd:attribute>
+
+				<xsd:attribute ref="xlink:title" use="optional"></xsd:attribute>
+				<xsd:attribute name="resourceType" type="ResourceType"
+					use="optional"></xsd:attribute>
+			</xsd:extension>
+		</xsd:simpleContent>
+	</xsd:complexType>
+
+
+	<xsd:complexType name="MonitoringStatus">
+		<xsd:sequence>
+			<xsd:element name="label" maxOccurs="1" minOccurs="1"
+				type="MonitoringStatusLabel">
+
+			</xsd:element>
+			<xsd:element name="message" type="xsd:string" maxOccurs="1"
+				minOccurs="1">
+			</xsd:element>
+			<xsd:element name="symbol" type="ResourceLink" maxOccurs="1"
+				minOccurs="1">
+			</xsd:element>
+			<xsd:element name="smallSymbol" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="lastChecked" type="xsd:dateTime"
+				maxOccurs="1" minOccurs="1" nillable="true"></xsd:element>
+		</xsd:sequence>
+
+	</xsd:complexType>
+
+	<xsd:complexType name="TagRelatedLinks">
+		<xsd:sequence>
+			<xsd:element name="services" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="soapOperations" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="restMethods" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:element name="tag" type="Tag"></xsd:element>
+
+	<xsd:complexType name="PageSizeParameter">
+		<xsd:simpleContent>
+			<xsd:extension base="xsd:nonNegativeInteger">
+				<xsd:attribute name="urlKey" use="required">
+
+					<xsd:simpleType>
+						<xsd:restriction base="xsd:string">
+							<xsd:enumeration value="per_page"></xsd:enumeration>
+						</xsd:restriction>
+					</xsd:simpleType>
+				</xsd:attribute>
+			</xsd:extension>
+		</xsd:simpleContent>
+	</xsd:complexType>
+
+	<xsd:element name="serviceProviders" type="ServiceProviders"></xsd:element>
+
+	<xsd:complexType name="ServiceProviders">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" type="ServiceProvidersParameters"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="statistics" type="ServiceProvidersStatistics"
+						maxOccurs="1" minOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="results" type="ServiceProvidersResults"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" type="ServiceProvidersRelatedLinks"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="ServiceProviderRelatedLinks">
+		<xsd:sequence>
+			<xsd:element name="annotations" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="annotationsBy" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="services" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="ServiceProvidersParameters">
+		<xsd:sequence>
+			<xsd:element name="filters" type="FiltersParameters"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="query" type="SearchQueryParameter"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+
+			<xsd:element ref="sortBy" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="sortOrder" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="page" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="pageSize" maxOccurs="1" minOccurs="1"></xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="ServiceProvidersStatistics">
+		<xsd:complexContent>
+
+			<xsd:extension base="CollectionCoreStatistics">
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="ServiceProvidersResults">
+		<xsd:sequence>
+			<xsd:element name="serviceProvider" type="ServiceProvider"
+				maxOccurs="unbounded" minOccurs="0">
+			</xsd:element>
+
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="ServiceProvidersRelatedLinks">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreRelatedLinks">
+				<xsd:sequence>
+					<xsd:element name="filters" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="filtersOnCurrentResults"
+						type="ResourceLink" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:element name="sortBy" type="SortByParameter"></xsd:element>
+
+	<xsd:element name="sortOrder" type="SortOrderParameter"></xsd:element>
+
+	<xsd:element name="page" type="PageParameter"></xsd:element>
+
+	<xsd:element name="pageSize" type="PageSizeParameter"></xsd:element>
+
+	<xsd:element name="soapService" type="SoapService"></xsd:element>
+
+	<xsd:complexType name="SoapService">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+
+					<xsd:element ref="dc:title" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="name" type="xsd:string" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="wsdlLocation" type="xsd:anyURI"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="submitter" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dc:description" maxOccurs="1"
+						minOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="documentationUrl" type="xsd:string"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dcterms:created" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="deployments" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="ResourceLink">
+
+									<xsd:sequence>
+										<xsd:element name="serviceDeployment" type="ServiceDeployment"
+											maxOccurs="unbounded" minOccurs="1">
+										</xsd:element>
+									</xsd:sequence>
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="operations" maxOccurs="1" minOccurs="0">
+
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="ResourceLink">
+									<xsd:sequence>
+										<xsd:element name="soapOperation" type="SoapOperation"
+											maxOccurs="unbounded" minOccurs="0">
+										</xsd:element>
+									</xsd:sequence>
+								</xsd:extension>
+							</xsd:complexContent>
+
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="ancestors" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="service"
+									type="Service" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+
+					</xsd:element>
+					<xsd:element name="related" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="deployments" type="ResourceLink"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="operations" type="ResourceLink"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="annotations" type="ResourceLink"
+									maxOccurs="1" minOccurs="1">
+
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+
+	<xsd:element name="restService" type="RestService"></xsd:element>
+
+	<xsd:complexType name="RestService">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element ref="dc:title" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="name" type="xsd:string"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="submitter" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dc:description" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="documentationUrl"
+						type="xsd:string" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dcterms:created" maxOccurs="1"
+						minOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="deployments" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="ResourceLink">
+									<xsd:sequence>
+										<xsd:element
+											name="serviceDeployment" type="ServiceDeployment"
+											maxOccurs="unbounded" minOccurs="0">
+										</xsd:element>
+									</xsd:sequence>
+
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="resources" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="ResourceLink">
+									<xsd:sequence>
+
+										<xsd:element name="restResource"
+											type="RestResource" maxOccurs="unbounded" minOccurs="0">
+										</xsd:element>
+									</xsd:sequence>
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="methods" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+
+							<xsd:complexContent>
+                <xsd:extension base="ResourceLink">
+                  <xsd:sequence>
+                    <xsd:element name="restMethod"
+                      type="RestMethod" maxOccurs="unbounded" minOccurs="0">
+                    </xsd:element>
+                  </xsd:sequence>
+                </xsd:extension>
+              </xsd:complexContent>
+						</xsd:complexType>
+
+					</xsd:element>
+					<xsd:element name="ancestors" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="service"
+									type="Service" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+
+					<xsd:element name="related" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="deployments"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="resources"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="methods"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+
+								<xsd:element name="annotations"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+
+	</xsd:complexType>
+
+
+	<xsd:element name="serviceDeployment" type="ServiceDeployment"></xsd:element>
+
+
+	<xsd:complexType name="FilterTypeParameter">
+		<xsd:sequence>
+			<xsd:element name="filter" type="FilterParameter"
+				maxOccurs="unbounded" minOccurs="1">
+			</xsd:element>
+		</xsd:sequence>
+
+		<xsd:attribute name="name" type="FilterTypeName" use="required">
+		</xsd:attribute>
+		<xsd:attribute name="urlKey" type="FilterTypeUrlKey"
+			use="required">
+		</xsd:attribute>
+		<xsd:attribute name="description" type="xsd:string" use="required"></xsd:attribute>
+	</xsd:complexType>
+
+	<xsd:complexType name="FilterParameter">
+		<xsd:simpleContent>
+
+			<xsd:extension base="xsd:string">
+				<xsd:attribute name="urlValue" type="xsd:string" use="required">
+				</xsd:attribute>
+			</xsd:extension>
+		</xsd:simpleContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="Location">
+		<xsd:sequence>
+
+			<xsd:element name="city" type="xsd:string" maxOccurs="1"
+				minOccurs="0">
+			</xsd:element>
+			<xsd:element name="country" type="xsd:string" maxOccurs="1"
+				minOccurs="0">
+			</xsd:element>
+			<xsd:element name="iso3166CountryCode" type="xsd:string"
+				maxOccurs="1" minOccurs="0">
+			</xsd:element>
+			<xsd:element name="flag" type="ResourceLink" maxOccurs="1"
+				minOccurs="0"></xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:element name="users" type="Users"></xsd:element>
+
+	<xsd:complexType name="Users">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" type="UsersParameters"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="statistics" type="UsersStatistics"
+						maxOccurs="1" minOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="results" type="UsersResults"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" type="UsersRelatedLinks"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="UsersParameters">
+		<xsd:sequence>
+			<xsd:element name="filters" type="FiltersParameters"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="query" type="SearchQueryParameter" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="sortBy" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="sortOrder" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="page" maxOccurs="1" minOccurs="1"></xsd:element>
+
+			<xsd:element ref="pageSize" maxOccurs="1" minOccurs="1"></xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="UsersStatistics">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreStatistics">
+			</xsd:extension>
+		</xsd:complexContent>
+
+	</xsd:complexType>
+
+	<xsd:complexType name="UsersResults">
+		<xsd:sequence>
+			<xsd:element name="user" type="User" maxOccurs="unbounded"
+				minOccurs="0">
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="UsersRelatedLinks">
+
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreRelatedLinks">
+				<xsd:sequence>
+					<xsd:element name="filters" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="filtersOnCurrentResults"
+						type="ResourceLink" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="UserRelatedLinks">
+		<xsd:sequence>
+			<xsd:element name="annotationsBy" type="ResourceLink"
+				maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element name="services" type="ResourceLink"
+				maxOccurs="1" minOccurs="1"></xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:element name="registries" type="Registries"></xsd:element>
+
+	<xsd:complexType name="Registries">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" type="RegistriesParameters"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="statistics" type="RegistriesStatistics"
+						maxOccurs="1" minOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="results" type="RegistriesResults"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" type="RegistriesRelatedLinks"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="RegistriesParameters">
+		<xsd:sequence>
+			<xsd:element ref="sortBy" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="sortOrder" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="page" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="pageSize" maxOccurs="1" minOccurs="1"></xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="RegistriesStatistics">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreStatistics">
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="RegistriesResults">
+		<xsd:sequence>
+
+			<xsd:element name="registry" type="Registry" maxOccurs="unbounded"
+				minOccurs="0">
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="RegistriesRelatedLinks">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreRelatedLinks"></xsd:extension>
+		</xsd:complexContent>
+
+	</xsd:complexType>
+
+	<xsd:complexType name="RegistryRelatedLinks">
+		<xsd:sequence>
+			<xsd:element name="annotationsBy" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="services" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+		</xsd:sequence>
+
+	</xsd:complexType>
+
+	<xsd:simpleType name="MonitoringStatusLabel">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="PASSED"></xsd:enumeration>
+			<xsd:enumeration value="WARNING"></xsd:enumeration>
+			<xsd:enumeration value="FAILED"></xsd:enumeration>
+			<xsd:enumeration value="UNCHECKED"></xsd:enumeration>
+		</xsd:restriction>
+
+	</xsd:simpleType>
+
+
+	<xsd:complexType name="SoapOperation">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element ref="dc:title" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="name" type="xsd:string"
+						maxOccurs="1" minOccurs="1">
+
+					</xsd:element>
+					<xsd:element ref="dc:description" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="parameterOrder" type="xsd:string"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dcterms:created" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="archived" type="xsd:dateTime"
+						maxOccurs="1" minOccurs="0">
+					</xsd:element>
+
+					<xsd:element name="inputs" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="ResourceLink">
+									<xsd:sequence>
+										<xsd:element name="soapInput"
+											type="SoapInput" maxOccurs="unbounded" minOccurs="0">
+										</xsd:element>
+									</xsd:sequence>
+								</xsd:extension>
+
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="outputs" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="ResourceLink">
+									<xsd:sequence>
+										<xsd:element name="soapOutput"
+											type="SoapOutput" maxOccurs="unbounded" minOccurs="0">
+
+										</xsd:element>
+									</xsd:sequence>
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="ancestors" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+
+								<xsd:element name="service"
+									type="Service" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="soapService"
+									type="SoapService" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="related" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+
+							<xsd:sequence>
+								<xsd:element name="inputs"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="outputs"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="annotations"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="annotationsOnAll"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:element name="soapOperation" type="SoapOperation"></xsd:element>
+
+	<xsd:element name="soapInput" type="SoapInput"></xsd:element>
+
+	<xsd:complexType name="SoapInput">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element ref="dc:title" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="name" type="xsd:string"
+						maxOccurs="1" minOccurs="1">
+
+					</xsd:element>
+					<xsd:element ref="dc:description" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="computationalType"
+						type="xsd:string" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="computationalTypeDetails"
+						type="xsd:string" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dcterms:created" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="archived" type="xsd:dateTime"
+						maxOccurs="1" minOccurs="0">
+					</xsd:element>
+					<xsd:element name="ancestors" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="service"
+									type="Service" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="soapService"
+									type="SoapService" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+
+								<xsd:element name="soapOperation"
+									type="SoapOperation" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="related" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="annotations"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:element name="soapOutput" type="SoapOutput"></xsd:element>
+
+	<xsd:complexType name="SoapOutput">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element ref="dc:title" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="name" type="xsd:string"
+						maxOccurs="1" minOccurs="1">
+
+					</xsd:element>
+					<xsd:element ref="dc:description" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="computationalType"
+						type="xsd:string" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="computationalTypeDetails"
+						type="xsd:string" maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dcterms:created" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="archived" type="xsd:dateTime"
+						maxOccurs="1" minOccurs="0">
+					</xsd:element>
+					<xsd:element name="ancestors" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="service"
+									type="Service" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="soapService"
+									type="SoapService" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+
+								<xsd:element name="soapOperation"
+									type="SoapOperation" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="related" maxOccurs="1"
+						minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="annotations"
+									type="ResourceLink" maxOccurs="1" minOccurs="1">
+
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:simpleType name="ResourceType">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="Annotation"></xsd:enumeration>
+			<xsd:enumeration value="AnnotationAttribute"></xsd:enumeration>
+			<xsd:enumeration value="Annotations"></xsd:enumeration>
+			<xsd:enumeration value="BioCatalogue"></xsd:enumeration>
+			<xsd:enumeration value="Categories"></xsd:enumeration>
+			<xsd:enumeration value="Category"></xsd:enumeration>
+
+			<xsd:enumeration value="Filters"></xsd:enumeration>
+			<xsd:enumeration value="Registries"></xsd:enumeration>
+			<xsd:enumeration value="Registry"></xsd:enumeration>
+			<xsd:enumeration value="RestService"></xsd:enumeration>
+			<xsd:enumeration value="Search"></xsd:enumeration>
+			<xsd:enumeration value="Service"></xsd:enumeration>
+			<xsd:enumeration value="ServiceDeployment"></xsd:enumeration>
+			<xsd:enumeration value="ServiceProvider"></xsd:enumeration>
+			<xsd:enumeration value="ServiceProviders"></xsd:enumeration>
+
+			<xsd:enumeration value="Services"></xsd:enumeration>
+			<xsd:enumeration value="SoapInput"></xsd:enumeration>
+			<xsd:enumeration value="SoapOperation"></xsd:enumeration>
+			<xsd:enumeration value="SoapOutput"></xsd:enumeration>
+			<xsd:enumeration value="SoapService"></xsd:enumeration>
+			<xsd:enumeration value="Tag"></xsd:enumeration>
+			<xsd:enumeration value="Tags"></xsd:enumeration>
+			<xsd:enumeration value="Tags"></xsd:enumeration>
+			<xsd:enumeration value="User"></xsd:enumeration>
+
+			<xsd:enumeration value="Users"></xsd:enumeration>
+			<xsd:enumeration value="Annotations"></xsd:enumeration>
+			<xsd:enumeration value="AnnotationAttributes"></xsd:enumeration>
+			<xsd:enumeration value="ServiceTest"></xsd:enumeration>
+			<xsd:enumeration value="TestResult"></xsd:enumeration>
+			<xsd:enumeration value="TestResults"></xsd:enumeration>
+			<xsd:enumeration value="UrlMonitor"></xsd:enumeration>
+			<xsd:enumeration value="TestScript"></xsd:enumeration>
+			<xsd:enumeration value="Errors"></xsd:enumeration>
+
+			<xsd:enumeration value="SearchByData"></xsd:enumeration>
+			<xsd:enumeration value="SoapOperations"></xsd:enumeration>
+			<xsd:enumeration value="Agent"></xsd:enumeration>
+			<xsd:enumeration value="Agents"></xsd:enumeration>
+			<xsd:enumeration value="RestMethod"></xsd:enumeration>
+			<xsd:enumeration value="RestParameter"></xsd:enumeration>
+			<xsd:enumeration value="RestRepresentation"></xsd:enumeration>
+			<xsd:enumeration value="RestMethods"></xsd:enumeration>
+			<xsd:enumeration value="SoapServices"></xsd:enumeration>
+
+			<xsd:enumeration value="RestServices"></xsd:enumeration>
+			<xsd:enumeration value="RestResources"></xsd:enumeration>
+			<xsd:enumeration value="RestResource"></xsd:enumeration>
+			<xsd:enumeration value="SavedSearch"></xsd:enumeration>
+			<xsd:enumeration value="WsdlLocations"></xsd:enumeration>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:element name="annotationAttribute" type="AnnotationAttribute">
+
+	</xsd:element>
+
+	<xsd:element name="annotationAttributes" type="AnnotationAttributes">
+	</xsd:element>
+
+	<xsd:complexType name="AnnotationAttributes">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" type="AnnotationAttributesParameters"
+						maxOccurs="1" minOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="statistics" type="AnnotationAttributesStatistics"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="results" type="AnnotationAttributesResults"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" type="AnnotationAttributesRelatedLinks"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="AnnotationAttributesParameters">
+		<xsd:sequence>
+			<xsd:element ref="page" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="pageSize" maxOccurs="1" minOccurs="1"></xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="AnnotationAttributesStatistics">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreStatistics"></xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="AnnotationAttributesResults">
+		<xsd:sequence>
+			<xsd:element name="annotationAttribute" type="AnnotationAttribute"
+				maxOccurs="unbounded" minOccurs="0">
+
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="AnnotationAttributesRelatedLinks">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreRelatedLinks"></xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:element name="annotations" type="Annotations"></xsd:element>
+
+	<xsd:complexType name="Annotations">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" type="AnnotationsParameters"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="statistics" type="AnnotationsStatistics"
+						maxOccurs="1" minOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="results" type="AnnotationsResults"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="related" type="AnnotationsRelatedLinks"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="AnnotationsParameters">
+		<xsd:sequence>
+			<xsd:element name="filters" type="FiltersParameters"
+				maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="sortBy" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="sortOrder" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="page" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="pageSize" maxOccurs="1" minOccurs="1"></xsd:element>
+		</xsd:sequence>
+
+	</xsd:complexType>
+
+	<xsd:complexType name="AnnotationsStatistics">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreStatistics"></xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="AnnotationsResults">
+		<xsd:sequence>
+
+			<xsd:element name="annotation" type="Annotation"
+				maxOccurs="unbounded" minOccurs="0">
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="AnnotationsRelatedLinks">
+		<xsd:complexContent>
+			<xsd:extension base="CollectionCoreRelatedLinks">
+				<xsd:sequence>
+
+					<xsd:element name="filters" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="filtersOnCurrentResults" type="ResourceLink"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:element name="annotation" type="Annotation"></xsd:element>
+
+	<xsd:element name="testResult" type="TestResult"></xsd:element>
+
+	<xsd:complexType name="TestResult">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="testAction" type="xsd:string"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="resultCode" type="xsd:integer"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element ref="dcterms:created" maxOccurs="1"
+						minOccurs="1">
+					</xsd:element>
+					<xsd:element name="status" type="MonitoringStatus"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="ancestors" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+
+								<xsd:element name="service"
+									type="Service" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="serviceTest"
+									type="ServiceTest" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="related" maxOccurs="1" minOccurs="0">
+						<xsd:complexType></xsd:complexType>
+
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:element name="testResults" type="TestResults"></xsd:element>
+
+	<xsd:complexType name="TestResults">
+		<xsd:complexContent>
+
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" maxOccurs="1" minOccurs="1">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element ref="sortBy" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element ref="sortOrder" maxOccurs="1"
+									minOccurs="1">
+								</xsd:element>
+
+								<xsd:element ref="page" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element ref="pageSize" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="serviceTest" type="ResourceLink"
+									maxOccurs="1" minOccurs="0">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+
+					<xsd:element name="statistics" maxOccurs="1" minOccurs="1">
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="CollectionCoreStatistics">
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="results" maxOccurs="1" minOccurs="1">
+
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="testResult" type="TestResult"
+									maxOccurs="unbounded" minOccurs="0">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="related" maxOccurs="1" minOccurs="1">
+						<xsd:complexType>
+
+							<xsd:complexContent>
+								<xsd:extension base="CollectionCoreRelatedLinks">
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+
+	</xsd:complexType>
+
+	<xsd:element name="serviceTest" type="ServiceTest"></xsd:element>
+
+	<xsd:complexType name="ServiceTest">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="testType" maxOccurs="1" minOccurs="1">
+						<xsd:complexType>
+
+							<xsd:choice>
+								<xsd:element name="urlMonitor" type="UrlMonitor"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="testScript" type="TestScript"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:choice>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element ref="dcterms:created" maxOccurs="1"
+						minOccurs="1">
+
+					</xsd:element>
+					<xsd:element name="latestStatus" type="MonitoringStatus"
+						maxOccurs="1" minOccurs="0">
+					</xsd:element>
+					<xsd:element name="ancestors" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="service"
+									type="Service" maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="related" maxOccurs="1" minOccurs="0">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="results" type="ResourceLink"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="UrlMonitor">
+		<xsd:sequence>
+			<xsd:element name="url" type="xsd:anyURI" maxOccurs="1"
+				minOccurs="1">
+
+			</xsd:element>
+			<xsd:element name="resource" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="TestScript">
+		<xsd:sequence>
+			<xsd:element name="name" type="xsd:string" maxOccurs="1"
+				minOccurs="1">
+
+			</xsd:element>
+			<xsd:element ref="dc:description" maxOccurs="1"
+				minOccurs="1">
+			</xsd:element>
+			<xsd:element name="contentType" type="xsd:string"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="programmingLanguage" type="xsd:string"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="executableFilename" type="xsd:string"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+
+			<xsd:element name="download" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element name="submitter" type="ResourceLink"
+				maxOccurs="1" minOccurs="1">
+			</xsd:element>
+			<xsd:element ref="dcterms:created" maxOccurs="1"
+				minOccurs="1">
+			</xsd:element>
+			<xsd:element name="activatedAt" type="xsd:dateTime"
+				nillable="true" maxOccurs="1" minOccurs="1">
+			</xsd:element>
+		</xsd:sequence>
+
+	</xsd:complexType>
+
+	<xsd:element name="errors" type="Errors"></xsd:element>
+
+	<xsd:complexType name="Errors">
+		<xsd:sequence>
+			<xsd:element name="error" type="xsd:string" maxOccurs="unbounded"
+				minOccurs="1"></xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:element name="searchByData" type="SearchByData"></xsd:element>
+
+	<xsd:complexType name="SearchByData">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" maxOccurs="1" minOccurs="1">
+						<xsd:complexType>
+							<xsd:sequence>
+
+								<xsd:element name="data" type="xsd:string"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element name="searchType" maxOccurs="1"
+									minOccurs="1">
+									<xsd:simpleType>
+										<xsd:restriction base="xsd:string">
+											<xsd:enumeration value="input">
+											</xsd:enumeration>
+											<xsd:enumeration value="output">
+											</xsd:enumeration>
+
+										</xsd:restriction>
+									</xsd:simpleType>
+								</xsd:element>
+								<xsd:element name="limit" type="xsd:nonNegativeInteger"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="statistics" maxOccurs="1" minOccurs="1">
+
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="CollectionCoreStatistics">
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="results" maxOccurs="1" minOccurs="1">
+						<xsd:complexType>
+
+							<xsd:sequence>
+								<xsd:element name="resultItem" maxOccurs="unbounded"
+									minOccurs="0">
+									<xsd:complexType>
+										<xsd:sequence>
+											<xsd:element name="service" type="ResourceLink"
+												maxOccurs="1" minOccurs="1">
+											</xsd:element>
+											<xsd:element name="soapOperation" type="ResourceLink"
+												maxOccurs="1" minOccurs="1">
+											</xsd:element>
+											<xsd:element name="port" type="ResourceLink"
+												maxOccurs="1" minOccurs="1">
+
+											</xsd:element>
+											<xsd:element name="annotation" type="ResourceLink"
+												maxOccurs="1" minOccurs="1">
+											</xsd:element>
+										</xsd:sequence>
+									</xsd:complexType>
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+
+					<xsd:element name="related" maxOccurs="1" minOccurs="1">
+						<xsd:complexType></xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+
+
+	<xsd:element name="soapOperations" type="SoapOperations"></xsd:element>
+
+	<xsd:complexType name="SoapOperations">
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" maxOccurs="1" minOccurs="1">
+						<xsd:complexType>
+							<xsd:sequence>
+
+								<xsd:element name="filters" maxOccurs="1" minOccurs="1"
+									type="FiltersParameters">
+								</xsd:element>
+								<xsd:element name="query" type="SearchQueryParameter"
+									maxOccurs="1" minOccurs="1">
+								</xsd:element>
+								<xsd:element ref="sortBy" maxOccurs="1" minOccurs="1"></xsd:element>
+								<xsd:element ref="sortOrder" maxOccurs="1"
+									minOccurs="1"></xsd:element>
+								<xsd:element ref="page" maxOccurs="1" minOccurs="1"></xsd:element>
+								<xsd:element ref="pageSize" maxOccurs="1" minOccurs="1"></xsd:element>
+							</xsd:sequence>
+
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="statistics" maxOccurs="1" minOccurs="1">
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="CollectionCoreStatistics">
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+
+					</xsd:element>
+					<xsd:element name="results" maxOccurs="1" minOccurs="1">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="soapOperation" type="SoapOperation"
+									maxOccurs="unbounded" minOccurs="0">
+								</xsd:element>
+							</xsd:sequence>
+						</xsd:complexType>
+					</xsd:element>
+
+					<xsd:element name="related" maxOccurs="1" minOccurs="1">
+						<xsd:complexType>
+							<xsd:complexContent>
+								<xsd:extension base="CollectionCoreRelatedLinks">
+									<xsd:sequence>
+										<xsd:element name="filters" type="ResourceLink"
+											maxOccurs="1" minOccurs="1">
+										</xsd:element>
+										<xsd:element name="filtersOnCurrentResults" type="ResourceLink"
+											maxOccurs="1" minOccurs="1">
+										</xsd:element>
+
+									</xsd:sequence>
+								</xsd:extension>
+							</xsd:complexContent>
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="FilterGroup">
+		<xsd:sequence>
+			<xsd:element name="type" type="FilterType" maxOccurs="unbounded"
+				minOccurs="1">
+			</xsd:element>
+		</xsd:sequence>
+		<xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
+	</xsd:complexType>
+
+	<xsd:complexType name="FilterGroupParameter">
+
+		<xsd:sequence>
+			<xsd:element name="type" type="FilterTypeParameter"
+				maxOccurs="unbounded" minOccurs="1">
+			</xsd:element>
+		</xsd:sequence>
+		<xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
+	</xsd:complexType>
+
+	<xsd:element name="agents" type="Agents"></xsd:element>
+
+	<xsd:complexType name="Agents">
+
+		<xsd:complexContent>
+			<xsd:extension base="ResourceLink">
+				<xsd:sequence>
+					<xsd:element name="parameters" type="AgentsParameters"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="statistics" type="AgentsStatistics"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+					<xsd:element name="results" type="AgentsResults"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+
+					<xsd:element name="related" type="AgentsRelatedLinks"
+						maxOccurs="1" minOccurs="1">
+					</xsd:element>
+				</xsd:sequence>
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="AgentsParameters">
+		<xsd:sequence>
+
+			<xsd:element ref="sortBy" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="sortOrder" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="page" maxOccurs="1" minOccurs="1"></xsd:element>
+			<xsd:element ref="pageSize" maxOccurs="1" minOccurs="1"></xsd:element>
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="AgentsStatistics">
+		<xsd:complexContent>
+
+			<xsd:extension base="CollectionCoreStatistics">
+			</xsd:extension>
+		</xsd:complexContent>
+	</xsd:complexType>
+
+	<xsd:complexType name="AgentsResults">
+		<xsd:sequence>
+			<xsd:element name="agent" type="Agent" maxOccurs="unbounded"
+				minOccurs="0">
+			</xsd:element>
+
+		</xsd:sequence>
+	</xsd:complexType>
+
+	<xsd:complexType name="AgentsRelatedLinks">
+		<xsd:complexContent>
+			<xsd:extension base="Col

<TRUNCATED>

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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseAllWorkflowsAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseAllWorkflowsAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseAllWorkflowsAction.java
new file mode 100644
index 0000000..2309b9c
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseAllWorkflowsAction.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
+import static java.awt.event.KeyEvent.VK_L;
+import static java.awt.event.KeyEvent.VK_W;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.closeAllIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+
+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.container.WorkflowBundle;
+
+@SuppressWarnings("serial")
+public class CloseAllWorkflowsAction extends AbstractAction {
+	@SuppressWarnings("unused")
+	private static Logger logger = Logger.getLogger(CloseWorkflowAction.class);
+	private static final String CLOSE_ALL_WORKFLOWS = "Close all workflows";
+	private FileManager fileManager;
+	private CloseWorkflowAction closeWorkflowAction;
+
+	public CloseAllWorkflowsAction(EditManager editManager, FileManager fileManager) {
+		super(CLOSE_ALL_WORKFLOWS, closeAllIcon);
+		this.fileManager = fileManager;
+		closeWorkflowAction = new CloseWorkflowAction(editManager, fileManager);
+		putValue(
+				ACCELERATOR_KEY,
+				getKeyStroke(VK_W, getDefaultToolkit().getMenuShortcutKeyMask()
+						| SHIFT_DOWN_MASK));
+		putValue(MNEMONIC_KEY, VK_L);
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent event) {
+		Component parentComponent = null;
+		if (event.getSource() instanceof Component)
+			parentComponent = (Component) event.getSource();
+		closeAllWorkflows(parentComponent);
+	}
+
+	public boolean closeAllWorkflows(Component parentComponent) {
+		// Close in reverse so we can save nested workflows first
+		List<WorkflowBundle> workflowBundles = fileManager.getOpenDataflows();
+
+		Collections.reverse(workflowBundles);
+
+		for (WorkflowBundle workflowBundle : workflowBundles) {
+			boolean success = closeWorkflowAction.closeWorkflow(
+					parentComponent, workflowBundle);
+			if (!success)
+				return false;
+		}
+		return true;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseWorkflowAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseWorkflowAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseWorkflowAction.java
new file mode 100644
index 0000000..03c90a6
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseWorkflowAction.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * 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.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_C;
+import static java.awt.event.KeyEvent.VK_W;
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.NO_OPTION;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.closeIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.exceptions.UnsavedException;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+@SuppressWarnings("serial")
+public class CloseWorkflowAction extends AbstractAction {
+	private static Logger logger = Logger.getLogger(CloseWorkflowAction.class);
+	private static final String CLOSE_WORKFLOW = "Close workflow";
+	private final SaveWorkflowAction saveWorkflowAction;
+	private FileManager fileManager;
+
+	public CloseWorkflowAction(EditManager editManager, FileManager fileManager) {
+		super(CLOSE_WORKFLOW, closeIcon);
+		this.fileManager = fileManager;
+		saveWorkflowAction = new SaveWorkflowAction(editManager, fileManager);
+		putValue(
+				ACCELERATOR_KEY,
+				getKeyStroke(VK_W, getDefaultToolkit().getMenuShortcutKeyMask()));
+		putValue(MNEMONIC_KEY, VK_C);
+
+	}
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		Component parentComponent = null;
+		if (e.getSource() instanceof Component)
+			parentComponent = (Component) e.getSource();
+		closeWorkflow(parentComponent, fileManager.getCurrentDataflow());
+	}
+
+	public boolean closeWorkflow(Component parentComponent, WorkflowBundle workflowBundle) {
+		if (workflowBundle == null) {
+			logger.warn("Attempted to close a null workflow");
+			return false;
+		}
+
+		try {
+			return fileManager.closeDataflow(workflowBundle, true);
+		} catch (UnsavedException e1) {
+			fileManager.setCurrentDataflow(workflowBundle);
+			String msg = "Do you want to save changes before closing the workflow "
+					+ fileManager.getDataflowName(workflowBundle) + "?";
+			switch (showConfirmDialog(parentComponent, msg, "Save workflow?",
+					YES_NO_CANCEL_OPTION)) {
+			case NO_OPTION:
+				try {
+					fileManager.closeDataflow(workflowBundle, false);
+					return true;
+				} catch (UnsavedException e2) {
+					logger.error("Unexpected UnsavedException while "
+							+ "closing workflow", e2);
+					return false;
+				}
+			case YES_OPTION:
+				boolean saved = saveWorkflowAction.saveDataflow(
+						parentComponent, workflowBundle);
+				if (!saved)
+					return false;
+				return closeWorkflow(parentComponent, workflowBundle);
+			case CANCEL_OPTION:
+			default:
+				return false;
+			}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/NewWorkflowAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/NewWorkflowAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/NewWorkflowAction.java
new file mode 100644
index 0000000..3b9256a
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/NewWorkflowAction.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_N;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.newIcon;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.workbench.file.FileManager;
+
+import org.apache.log4j.Logger;
+
+@SuppressWarnings("serial")
+public class NewWorkflowAction extends AbstractAction {
+	@SuppressWarnings("unused")
+	private static Logger logger = Logger.getLogger(NewWorkflowAction.class);
+	private static final String NEW_WORKFLOW = "New workflow";
+	private FileManager fileManager;
+
+	public NewWorkflowAction(FileManager fileManager) {
+		super(NEW_WORKFLOW, newIcon);
+		this.fileManager = fileManager;
+		putValue(SHORT_DESCRIPTION, NEW_WORKFLOW);
+		putValue(MNEMONIC_KEY, KeyEvent.VK_N);
+		putValue(
+				ACCELERATOR_KEY,
+				getKeyStroke(VK_N, getDefaultToolkit().getMenuShortcutKeyMask()));
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		fileManager.newDataflow();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenNestedWorkflowAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenNestedWorkflowAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenNestedWorkflowAction.java
new file mode 100644
index 0000000..08030c7
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenNestedWorkflowAction.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * 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.file.impl.actions;
+
+import java.awt.Component;
+import java.io.File;
+
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * An action for opening a nested workflow from a file.
+ * 
+ * @author Alex Nenadic
+ */
+public class OpenNestedWorkflowAction extends OpenWorkflowAction {
+	private static final long serialVersionUID = -5398423684000142379L;
+	private static Logger logger = Logger
+			.getLogger(OpenNestedWorkflowAction.class);
+
+	public OpenNestedWorkflowAction(FileManager fileManager) {
+		super(fileManager);
+	}
+
+	/**
+	 * Opens a nested workflow from a file (should be one file even though the
+	 * method takes a list of files - this is because it overrides the
+	 * {@link OpenWorkflowAction#openWorkflows(Component, File[], FileType, OpenCallback)
+	 * openWorkflows(...)} method).
+	 */
+	@Override
+	public void openWorkflows(final Component parentComponent, File[] files,
+			FileType fileType, OpenCallback openCallback) {
+		ErrorLoggingOpenCallbackWrapper callback = new ErrorLoggingOpenCallbackWrapper(
+				openCallback);
+		for (File file : files)
+			try {
+				callback.aboutToOpenDataflow(file);
+				WorkflowBundle workflowBundle = fileManager.openDataflow(
+						fileType, file);
+				callback.openedDataflow(file, workflowBundle);
+			} catch (final RuntimeException ex) {
+				logger.warn("Could not open workflow from " + file, ex);
+				if (!callback.couldNotOpenDataflow(file, ex))
+					showErrorMessage(parentComponent, file, ex);
+			} catch (final OpenException ex) {
+				logger.warn("Could not open workflow from " + file, ex);
+				if (!callback.couldNotOpenDataflow(file, ex))
+					showErrorMessage(parentComponent, file, ex);
+				return;
+			}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowAction.java
new file mode 100644
index 0000000..e2ecbd7
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowAction.java
@@ -0,0 +1,395 @@
+/*******************************************************************************
+ * 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.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_O;
+import static java.util.prefs.Preferences.userNodeForPackage;
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.QUESTION_MESSAGE;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.JOptionPane.showOptionDialog;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static javax.swing.SwingUtilities.invokeLater;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.openIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+import net.sf.taverna.t2.workbench.file.impl.FileTypeFileFilter;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * An action for opening a workflow from a file. All file types exposed by the
+ * {@link FileManager} as compatible with the {@link File} type are supported.
+ *
+ * @author Stian Soiland-Reyes
+ */
+public class OpenWorkflowAction extends AbstractAction {
+	private static final long serialVersionUID = 103237694130052153L;
+	private static Logger logger = Logger.getLogger(OpenWorkflowAction.class);
+	private static final String OPEN_WORKFLOW = "Open workflow...";
+
+	public final OpenCallback DUMMY_OPEN_CALLBACK = new OpenCallbackAdapter();
+	protected FileManager fileManager;
+
+	public OpenWorkflowAction(FileManager fileManager) {
+		super(OPEN_WORKFLOW, openIcon);
+		this.fileManager = fileManager;
+		putValue(
+				ACCELERATOR_KEY,
+				getKeyStroke(VK_O, getDefaultToolkit().getMenuShortcutKeyMask()));
+		putValue(MNEMONIC_KEY, VK_O);
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		final Component parentComponent;
+		if (e.getSource() instanceof Component)
+			parentComponent = (Component) e.getSource();
+		else
+			parentComponent = null;
+		openWorkflows(parentComponent);
+	}
+
+	/**
+	 * Pop up an Open-dialogue to select one or more workflow files to open.
+	 * <p>
+	 * Note that the file opening occurs in a separate thread. If you want to
+	 * check if the file was opened or not, which workflow was opened, etc, use
+	 * {@link #openWorkflows(Component, OpenCallback)} instead.
+	 *
+	 * @see #openWorkflows(Component, OpenCallback)
+	 * @param parentComponent
+	 *            The UI parent component to use for pop up dialogues
+	 *
+	 * @return <code>false</code> if no files were selected or the dialogue was
+	 *         cancelled, or <code>true</code> if the process of opening one or
+	 *         more files has been started.
+	 */
+	public void openWorkflows(Component parentComponent) {
+		openWorkflows(parentComponent, DUMMY_OPEN_CALLBACK);
+	}
+
+	/**
+	 * Open an array of workflow files.
+	 *
+	 * @param parentComponent
+	 *            Parent component for UI dialogues
+	 * @param files
+	 *            Array of files to be opened
+	 * @param fileType
+	 *            {@link FileType} of the files that are to be opened, for
+	 *            instance
+	 *            {@link net.sf.taverna.t2.workbench.file.impl.T2FlowFileType},
+	 *            or <code>null</code> to guess.
+	 * @param openCallback
+	 *            An {@link OpenCallback} to be invoked during and after opening
+	 *            the file. Use {@link OpenWorkflowAction#DUMMY_OPEN_CALLBACK}
+	 *            if no callback is needed.
+	 */
+	public void openWorkflows(final Component parentComponent, File[] files,
+			FileType fileType, OpenCallback openCallback) {
+		ErrorLoggingOpenCallbackWrapper callback = new ErrorLoggingOpenCallbackWrapper(
+				openCallback);
+		for (File file : files)
+			try {
+				Object canonicalSource = fileManager.getCanonical(file);
+				WorkflowBundle alreadyOpen = fileManager.getDataflowBySource(canonicalSource);
+				if (alreadyOpen != null) {
+					/*
+					 * The workflow from the same source is already opened - ask
+					 * the user if they want to switch to it or open another
+					 * copy...
+					 */
+
+					Object[] options = { "Switch to opened", "Open new copy",
+							"Cancel" };
+					switch (showOptionDialog(
+							null,
+							"The workflow from the same location is already opened.\n"
+									+ "Do you want to switch to it or open a new copy?",
+							"File Manager Alert", YES_NO_CANCEL_OPTION,
+							QUESTION_MESSAGE, null, options, // the titles of buttons
+							options[0])) { // default button title
+					case YES_OPTION:
+						fileManager.setCurrentDataflow(alreadyOpen);
+						return;
+					case CANCEL_OPTION:
+						// do nothing
+						return;
+					}
+					// else open the workflow as usual
+				}
+
+				callback.aboutToOpenDataflow(file);
+				WorkflowBundle workflowBundle = fileManager.openDataflow(fileType, file);
+				callback.openedDataflow(file, workflowBundle);
+			} catch (RuntimeException ex) {
+				logger.warn("Failed to open workflow from " + file, ex);
+				if (!callback.couldNotOpenDataflow(file, ex))
+					showErrorMessage(parentComponent, file, ex);
+			} catch (Exception ex) {
+				logger.warn("Failed to open workflow from " + file, ex);
+				if (!callback.couldNotOpenDataflow(file, ex))
+					showErrorMessage(parentComponent, file, ex);
+				return;
+			}
+	}
+
+	/**
+	 * Pop up an Open-dialogue to select one or more workflow files to open.
+	 *
+	 * @param parentComponent
+	 *            The UI parent component to use for pop up dialogues
+	 * @param openCallback
+	 *            An {@link OpenCallback} to be called during the file opening.
+	 *            The callback will be invoked for each file that has been
+	 *            opened, as file opening happens in a separate thread that
+	 *            might execute after the return of this method.
+	 * @return <code>false</code> if no files were selected or the dialogue was
+	 *         cancelled, or <code>true</code> if the process of opening one or
+	 *         more files has been started.
+	 */
+	public boolean openWorkflows(final Component parentComponent,
+			OpenCallback openCallback) {
+		JFileChooser fileChooser = new JFileChooser();
+		Preferences prefs = userNodeForPackage(getClass());
+		String curDir = prefs
+				.get("currentDir", System.getProperty("user.home"));
+		fileChooser.setDialogTitle(OPEN_WORKFLOW);
+
+		fileChooser.resetChoosableFileFilters();
+		fileChooser.setAcceptAllFileFilterUsed(false);
+		List<FileFilter> fileFilters = fileManager.getOpenFileFilters();
+		if (fileFilters.isEmpty()) {
+			logger.warn("No file types found for opening workflow");
+			showMessageDialog(parentComponent,
+					"No file types found for opening workflow.", "Error",
+					ERROR_MESSAGE);
+			return false;
+		}
+		for (FileFilter fileFilter : fileFilters)
+			fileChooser.addChoosableFileFilter(fileFilter);
+		fileChooser.setFileFilter(fileFilters.get(0));
+		fileChooser.setCurrentDirectory(new File(curDir));
+		fileChooser.setMultiSelectionEnabled(true);
+
+		int returnVal = fileChooser.showOpenDialog(parentComponent);
+		if (returnVal == APPROVE_OPTION) {
+			prefs.put("currentDir", fileChooser.getCurrentDirectory()
+					.toString());
+			final File[] selectedFiles = fileChooser.getSelectedFiles();
+			if (selectedFiles.length == 0) {
+				logger.warn("No files selected");
+				return false;
+			}
+			FileFilter fileFilter = fileChooser.getFileFilter();
+			FileType fileType;
+			if (fileFilter instanceof FileTypeFileFilter)
+				fileType = ((FileTypeFileFilter) fileChooser.getFileFilter())
+						.getFileType();
+			else
+				// Unknown filetype, try all of them
+				fileType = null;
+			new FileOpenerThread(parentComponent, selectedFiles, fileType,
+					openCallback).start();
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Show an error message if a file could not be opened
+	 * 
+	 * @param parentComponent
+	 * @param file
+	 * @param throwable
+	 */
+	protected void showErrorMessage(final Component parentComponent,
+			final File file, final Throwable throwable) {
+		invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				Throwable cause = throwable;
+				while (cause.getCause() != null)
+					cause = cause.getCause();
+				showMessageDialog(
+						parentComponent,
+						"Failed to open workflow from " + file + ": \n"
+								+ cause.getMessage(), "Warning",
+						WARNING_MESSAGE);
+			}
+		});
+
+	}
+
+	/**
+	 * Callback interface for openWorkflows().
+	 * <p>
+	 * The callback will be invoked during the invocation of
+	 * {@link OpenWorkflowAction#openWorkflows(Component, OpenCallback)} and
+	 * {@link OpenWorkflowAction#openWorkflows(Component, File[], FileType, OpenCallback)}
+	 * as file opening happens in a separate thread.
+	 *
+	 * @author Stian Soiland-Reyes
+	 */
+	public interface OpenCallback {
+		/**
+		 * Called before a workflowBundle is to be opened from the given file
+		 *
+		 * @param file
+		 *            File which workflowBundle is to be opened
+		 */
+		void aboutToOpenDataflow(File file);
+
+		/**
+		 * Called if an exception happened while attempting to open the
+		 * workflowBundle.
+		 *
+		 * @param file
+		 *            File which was attempted to be opened
+		 * @param ex
+		 *            An {@link OpenException} or a {@link RuntimeException}.
+		 * @return <code>true</code> if the error has been handled, or
+		 *         <code>false</code>3 if a UI warning dialogue is to be opened.
+		 */
+		boolean couldNotOpenDataflow(File file, Exception ex);
+
+		/**
+		 * Called when a workflowBundle has been successfully opened. The workflowBundle
+		 * will be registered in {@link FileManager#getOpenDataflows()}.
+		 *
+		 * @param file
+		 *            File from which workflowBundle was opened
+		 * @param workflowBundle
+		 *            WorkflowBundle that was opened
+		 */
+		void openedDataflow(File file, WorkflowBundle workflowBundle);
+	}
+
+	/**
+	 * Adapter for {@link OpenCallback}
+	 *
+	 * @author Stian Soiland-Reyes
+	 */
+	public static class OpenCallbackAdapter implements OpenCallback {
+		@Override
+		public void aboutToOpenDataflow(File file) {
+		}
+
+		@Override
+		public boolean couldNotOpenDataflow(File file, Exception ex) {
+			return false;
+		}
+
+		@Override
+		public void openedDataflow(File file, WorkflowBundle workflowBundle) {
+		}
+	}
+
+	private final class FileOpenerThread extends Thread {
+		private final File[] files;
+		private final FileType fileType;
+		private final OpenCallback openCallback;
+		private final Component parentComponent;
+
+		private FileOpenerThread(Component parentComponent,
+				File[] selectedFiles, FileType fileType,
+				OpenCallback openCallback) {
+			super("Opening workflows(s) " + Arrays.asList(selectedFiles));
+			this.parentComponent = parentComponent;
+			this.files = selectedFiles;
+			this.fileType = fileType;
+			this.openCallback = openCallback;
+		}
+
+		@Override
+		public void run() {
+			openWorkflows(parentComponent, files, fileType, openCallback);
+		}
+	}
+
+	/**
+	 * A wrapper for {@link OpenCallback} implementations that logs exceptions
+	 * thrown without disrupting the caller of the callback.
+	 *
+	 * @author Stian Soiland-Reyes
+	 */
+	protected class ErrorLoggingOpenCallbackWrapper implements OpenCallback {
+		private final OpenCallback wrapped;
+
+		public ErrorLoggingOpenCallbackWrapper(OpenCallback wrapped) {
+			this.wrapped = wrapped;
+		}
+
+		@Override
+		public void aboutToOpenDataflow(File file) {
+			try {
+				wrapped.aboutToOpenDataflow(file);
+			} catch (RuntimeException wrapperEx) {
+				logger.warn("Failed OpenCallback " + wrapped
+						+ ".aboutToOpenDataflow(File)", wrapperEx);
+			}
+		}
+
+		@Override
+		public boolean couldNotOpenDataflow(File file, Exception ex) {
+			try {
+				return wrapped.couldNotOpenDataflow(file, ex);
+			} catch (RuntimeException wrapperEx) {
+				logger.warn("Failed OpenCallback " + wrapped
+						+ ".couldNotOpenDataflow(File, Exception)", wrapperEx);
+				return false;
+			}
+		}
+
+		@Override
+		public void openedDataflow(File file, WorkflowBundle workflowBundle) {
+			try {
+				wrapped.openedDataflow(file, workflowBundle);
+			} catch (RuntimeException wrapperEx) {
+				logger.warn("Failed OpenCallback " + wrapped
+						+ ".openedDataflow(File, Dataflow)", wrapperEx);
+			}
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowFromURLAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowFromURLAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowFromURLAction.java
new file mode 100644
index 0000000..e98a8f2
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowFromURLAction.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * 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.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_L;
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.QUESTION_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showInputDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.JOptionPane.showOptionDialog;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.openurlIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.net.URL;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.workbench.file.FileManager;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * An action for opening a workflow from a url.
+ * 
+ * @author David Withers
+ */
+public class OpenWorkflowFromURLAction extends AbstractAction {
+	private static final long serialVersionUID = 1474356457949961974L;
+	private static Logger logger = Logger
+			.getLogger(OpenWorkflowFromURLAction.class);
+	private static Preferences prefs = Preferences
+			.userNodeForPackage(OpenWorkflowFromURLAction.class);
+	private static final String PREF_CURRENT_URL = "currentUrl";
+	private static final String ACTION_NAME = "Open workflow location...";
+	private static final String ACTION_DESCRIPTION = "Open a workflow from the web into a new workflow";
+
+	private Component component;
+	private FileManager fileManager;
+
+	public OpenWorkflowFromURLAction(final Component component,
+			FileManager fileManager) {
+		this.component = component;
+		this.fileManager = fileManager;
+		putValue(SMALL_ICON, openurlIcon);
+		putValue(NAME, ACTION_NAME);
+		putValue(SHORT_DESCRIPTION, ACTION_DESCRIPTION);
+		putValue(MNEMONIC_KEY, VK_L);
+		putValue(
+				ACCELERATOR_KEY,
+				getKeyStroke(VK_L, getDefaultToolkit().getMenuShortcutKeyMask()));
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		String currentUrl = prefs.get(PREF_CURRENT_URL, "http://");
+
+		final String url = (String) showInputDialog(component,
+				"Enter the URL of a workflow definition to load",
+				"Workflow URL", QUESTION_MESSAGE, null, null, currentUrl);
+		if (url != null)
+			new Thread("OpenWorkflowFromURLAction") {
+				@Override
+				public void run() {
+					openFromURL(url);
+				}
+			}.start();
+	}
+
+	private void openFromURL(String urlString) {
+		try {
+			URL url = new URL(urlString);
+
+			Object canonicalSource = fileManager.getCanonical(url);
+			WorkflowBundle alreadyOpen = fileManager
+					.getDataflowBySource(canonicalSource);
+			if (alreadyOpen != null) {
+				/*
+				 * The workflow from the same source is already opened - ask the
+				 * user if they want to switch to it or open another copy.
+				 */
+
+				Object[] options = { "Switch to opened", "Open new copy",
+						"Cancel" };
+				int iSelected = showOptionDialog(
+						null,
+						"The workflow from the same location is already opened.\n"
+								+ "Do you want to switch to it or open a new copy?",
+						"File Manager Alert", YES_NO_CANCEL_OPTION,
+						QUESTION_MESSAGE, null, options, // the titles of buttons
+						options[0]); // default button title
+
+				if (iSelected == YES_OPTION) {
+					fileManager.setCurrentDataflow(alreadyOpen);
+					return;
+				} else if (iSelected == CANCEL_OPTION) {
+					// do nothing
+					return;
+				}
+				// else open the workflow as usual
+			}
+
+			fileManager.openDataflow(null, url);
+			prefs.put(PREF_CURRENT_URL, urlString);
+		} catch (Exception ex) {
+			logger.warn("Failed to open the workflow from url " + urlString
+					+ " \n", ex);
+			showMessageDialog(component,
+					"Failed to open the workflow from url " + urlString + " \n"
+							+ ex.getMessage(), "Error!", ERROR_MESSAGE);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/PasswordInput.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/PasswordInput.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/PasswordInput.java
new file mode 100644
index 0000000..401a232
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/PasswordInput.java
@@ -0,0 +1,221 @@
+/*******************************************************************************
+ * 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.file.impl.actions;
+
+import static java.awt.EventQueue.invokeLater;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.log4j.Logger;
+
+/**
+ * Simple dialogue to handle username/password input for workflow URL requiring
+ * http authentication.
+ * 
+ * @author Stuart Owen
+ * @author Stian Soiland-Reyes
+ * @author Alan R Williams
+ */
+@SuppressWarnings("serial")
+public class PasswordInput extends HelpEnabledDialog {
+	private static Logger logger = Logger.getLogger(PasswordInput.class);
+
+	private String password = null;
+	private String username = null;
+	private URL url = null;
+	private int tryCount = 0;
+	private final static int MAX_TRIES = 3;
+
+	private JButton cancelButton;
+	private JLabel jLabel1;
+	private JLabel jLabel2;
+	private JLabel messageLabel;
+	private JButton okButton;
+	private JPasswordField passwordTextField;
+	private JLabel urlLabel;
+	private JTextField usernameTextField;
+
+	public void setUrl(URL url) {
+		this.url = url;
+		urlLabel.setText(url.toExternalForm());
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public String getUsername() {
+		return username;
+	}
+
+	public PasswordInput(JFrame parent) {
+		super(parent, "Authorization", true, null);
+		initComponents();
+	}
+
+	/** Creates new form PasswordInput */
+	public PasswordInput() {
+		super((JFrame) null, "Authorization", true, null);
+		initComponents();
+	}
+
+	/**
+	 * This method is called from within the constructor to initialize the form.
+	 * WARNING: Do NOT modify this code. The content of this method is always
+	 * regenerated by the Form Editor.
+	 */
+	private void initComponents() {
+		usernameTextField = new javax.swing.JTextField();
+		cancelButton = new javax.swing.JButton();
+		okButton = new javax.swing.JButton();
+		passwordTextField = new javax.swing.JPasswordField();
+		jLabel1 = new javax.swing.JLabel();
+		jLabel2 = new javax.swing.JLabel();
+		messageLabel = new javax.swing.JLabel();
+		urlLabel = new javax.swing.JLabel();
+
+		getContentPane().setLayout(null);
+
+		setModal(true);
+		// setResizable(false);
+		getContentPane().add(usernameTextField);
+		usernameTextField.setBounds(20, 80, 280, 22);
+
+		cancelButton.setText("Cancel");
+		cancelButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				cancelButtonActionPerformed(evt);
+			}
+		});
+
+		getContentPane().add(cancelButton);
+		cancelButton.setBounds(230, 160, 75, 29);
+
+		okButton.setText("OK");
+		okButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				okButtonActionPerformed(evt);
+			}
+		});
+
+		getContentPane().add(okButton);
+		okButton.setBounds(150, 160, 75, 29);
+
+		getContentPane().add(passwordTextField);
+		passwordTextField.setBounds(20, 130, 280, 22);
+
+		jLabel1.setText("Username");
+		getContentPane().add(jLabel1);
+		jLabel1.setBounds(20, 60, 70, 16);
+
+		jLabel2.setText("Password");
+		getContentPane().add(jLabel2);
+		jLabel2.setBounds(20, 110, 70, 16);
+
+		messageLabel.setText("A username and password is required for:");
+		getContentPane().add(messageLabel);
+		messageLabel.setBounds(20, 10, 270, 20);
+
+		urlLabel.setText("service");
+		getContentPane().add(urlLabel);
+		urlLabel.setBounds(20, 30, 270, 16);
+
+		pack();
+	}
+
+	private void okButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
+		String password = String.valueOf(passwordTextField.getPassword());
+		String username = usernameTextField.getText();
+		HttpURLConnection connection;
+		try {
+			connection = (HttpURLConnection) url.openConnection();
+			String userPassword = username + ":" + password;
+			/*
+			 * Note: non-latin1 support for basic auth is fragile/unsupported
+			 * and must be MIME-encoded (RFC2047) according to
+			 * https://bugzilla.mozilla.org/show_bug.cgi?id=41489
+			 */
+			byte[] encoded = Base64.encodeBase64(userPassword
+					.getBytes("latin1"));
+			connection.setRequestProperty("Authorization", "Basic "
+					+ new String(encoded, "ascii"));
+			connection.setRequestProperty("Accept", "text/xml");
+			int code = connection.getResponseCode();
+
+			/*
+			 * NB: myExperiment gives a 500 response for an invalid
+			 * username/password
+			 */
+			if (code == 401 || code == 500) {
+				tryCount++;
+				showMessageDialog(this, "The username and password failed",
+						"Invalid username or password", ERROR_MESSAGE);
+				if (tryCount >= MAX_TRIES) { // close after 3 attempts.
+					this.password = null;
+					this.username = null;
+					this.setVisible(false);
+				}
+			} else {
+				this.username = username;
+				this.password = password;
+				this.setVisible(false);
+			}
+		} catch (IOException ex) {
+			logger.error("Could not get password", ex);
+		}
+	}
+
+	private void cancelButtonActionPerformed(ActionEvent evt) {
+		this.password = null;
+		this.username = null;
+		this.setVisible(false);
+	}
+
+	/**
+	 * @param args
+	 *            the command line arguments
+	 */
+	public static void main(String args[]) {
+		invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				new PasswordInput().setVisible(true);
+			}
+		});
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveAllWorkflowsAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveAllWorkflowsAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveAllWorkflowsAction.java
new file mode 100644
index 0000000..6b011d3
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveAllWorkflowsAction.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * 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.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
+import static java.awt.event.KeyEvent.VK_A;
+import static java.awt.event.KeyEvent.VK_S;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.saveAllIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+@SuppressWarnings("serial")
+public class SaveAllWorkflowsAction extends AbstractAction {
+	private final class FileManagerObserver implements
+			Observer<FileManagerEvent> {
+		@Override
+		public void notify(Observable<FileManagerEvent> sender,
+				FileManagerEvent message) throws Exception {
+			updateEnabled();
+		}
+	}
+
+	@SuppressWarnings("unused")
+	private static Logger logger = Logger
+			.getLogger(SaveAllWorkflowsAction.class);
+	private static final String SAVE_ALL_WORKFLOWS = "Save all workflows";
+
+	private final SaveWorkflowAction saveWorkflowAction;
+	private FileManager fileManager;
+	private FileManagerObserver fileManagerObserver = new FileManagerObserver();
+
+	public SaveAllWorkflowsAction(EditManager editManager,
+			FileManager fileManager) {
+		super(SAVE_ALL_WORKFLOWS, saveAllIcon);
+		this.fileManager = fileManager;
+		saveWorkflowAction = new SaveWorkflowAction(editManager, fileManager);
+		putValue(
+				ACCELERATOR_KEY,
+				getKeyStroke(VK_S, getDefaultToolkit().getMenuShortcutKeyMask()
+						| SHIFT_DOWN_MASK));
+		putValue(MNEMONIC_KEY, VK_A);
+
+		fileManager.addObserver(fileManagerObserver);
+		updateEnabled();
+	}
+
+	public void updateEnabled() {
+		setEnabled(!(fileManager.getOpenDataflows().isEmpty()));
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent ev) {
+		Component parentComponent = null;
+		if (ev.getSource() instanceof Component)
+			parentComponent = (Component) ev.getSource();
+		saveAllDataflows(parentComponent);
+	}
+
+	public void saveAllDataflows(Component parentComponent) {
+		// Save in reverse so we save nested workflows first
+		List<WorkflowBundle> workflowBundles = fileManager.getOpenDataflows();
+		Collections.reverse(workflowBundles);
+
+		for (WorkflowBundle workflowBundle : workflowBundles)
+			if (!saveWorkflowAction.saveDataflow(parentComponent,
+					workflowBundle))
+				break;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAction.java
new file mode 100644
index 0000000..9776550
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAction.java
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * 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.file.impl.actions;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_S;
+import static javax.swing.JOptionPane.NO_OPTION;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.saveIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.file.events.SavedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.SetCurrentDataflowEvent;
+import net.sf.taverna.t2.workbench.file.exceptions.OverwriteException;
+import net.sf.taverna.t2.workbench.file.exceptions.SaveException;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+@SuppressWarnings("serial")
+public class SaveWorkflowAction extends AbstractAction {
+	private static Logger logger = Logger.getLogger(SaveWorkflowAction.class);
+	private static final String SAVE_WORKFLOW = "Save workflow";
+
+	private final SaveWorkflowAsAction saveWorkflowAsAction;
+	private EditManagerObserver editManagerObserver = new EditManagerObserver();
+	private FileManager fileManager;
+	private FileManagerObserver fileManagerObserver = new FileManagerObserver();
+
+	public SaveWorkflowAction(EditManager editManager, FileManager fileManager) {
+		super(SAVE_WORKFLOW, saveIcon);
+		this.fileManager = fileManager;
+		saveWorkflowAsAction = new SaveWorkflowAsAction(fileManager);
+		putValue(
+				ACCELERATOR_KEY,
+				getKeyStroke(VK_S, getDefaultToolkit().getMenuShortcutKeyMask()));
+		putValue(MNEMONIC_KEY, VK_S);
+		editManager.addObserver(editManagerObserver);
+		fileManager.addObserver(fileManagerObserver);
+		updateEnabledStatus(fileManager.getCurrentDataflow());
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent ev) {
+		Component parentComponent = null;
+		if (ev.getSource() instanceof Component)
+			parentComponent = (Component) ev.getSource();
+		saveCurrentDataflow(parentComponent);
+	}
+
+	public boolean saveCurrentDataflow(Component parentComponent) {
+		WorkflowBundle workflowBundle = fileManager.getCurrentDataflow();
+		return saveDataflow(parentComponent, workflowBundle);
+	}
+
+	public boolean saveDataflow(Component parentComponent,
+			WorkflowBundle workflowBundle) {
+		if (!fileManager.canSaveWithoutDestination(workflowBundle))
+			return saveWorkflowAsAction.saveDataflow(parentComponent,
+					workflowBundle);
+
+		try {
+			try {
+				fileManager.saveDataflow(workflowBundle, true);
+				Object workflowBundleSource = fileManager
+						.getDataflowSource(workflowBundle);
+				logger.info("Saved workflow " + workflowBundle + " to "
+						+ workflowBundleSource);
+				return true;
+			} catch (OverwriteException ex) {
+				Object workflowBundleSource = fileManager
+						.getDataflowSource(workflowBundle);
+				logger.info("Workflow was changed on source: "
+						+ workflowBundleSource);
+				fileManager.setCurrentDataflow(workflowBundle);
+				String msg = "Workflow destination " + workflowBundleSource
+						+ " has been changed from elsewhere, "
+						+ "are you sure you want to overwrite?";
+				int ret = showConfirmDialog(parentComponent, msg,
+						"Workflow changed", YES_NO_CANCEL_OPTION);
+				if (ret == YES_OPTION) {
+					fileManager.saveDataflow(workflowBundle, false);
+					logger.info("Saved workflow " + workflowBundle
+							+ " by overwriting " + workflowBundleSource);
+					return true;
+				} else if (ret == NO_OPTION) {
+					// Pop up Save As instead to choose another name
+					return saveWorkflowAsAction.saveDataflow(parentComponent,
+							workflowBundle);
+				} else {
+					logger.info("Aborted overwrite of " + workflowBundleSource);
+					return false;
+				}
+			}
+		} catch (SaveException ex) {
+			logger.warn("Could not save workflow " + workflowBundle, ex);
+			showMessageDialog(parentComponent, "Could not save workflow: \n\n"
+					+ ex.getMessage(), "Warning", WARNING_MESSAGE);
+			return false;
+		} catch (RuntimeException ex) {
+			logger.warn("Could not save workflow " + workflowBundle, ex);
+			showMessageDialog(parentComponent, "Could not save workflow: \n\n"
+					+ ex.getMessage(), "Warning", WARNING_MESSAGE);
+			return false;
+		}
+	}
+
+	protected void updateEnabledStatus(WorkflowBundle workflowBundle) {
+		setEnabled(workflowBundle != null
+				&& fileManager.isDataflowChanged(workflowBundle));
+	}
+
+	private final class EditManagerObserver implements
+			Observer<EditManagerEvent> {
+		@Override
+		public void notify(Observable<EditManagerEvent> sender,
+				EditManagerEvent message) throws Exception {
+			if (message instanceof AbstractDataflowEditEvent) {
+				WorkflowBundle workflowBundle = ((AbstractDataflowEditEvent) message)
+						.getDataFlow();
+				if (workflowBundle == fileManager.getCurrentDataflow())
+					updateEnabledStatus(workflowBundle);
+			}
+		}
+	}
+
+	private final class FileManagerObserver implements
+			Observer<FileManagerEvent> {
+		@Override
+		public void notify(Observable<FileManagerEvent> sender,
+				FileManagerEvent message) throws Exception {
+			if (message instanceof SavedDataflowEvent)
+				updateEnabledStatus(((SavedDataflowEvent) message)
+						.getDataflow());
+			else if (message instanceof SetCurrentDataflowEvent)
+				updateEnabledStatus(((SetCurrentDataflowEvent) message)
+						.getDataflow());
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAsAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAsAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAsAction.java
new file mode 100644
index 0000000..1872d5d
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAsAction.java
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * 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.file.impl.actions;
+
+import static java.awt.event.KeyEvent.VK_F6;
+import static java.awt.event.KeyEvent.VK_S;
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.NO_OPTION;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.saveAsIcon;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.net.URL;
+import java.util.List;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.file.events.SetCurrentDataflowEvent;
+import net.sf.taverna.t2.workbench.file.exceptions.OverwriteException;
+import net.sf.taverna.t2.workbench.file.exceptions.SaveException;
+import net.sf.taverna.t2.workbench.file.impl.FileTypeFileFilter;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+@SuppressWarnings("serial")
+public class SaveWorkflowAsAction extends AbstractAction {
+	private static final String SAVE_WORKFLOW_AS = "Save workflow as...";
+	private static final String PREF_CURRENT_DIR = "currentDir";
+	private static Logger logger = Logger.getLogger(SaveWorkflowAsAction.class);
+	private FileManager fileManager;
+
+	public SaveWorkflowAsAction(FileManager fileManager) {
+		super(SAVE_WORKFLOW_AS, saveAsIcon);
+		this.fileManager = fileManager;
+		fileManager.addObserver(new FileManagerObserver());
+		putValue(ACCELERATOR_KEY, getKeyStroke(VK_F6, 0));
+		putValue(MNEMONIC_KEY, VK_S);
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		Component parentComponent = null;
+		if (e.getSource() instanceof Component)
+			parentComponent = (Component) e.getSource();
+		WorkflowBundle workflowBundle = fileManager.getCurrentDataflow();
+		if (workflowBundle == null) {
+			showMessageDialog(parentComponent, "No workflow open yet",
+					"No workflow to save", ERROR_MESSAGE);
+			return;
+		}
+		saveCurrentDataflow(parentComponent);
+	}
+
+	public boolean saveCurrentDataflow(Component parentComponent) {
+		WorkflowBundle workflowBundle = fileManager.getCurrentDataflow();
+		return saveDataflow(parentComponent, workflowBundle);
+	}
+
+	private String determineFileName(final WorkflowBundle workflowBundle) {
+		String result;
+		Object source = fileManager.getDataflowSource(workflowBundle);
+		String fileName = null;
+		if (source instanceof File)
+			fileName = ((File) source).getName();
+		else if (source instanceof URL)
+			fileName = ((URL) source).getPath();
+
+		if (fileName != null) {
+			int lastIndex = fileName.lastIndexOf(".");
+			if (lastIndex > 0)
+				fileName = fileName.substring(0, fileName.lastIndexOf("."));
+			result = fileName;
+		} else {
+			Workflow mainWorkflow = workflowBundle.getMainWorkflow();
+			if (mainWorkflow != null)
+				result = mainWorkflow.getName();
+			else
+				result = workflowBundle.getName();
+		}
+		return result;
+	}
+
+	public boolean saveDataflow(Component parentComponent, WorkflowBundle workflowBundle) {
+		fileManager.setCurrentDataflow(workflowBundle);
+		JFileChooser fileChooser = new JFileChooser();
+		Preferences prefs = Preferences.userNodeForPackage(getClass());
+		String curDir = prefs
+				.get(PREF_CURRENT_DIR, System.getProperty("user.home"));
+		fileChooser.setDialogTitle(SAVE_WORKFLOW_AS);
+
+		fileChooser.resetChoosableFileFilters();
+		fileChooser.setAcceptAllFileFilterUsed(false);
+
+		List<FileFilter> fileFilters = fileManager
+				.getSaveFileFilters(File.class);
+		if (fileFilters.isEmpty()) {
+			logger.warn("No file types found for saving workflow "
+					+ workflowBundle);
+			showMessageDialog(parentComponent,
+					"No file types found for saving workflow.", "Error",
+					ERROR_MESSAGE);
+			return false;
+		}
+		for (FileFilter fileFilter : fileFilters)
+			fileChooser.addChoosableFileFilter(fileFilter);
+		fileChooser.setFileFilter(fileFilters.get(0));
+		fileChooser.setCurrentDirectory(new File(curDir));
+
+		File possibleName = new File(determineFileName(workflowBundle));
+		boolean tryAgain = true;
+		while (tryAgain) {
+			tryAgain = false;
+			fileChooser.setSelectedFile(possibleName);
+			int returnVal = fileChooser.showSaveDialog(parentComponent);
+			if (returnVal == APPROVE_OPTION) {
+				prefs.put(PREF_CURRENT_DIR, fileChooser.getCurrentDirectory()
+						.toString());
+				File file = fileChooser.getSelectedFile();
+				FileTypeFileFilter fileFilter = (FileTypeFileFilter) fileChooser
+						.getFileFilter();
+				FileType fileType = fileFilter.getFileType();
+				String extension = "." + fileType.getExtension();
+				if (!file.getName().toLowerCase().endsWith(extension)) {
+					String newName = file.getName() + extension;
+					file = new File(file.getParentFile(), newName);
+				}
+
+				// TODO: Open in separate thread to avoid hanging UI
+				try {
+					try {
+						fileManager.saveDataflow(workflowBundle, fileType,
+								file, true);
+						logger.info("Saved workflow " + workflowBundle + " to "
+								+ file);
+						return true;
+					} catch (OverwriteException ex) {
+						logger.info("File already exists: " + file);
+						String msg = "Are you sure you want to overwrite existing file "
+								+ file + "?";
+						int ret = showConfirmDialog(parentComponent, msg,
+								"File already exists", YES_NO_CANCEL_OPTION);
+						if (ret == YES_OPTION) {
+							fileManager.saveDataflow(workflowBundle, fileType,
+									file, false);
+							logger.info("Saved workflow " + workflowBundle
+									+ " by overwriting " + file);
+							return true;
+						} else if (ret == NO_OPTION) {
+							tryAgain = true;
+							continue;
+						} else {
+							logger.info("Aborted overwrite of " + file);
+							return false;
+						}
+					}
+				} catch (SaveException ex) {
+					logger.warn("Could not save workflow to " + file, ex);
+					showMessageDialog(parentComponent,
+							"Could not save workflow to " + file + ": \n\n"
+									+ ex.getMessage(), "Warning",
+							WARNING_MESSAGE);
+					return false;
+				}
+			}
+		}
+		return false;
+	}
+
+	protected void updateEnabledStatus(WorkflowBundle workflowBundle) {
+		setEnabled(workflowBundle != null);
+	}
+
+	private final class FileManagerObserver implements Observer<FileManagerEvent> {
+		@Override
+		public void notify(Observable<FileManagerEvent> sender,
+				FileManagerEvent message) throws Exception {
+			if (message instanceof SetCurrentDataflowEvent)
+				updateEnabledStatus(((SetCurrentDataflowEvent) message)
+						.getDataflow());
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/hooks/CloseWorkflowsOnShutdown.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/hooks/CloseWorkflowsOnShutdown.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/hooks/CloseWorkflowsOnShutdown.java
new file mode 100644
index 0000000..6c0be19
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/hooks/CloseWorkflowsOnShutdown.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (C) 2010 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.file.impl.hooks;
+
+import net.sf.taverna.t2.workbench.ShutdownSPI;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.CloseAllWorkflowsAction;
+
+/**
+ * Close open workflows (and ask the user if she wants to save changes) on
+ * shutdown.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class CloseWorkflowsOnShutdown implements ShutdownSPI {
+	private CloseAllWorkflowsAction closeAllWorkflowsAction;
+
+	public CloseWorkflowsOnShutdown(EditManager editManager,
+			FileManager fileManager) {
+		closeAllWorkflowsAction = new CloseAllWorkflowsAction(editManager,
+				fileManager);
+	}
+
+	@Override
+	public int positionHint() {
+		/*
+		 * Quite early, we don't want to do various clean-up in case the user
+		 * clicks Cancel
+		 */
+		return 50;
+	}
+
+	@Override
+	public boolean shutdown() {
+		return closeAllWorkflowsAction.closeAllWorkflows(null);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseAllMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseAllMenuAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseAllMenuAction.java
new file mode 100644
index 0000000..e8e5252
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseAllMenuAction.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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.file.impl.menu;
+
+import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_URI;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.CloseAllWorkflowsAction;
+
+public class FileCloseAllMenuAction extends AbstractMenuAction {
+	private static final URI FILE_CLOSE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileCloseAll");
+	private final EditManager editManager;
+	private final FileManager fileManager;
+
+	public FileCloseAllMenuAction(EditManager editManager,
+			FileManager fileManager) {
+		super(FILE_URI, 39, FILE_CLOSE_URI);
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new CloseAllWorkflowsAction(editManager, fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseMenuAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseMenuAction.java
new file mode 100644
index 0000000..a97219f
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseMenuAction.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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.file.impl.menu;
+
+import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_URI;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.CloseWorkflowAction;
+
+public class FileCloseMenuAction extends AbstractMenuAction {
+	private static final URI FILE_CLOSE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileClose");
+	private final EditManager editManager;
+	private final FileManager fileManager;
+
+	public FileCloseMenuAction(EditManager editManager, FileManager fileManager) {
+		super(FILE_URI, 30, FILE_CLOSE_URI);
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new CloseWorkflowAction(editManager, fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileNewMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileNewMenuAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileNewMenuAction.java
new file mode 100644
index 0000000..3a48e0d
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileNewMenuAction.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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.file.impl.menu;
+
+import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_OPEN_SECTION_URI;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.NewWorkflowAction;
+
+public class FileNewMenuAction extends AbstractMenuAction {
+	private static final URI FILE_NEW_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileNew");
+	private final FileManager fileManager;
+
+	public FileNewMenuAction(FileManager fileManager) {
+		super(FILE_OPEN_SECTION_URI, 10, FILE_NEW_URI);
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new NewWorkflowAction(fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenFromURLMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenFromURLMenuAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenFromURLMenuAction.java
new file mode 100644
index 0000000..9af1d6b
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenFromURLMenuAction.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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.file.impl.menu;
+
+import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_OPEN_SECTION_URI;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.OpenWorkflowFromURLAction;
+
+public class FileOpenFromURLMenuAction extends AbstractMenuAction {
+
+	private static final URI FILE_OPEN_FROM_URL_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileOpenURL");
+	private final FileManager fileManager;
+
+	public FileOpenFromURLMenuAction(FileManager fileManager) {
+		super(FILE_OPEN_SECTION_URI, 30, FILE_OPEN_FROM_URL_URI);
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new OpenWorkflowFromURLAction(null, fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuAction.java
new file mode 100644
index 0000000..4ee4e39
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuAction.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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.file.impl.menu;
+
+import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_OPEN_SECTION_URI;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.OpenWorkflowAction;
+
+public class FileOpenMenuAction extends AbstractMenuAction {
+	private static final URI FILE_OPEN_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileOpen");
+	private final FileManager fileManager;
+
+	public FileOpenMenuAction(FileManager fileManager) {
+		super(FILE_OPEN_SECTION_URI, 20, FILE_OPEN_URI);
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new OpenWorkflowAction(fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuSection.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuSection.java
new file mode 100644
index 0000000..46ef476
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuSection.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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.file.impl.menu;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+public class FileOpenMenuSection extends AbstractMenuSection {
+	public static final URI FILE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#file");
+	public static final URI FILE_OPEN_SECTION_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileOpenSection");
+
+	public FileOpenMenuSection() {
+		super(FILE_URI, 20, FILE_OPEN_SECTION_URI);
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigUIFactory.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigUIFactory.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigUIFactory.java
new file mode 100644
index 0000000..8746b54
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigUIFactory.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.config;
+
+import javax.swing.JPanel;
+
+import uk.org.taverna.configuration.Configurable;
+import uk.org.taverna.configuration.ConfigurationUIFactory;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration;
+
+public class ServiceDescriptionConfigUIFactory implements ConfigurationUIFactory {
+	private ServiceDescriptionsConfiguration serviceDescriptionsConfiguration;
+	private ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	@Override
+	public boolean canHandle(String uuid) {
+		return uuid.equals(serviceDescriptionsConfiguration.getUUID());
+	}
+
+	@Override
+	public Configurable getConfigurable() {
+		return serviceDescriptionsConfiguration;
+	}
+
+	@Override
+	public JPanel getConfigurationPanel() {
+		return new ServiceDescriptionConfigPanel(serviceDescriptionsConfiguration, serviceDescriptionRegistry);
+	}
+
+	public void setServiceDescriptionRegistry(ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	public void setServiceDescriptionsConfiguration(ServiceDescriptionsConfiguration serviceDescriptionsConfiguration) {
+		this.serviceDescriptionsConfiguration = serviceDescriptionsConfiguration;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/menu/AddServiceProviderMenu.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/menu/AddServiceProviderMenu.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/menu/AddServiceProviderMenu.java
new file mode 100644
index 0000000..f975778
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/menu/AddServiceProviderMenu.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.menu;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JPopupMenu;
+
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+import net.sf.taverna.t2.workbench.ui.servicepanel.ServicePanel;
+import net.sf.taverna.t2.workbench.ui.servicepanel.actions.AddServiceProviderAction;
+
+/**
+ * A menu that provides a set up menu actions for adding new service providers
+ * to the Service Panel.
+ * <p>
+ * The Actions are discovered from the {@link ServiceDescriptionProvider}s found
+ * through the SPI.
+ *
+ * @author Stuart Owen
+ * @author Stian Soiland-Reyes
+ * @author Alan R Williams
+ *
+ * @see ServiceDescription
+ * @see ServicePanel
+ * @see ServiceDescriptionRegistry#addServiceDescriptionProvider(ServiceDescriptionProvider)
+ */
+@SuppressWarnings("serial")
+public class AddServiceProviderMenu extends JButton {
+	public static class ServiceProviderComparator implements
+			Comparator<ServiceDescriptionProvider> {
+		@Override
+		public int compare(ServiceDescriptionProvider o1,
+				ServiceDescriptionProvider o2) {
+			return o1.getName().toLowerCase().compareTo(
+					o2.getName().toLowerCase());
+		}
+	}
+
+	private final static String ADD_SERVICE_PROVIDER_MENU_NAME = "Import new services";
+
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+
+	public AddServiceProviderMenu(ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		super();
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+
+		final Component c = createCustomComponent();
+		setAction(new AbstractAction(ADD_SERVICE_PROVIDER_MENU_NAME) {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				((JPopupMenu) c).show(AddServiceProviderMenu.this, 0,
+						AddServiceProviderMenu.this.getHeight());
+			}
+		});
+	}
+
+	private Component createCustomComponent() {
+		JPopupMenu addServiceMenu = new JPopupMenu(
+				ADD_SERVICE_PROVIDER_MENU_NAME);
+		addServiceMenu.setToolTipText("Add a new service provider");
+		boolean isEmpty = true;
+		List<ConfigurableServiceProvider> providers = new ArrayList<>(
+				serviceDescriptionRegistry.getUnconfiguredServiceProviders());
+		Collections.sort(providers,  new ServiceProviderComparator());
+		for (ConfigurableServiceProvider provider : providers) {
+			/*
+			 * Skip BioCatalogue's ConfigurableServiceProviderS as they should
+			 * not be used to add servcie directlry but rather though the
+			 * Service Catalogue perspective
+			 */
+			if (provider.getId().toLowerCase().contains("servicecatalogue"))
+				continue;
+
+			AddServiceProviderAction addAction = new AddServiceProviderAction(
+					provider, this);
+			addAction.setServiceDescriptionRegistry(serviceDescriptionRegistry);
+			addServiceMenu.add(addAction);
+			isEmpty = false;
+		}
+		if (isEmpty)
+			addServiceMenu.setEnabled(false);
+		return addServiceMenu;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/Filter.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/Filter.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/Filter.java
new file mode 100644
index 0000000..e67e8f5
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/Filter.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.tree;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+
+public interface Filter {
+	boolean pass(DefaultMutableTreeNode node);
+
+	String filterRepresentation(String original);
+
+	void setSuperseded(boolean superseded);
+
+	boolean isSuperseded();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeCellRenderer.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeCellRenderer.java
new file mode 100644
index 0000000..21f43c5
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeCellRenderer.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.tree;
+
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.folderClosedIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.folderOpenIcon;
+
+import java.awt.Component;
+
+import javax.swing.JTree;
+import javax.swing.tree.DefaultTreeCellRenderer;
+
+@SuppressWarnings("serial")
+public class FilterTreeCellRenderer extends DefaultTreeCellRenderer {
+	private Filter filter = null;
+
+	@Override
+	public Component getTreeCellRendererComponent(JTree tree, Object value,
+			boolean sel, boolean expanded, boolean leaf, int row,
+			boolean hasFocus) {
+
+		super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
+				row, hasFocus);
+		Filter filter = getFilter();
+		if (filter != null)
+			setText(filter.filterRepresentation(getText()));
+		if (expanded)
+			setIcon(folderOpenIcon);
+		else
+			setIcon(folderClosedIcon);
+		return this;
+	}
+
+	public Filter getFilter() {
+		return filter;
+	}
+
+	public void setFilter(Filter currentFilter) {
+		this.filter = currentFilter;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeModel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeModel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeModel.java
new file mode 100644
index 0000000..191ed66
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeModel.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.ui.servicepanel.tree;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreePath;
+
+import org.apache.log4j.Logger;
+
+public final class FilterTreeModel extends DefaultTreeModel {
+	private static final long serialVersionUID = -8931308369832839862L;
+	private static final Logger logger = Logger
+			.getLogger(FilterTreeModel.class);
+	
+	Filter currentFilter;
+
+	public FilterTreeModel(FilterTreeNode node) {
+		this(node, null);
+	}
+	
+	public FilterTreeModel(FilterTreeNode node, Filter filter) {
+		super(node);
+		currentFilter = filter;
+		node.setFilter(filter);
+	}
+
+	public void setFilter(Filter filter) {
+		if (root != null) {
+			currentFilter = filter;
+			((FilterTreeNode) root).setFilter(filter);
+			Object[] path = { root };
+			fireTreeStructureChanged(this, path, null, null);
+		}
+	}
+
+	@Override
+	public int getChildCount(Object parent) {
+		if (parent instanceof FilterTreeNode)
+			return (((FilterTreeNode) parent).getChildCount());
+		return 0;
+	}
+
+	@Override
+	public Object getChild(Object parent, int index) {
+		if (parent instanceof FilterTreeNode)
+			return (((FilterTreeNode) parent).getChildAt(index));
+		return null;
+	}
+
+	/**
+	 * @return the currentFilter
+	 */
+	public Filter getCurrentFilter() {
+		return currentFilter;
+	}
+
+	public TreePath getTreePathForObjectPath(List<Object> path) {
+		List<FilterTreeNode> resultList = new ArrayList<>();
+		FilterTreeNode current = (FilterTreeNode) root;
+		resultList.add(current);
+		for (int i = 1; (i < path.size()) && (current != null); i++) {
+			logger.debug("Looking in " + current.getUserObject() + " for " + path.get(i));
+			current = current.getChildForObject(path.get(i));
+			if (current != null)
+				resultList.add(current);
+		}
+		if (current != null)
+			return new TreePath(resultList.toArray());
+		return null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeNode.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeNode.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeNode.java
new file mode 100644
index 0000000..83fd439
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeNode.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.tree;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+
+import org.apache.log4j.Logger;
+
+public class FilterTreeNode extends DefaultMutableTreeNode {
+	private static final long serialVersionUID = 1933553584349932151L;
+	@SuppressWarnings("unused")
+	private static Logger logger = Logger.getLogger(FilterTreeNode.class);
+	
+	private Filter filter;
+	private boolean passed = true;
+	private List<FilterTreeNode> filteredChildren = new ArrayList<>();
+
+	public FilterTreeNode(Object userObject) {
+		super(userObject);
+		userObject.toString();
+	}
+
+	public Filter getFilter() {
+		return filter;
+	}
+	
+	public void setFilter(Filter filter) {
+		if ((filter == null) || !filter.isSuperseded()) {
+			this.filter = filter;
+			passed = false;
+			filteredChildren.clear();
+			if (filter == null) {
+				passed = true;
+				passFilterDown(null);
+			} else if (filter.pass(this)) {
+				passed = true;
+				passFilterDown(null);
+			} else {
+				passFilterDown(filter);
+				passed = filteredChildren.size() != 0;
+			}
+		}
+	}
+
+	private void passFilterDown(Filter filter) {
+		int realChildCount = super.getChildCount();
+		for (int i = 0; i < realChildCount; i++) {
+			FilterTreeNode realChild = (FilterTreeNode) super.getChildAt(i);
+			realChild.setFilter(filter);
+			if (realChild.isPassed())
+				filteredChildren.add(realChild);
+		}
+	}
+
+	public void add(FilterTreeNode node) {
+		super.add(node);
+		node.setFilter(filter);
+		// TODO work up
+		if (node.isPassed())
+			filteredChildren.add(node);
+	}
+	
+	@Override
+	public void remove(int childIndex) {
+		if (filter != null)
+			// as child indexes might be inconsistent..
+			throw new IllegalStateException("Can't remove while the filter is active");
+		super.remove(childIndex);
+	}
+
+	@Override
+	public int getChildCount() {
+		if (filter == null)
+			return super.getChildCount();
+		return filteredChildren.size();
+	}
+
+	@Override
+	public FilterTreeNode getChildAt(int index) {
+		if (filter == null)
+			return (FilterTreeNode) super.getChildAt(index);
+		return filteredChildren.get(index);
+	}
+
+	public boolean isPassed() {
+		return passed;
+	}
+	
+	public Set<FilterTreeNode> getLeaves() {
+		Set<FilterTreeNode> result = new HashSet<>();
+		if (super.getChildCount() == 0) {
+			result.add(this);
+			return result;
+		}
+
+		for (int i = 0; i < super.getChildCount(); i++) {
+			FilterTreeNode child = (FilterTreeNode) super.getChildAt(i);
+			result.addAll(child.getLeaves());
+		}
+		return result;
+	}
+
+	public FilterTreeNode getChildForObject(Object userObject) {
+		FilterTreeNode result = null;
+		for (int i=0; (i < super.getChildCount()) && (result == null); i++) {
+			FilterTreeNode child = (FilterTreeNode) super.getChildAt(i);
+			Object nodeObject = child.getUserObject();
+//			logger.info("nodeObject is a " + nodeObject.getClass() + " - " +
+//					"userObject is a " + userObject.getClass());
+			if (nodeObject.toString().equals(userObject.toString())) {
+				result = child;
+//				logger.info(nodeObject + " is equal to " + userObject);
+//			} else {
+//				logger.info(nodeObject + " is not equal to " + userObject);
+			}
+		}
+		return result;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeSelectionModel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeSelectionModel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeSelectionModel.java
new file mode 100644
index 0000000..a5adfe9
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeSelectionModel.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.ui.servicepanel.tree;
+
+import javax.swing.tree.DefaultTreeSelectionModel;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+
+public class FilterTreeSelectionModel extends DefaultTreeSelectionModel{
+	private static final long serialVersionUID = 3127644524735089630L;
+	
+	public FilterTreeSelectionModel(){
+		super();
+		setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+	}
+	
+	@Override
+	public void setSelectionPath(TreePath path) {
+		/*
+		 * Nothing happens here - only calls to mySetSelectionPath() will have
+		 * the effect of a node being selected.
+		 */
+	}
+	
+	public void mySetSelectionPath(TreePath path) {
+		super.setSelectionPath(path);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/MyFilter.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/MyFilter.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/MyFilter.java
new file mode 100644
index 0000000..8baa0eb
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/MyFilter.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.tree;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+
+public class MyFilter implements Filter {
+	private static final String HTML_MATCH_END = "</font><font color=\"black\">";
+	private static final String HTML_MATCH_START = "</font><font color=\"red\">";
+	private static final String HTML_POSTFIX = "</font></html>";
+	private static final String HTML_PREFIX = "<html><font color=\"black\">";
+
+	private String filterString;
+	private boolean superseded;
+	private String filterLowerCase;
+
+	public MyFilter(String filterString) {
+		this.filterString = filterString;
+		this.filterLowerCase = filterString.toLowerCase();
+		this.superseded = false;
+	}
+
+	private boolean basicFilter(DefaultMutableTreeNode node) {
+		if (filterString.isEmpty())
+			return true;
+		return node.getUserObject().toString().toLowerCase()
+				.contains(filterLowerCase);
+	}
+
+	@Override
+	public boolean pass(DefaultMutableTreeNode node) {
+		return basicFilter(node);
+	}
+
+	@Override
+	public String filterRepresentation(String original) {
+		StringBuilder sb = new StringBuilder(HTML_PREFIX);
+		int from = 0;
+		String originalLowerCase = original.toLowerCase();
+		int index = originalLowerCase.indexOf(filterLowerCase, from);
+		while (index > -1) {
+			sb.append(original.substring(from, index));
+			sb.append(HTML_MATCH_START);
+			sb.append(original.substring(index,
+					index + filterLowerCase.length()));
+			sb.append(HTML_MATCH_END);
+			from = index + filterLowerCase.length();
+			index = originalLowerCase.indexOf(filterLowerCase, from);
+		}
+		if (from < original.length())
+			sb.append(original.substring(from, original.length()));
+		return sb.append(HTML_POSTFIX).toString();
+	}
+
+	/**
+	 * @return the superseded
+	 */
+	@Override
+	public boolean isSuperseded() {
+		return superseded;
+	}
+
+	/**
+	 * @param superseded
+	 *            the superseded to set
+	 */
+	@Override
+	public void setSuperseded(boolean superseded) {
+		this.superseded = superseded;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/TreePanel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/TreePanel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/TreePanel.java
new file mode 100644
index 0000000..46eca53
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/TreePanel.java
@@ -0,0 +1,371 @@
+/*******************************************************************************
+ * 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.ui.servicepanel.tree;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.WEST;
+import static java.awt.Color.GRAY;
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.NONE;
+import static javax.swing.SwingUtilities.invokeLater;
+import static net.sf.taverna.t2.lang.ui.EdgeLineBorder.TOP;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.JTree;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.TreeExpansionEvent;
+import javax.swing.event.TreeExpansionListener;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreePath;
+
+import net.sf.taverna.t2.lang.ui.EdgeLineBorder;
+
+import org.apache.log4j.Logger;
+
+@SuppressWarnings("serial")
+public abstract class TreePanel extends JPanel {
+	private static int MAX_EXPANSION = 100;
+	private static final int SEARCH_WIDTH = 15;
+	private static Logger logger = Logger.getLogger(TreePanel.class);
+
+	protected Set<List<Object>> expandedPaths = new HashSet<>();
+	protected FilterTreeModel filterTreeModel;
+	protected JTextField searchField = new JTextField(SEARCH_WIDTH);
+	protected JTree tree = new JTree();
+	protected JScrollPane treeScrollPane;
+
+	private String availableObjectsString = "";
+	private String matchingObjectsString = "";
+	private String noMatchingObjectsString = "";
+
+	private TreeExpandCollapseListener treeExpandListener = new TreeExpandCollapseListener();
+	private Object filterLock = new Object();
+
+	public TreePanel(FilterTreeModel treeModel) {
+		filterTreeModel = treeModel;
+	}
+
+	public void expandTreePaths() throws InterruptedException,
+			InvocationTargetException {
+//		Filter appliedFilter = filterTreeModel.getCurrentFilter();
+//		if (appliedFilter == null) {
+			for (int i = 0; (i < tree.getRowCount()) && (i < MAX_EXPANSION); i++)
+				tree.expandRow(i);
+//		} else {
+//			boolean rowsFinished = false;
+//			for (int i = 0; (!appliedFilter.isSuperseded()) && (!rowsFinished)
+//					&& (i < MAX_EXPANSION); i++) {
+//				TreePath tp = tree.getPathForRow(i);
+//				if (tp == null) {
+//					rowsFinished = true;
+//				} else {
+//					if (!appliedFilter.pass((DefaultMutableTreeNode) tp
+//							.getLastPathComponent())) {
+//						tree.expandRow(i);
+//					}
+//				}
+//			}
+//		}
+	}
+
+	public void expandAll(FilterTreeNode node, boolean expand) {
+        @SuppressWarnings("unused")
+		FilterTreeNode root = (FilterTreeNode) tree.getModel().getRoot();
+
+        // Traverse tree from root
+        expandAll(new TreePath(node.getPath()), expand);
+    }
+
+    @SuppressWarnings("rawtypes")
+	private void expandAll(TreePath parent, boolean expand) {
+        // Traverse children
+        FilterTreeNode node = (FilterTreeNode) parent.getLastPathComponent();
+        if (node.getChildCount() >= 0)
+            for (Enumeration e=node.children(); e.hasMoreElements(); ) {
+                FilterTreeNode n = (FilterTreeNode) e.nextElement();
+                TreePath path = parent.pathByAddingChild(n);
+                expandAll(path, expand);
+            }
+
+        // Expansion or collapse must be done bottom-up
+        if (expand)
+            tree.expandPath(parent);
+        else
+            tree.collapsePath(parent);
+    }
+
+	protected void initialize() {
+		setLayout(new BorderLayout());
+		treeScrollPane = new JScrollPane(tree);
+		tree.setModel(filterTreeModel);
+		tree.addTreeExpansionListener(treeExpandListener);
+		tree.setCellRenderer(createCellRenderer());
+		tree.setSelectionModel(new FilterTreeSelectionModel());
+
+		JPanel topPanel = new JPanel();
+		topPanel.setBorder(new CompoundBorder(new EdgeLineBorder(TOP, GRAY), new EmptyBorder(10, 5, 0, 5)));
+		topPanel.setLayout(new GridBagLayout());
+		GridBagConstraints c = new GridBagConstraints();
+
+		JLabel filterLabel = new JLabel("Filter:  ");
+		c.fill = NONE;
+		c.gridx = 0;
+		c.gridy = 0;
+		c.weightx = 0.0;
+		c.anchor = GridBagConstraints.LINE_START;
+		topPanel.add(filterLabel, c);
+
+		c.fill = HORIZONTAL;
+		c.gridx = 1;
+		c.gridy = 0;
+		c.weightx = 1.0;
+		topPanel.add(searchField, c);
+
+
+		c.fill = NONE;
+		c.gridx = 2;
+		c.gridy = 0;
+		c.weightx = 0.0;
+		final JButton clearButton = new JButton("Clear");
+		clearButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				searchField.setText("");
+				invokeLater(new RunFilter());
+				clearButton.getParent().requestFocusInWindow();// so that the button does not stay focused after it is clicked on and did its action
+			}
+		});
+		topPanel.add(clearButton, c);
+
+		c.gridx = 3;
+		c.weightx = 0.2;
+		topPanel.add(new JPanel(), c);
+
+		JPanel topExtraPanel = new JPanel(new BorderLayout());
+
+		topExtraPanel.add(topPanel, NORTH);
+
+		Component extraComponent = createExtraComponent();
+		if (extraComponent != null) {
+			JPanel extraPanel  = new JPanel();
+			extraPanel.setLayout(new BorderLayout());
+			extraPanel.add(extraComponent, WEST);
+			topExtraPanel.add(extraPanel, CENTER);
+		}
+
+		add(topExtraPanel, NORTH);
+		add(treeScrollPane, CENTER);
+
+		searchField.addKeyListener(new SearchFieldKeyAdapter());
+	}
+
+	protected Component createExtraComponent() {
+		return null;
+	}
+
+	protected TreeCellRenderer createCellRenderer() {
+		return new FilterTreeCellRenderer();
+	}
+
+	public void runFilter() throws InterruptedException,
+			InvocationTargetException {
+		/*
+		 * Special lock object, don't do a synchronized model, as the lock on
+		 * JComponent might deadlock when painting the panel - see comments at
+		 * http://www.mygrid.org.uk/dev/issues/browse/T2-1438
+		 */
+		synchronized (filterLock) {
+			tree.removeTreeExpansionListener(treeExpandListener);
+			String text = searchField.getText();
+			FilterTreeNode root = (FilterTreeNode) tree.getModel().getRoot();
+			if (text.isEmpty()) {
+				setFilter(null);
+				root.setUserObject(getAvailableObjectsString());
+				filterTreeModel.nodeChanged(root);
+				for (List<Object> tp : expandedPaths) {
+	//				for (int i = 0; i < tp.length; i++)
+	//					logger.info("Trying to expand " + tp[i]);
+					tree.expandPath(filterTreeModel.getTreePathForObjectPath(tp));
+				}
+			} else {
+				setFilter(createFilter(text));
+				root.setUserObject(root.getChildCount() > 0 ? getMatchingObjectsString()
+						: getNoMatchingObjectsString());
+				filterTreeModel.nodeChanged(root);
+				expandTreePaths();
+			}
+			tree.addTreeExpansionListener(treeExpandListener);
+		}
+	}
+
+	/**
+	 * @return the availableObjectsString
+	 */
+	public String getAvailableObjectsString() {
+		return availableObjectsString;
+	}
+
+	/**
+	 * @param availableObjectsString the availableObjectsString to set
+	 */
+	public void setAvailableObjectsString(String availableObjectsString) {
+		this.availableObjectsString = availableObjectsString;
+	}
+
+	/**
+	 * @return the matchingObjectsString
+	 */
+	public String getMatchingObjectsString() {
+		return matchingObjectsString;
+	}
+
+	/**
+	 * @param matchingObjectsString the matchingObjectsString to set
+	 */
+	public void setMatchingObjectsString(String matchingObjectsString) {
+		this.matchingObjectsString = matchingObjectsString;
+	}
+
+	/**
+	 * @return the noMatchingObjectsString
+	 */
+	public String getNoMatchingObjectsString() {
+		return noMatchingObjectsString;
+	}
+
+	/**
+	 * @param noMatchingObjectsString the noMatchingObjectsString to set
+	 */
+	public void setNoMatchingObjectsString(String noMatchingObjectsString) {
+		this.noMatchingObjectsString = noMatchingObjectsString;
+	}
+
+	public Filter createFilter(String text) {
+		return new MyFilter(text);
+	}
+
+	public void setFilter(Filter filter) {
+		if (tree.getCellRenderer() instanceof FilterTreeCellRenderer)
+			((FilterTreeCellRenderer)tree.getCellRenderer()).setFilter(filter);
+		filterTreeModel.setFilter(filter);
+	}
+
+	protected class ExpandRowRunnable implements Runnable {
+		int rowNumber;
+
+		public ExpandRowRunnable(int rowNumber) {
+			this.rowNumber = rowNumber;
+		}
+
+		@Override
+		public void run() {
+			tree.expandRow(rowNumber);
+		}
+	}
+
+	protected class RunFilter implements Runnable {
+		@Override
+		public void run() {
+			Filter oldFilter = filterTreeModel.getCurrentFilter();
+			if (oldFilter != null)
+				oldFilter.setSuperseded(true);
+			try {
+				runFilter();
+			} catch (InterruptedException e) {
+				Thread.interrupted();
+			} catch (InvocationTargetException e) {
+				logger.error("", e);
+			}
+		}
+	}
+
+	protected class SearchFieldKeyAdapter extends KeyAdapter {
+		private final Runnable runFilterRunnable;
+		Timer timer = new Timer("Search field timer", true);
+
+		private SearchFieldKeyAdapter() {
+			this.runFilterRunnable = new RunFilter();
+		}
+
+		@Override
+		public void keyReleased(KeyEvent e) {
+			timer.cancel();
+			timer = new Timer();
+			timer.schedule(new TimerTask() {
+				@Override
+				public void run() {
+					invokeLater(runFilterRunnable);
+				}
+			}, 500);
+		}
+	}
+
+	private void noteExpansions() {
+		expandedPaths.clear();
+		TreePath rootPath = new TreePath(filterTreeModel.getRoot());
+		for (Enumeration<TreePath> e = tree.getExpandedDescendants(rootPath); e.hasMoreElements();) {
+			List<Object> userObjects = new ArrayList<>();
+			Object[] expandedPath = e.nextElement().getPath();
+			for (int i = 0; i < expandedPath.length; i++) {
+				FilterTreeNode node = (FilterTreeNode) expandedPath[i];
+//				logger.info("The object in the path is a " + expandedPath[i].getClass());
+				userObjects.add(node.getUserObject());
+//				logger.info("Added " + node.getUserObject() + " to path");
+			}
+			expandedPaths.add(userObjects);
+		}
+	}
+	
+	protected class TreeExpandCollapseListener implements TreeExpansionListener {
+		@Override
+		public void treeCollapsed(TreeExpansionEvent event) {
+			noteExpansions();
+		}
+
+		@Override
+		public void treeExpanded(TreeExpansionEvent event) {
+			noteExpansions();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI b/taverna-activity-palette-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
new file mode 100644
index 0000000..bb87331
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.ui.servicepanel.ServicePanelComponentFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context-osgi.xml b/taverna-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context-osgi.xml
new file mode 100644
index 0000000..2d96b28
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context-osgi.xml
@@ -0,0 +1,20 @@
+<?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="ServiceDescriptionConfigUIFactory" interface="uk.org.taverna.configuration.ConfigurationUIFactory" />
+
+	<service ref="ServicePanelComponentFactory" interface="net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI" />
+
+	<reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" />
+	<reference id="menuManager" interface="net.sf.taverna.t2.ui.menu.MenuManager" />
+	<reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" />
+	<reference id="serviceDescriptionRegistry" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry"/>
+	<reference id="serviceDescriptionsConfiguration" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration"/>
+	<reference id="serviceRegistry" interface="uk.org.taverna.commons.services.ServiceRegistry" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context.xml
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context.xml b/taverna-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context.xml
new file mode 100644
index 0000000..f0a11c1
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context.xml
@@ -0,0 +1,22 @@
+<?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="ServiceDescriptionConfigUIFactory"
+		class="net.sf.taverna.t2.workbench.ui.servicepanel.config.ServiceDescriptionConfigUIFactory">
+		<property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" />
+		<property name="serviceDescriptionsConfiguration" ref="serviceDescriptionsConfiguration" />
+	</bean>
+
+	<bean id="ServicePanelComponentFactory"
+		class="net.sf.taverna.t2.workbench.ui.servicepanel.ServicePanelComponentFactory">
+		<property name="editManager" ref="editManager" />
+		<property name="menuManager" ref="menuManager" />
+		<property name="selectionManager" ref="selectionManager" />
+		<property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" />
+		<property name="serviceRegistry" ref="serviceRegistry" />
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-tools/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-activity-tools/pom.xml b/taverna-activity-tools/pom.xml
new file mode 100644
index 0000000..53619a4
--- /dev/null
+++ b/taverna-activity-tools/pom.xml
@@ -0,0 +1,46 @@
+<?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>taverna-activity-tools</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Activity tools</name>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-menu-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+			<version>${taverna.language.version}</version>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-tools/src/main/java/net/sf/taverna/t2/workbench/activitytools/AbstractConfigureActivityMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-activity-tools/src/main/java/net/sf/taverna/t2/workbench/activitytools/AbstractConfigureActivityMenuAction.java b/taverna-activity-tools/src/main/java/net/sf/taverna/t2/workbench/activitytools/AbstractConfigureActivityMenuAction.java
new file mode 100644
index 0000000..4744774
--- /dev/null
+++ b/taverna-activity-tools/src/main/java/net/sf/taverna/t2/workbench/activitytools/AbstractConfigureActivityMenuAction.java
@@ -0,0 +1,64 @@
+package net.sf.taverna.t2.workbench.activitytools;
+
+import static javax.swing.Action.NAME;
+
+import java.awt.Frame;
+import java.net.URI;
+
+import javax.swing.Action;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.ui.Utils;
+
+public abstract class AbstractConfigureActivityMenuAction extends AbstractContextualMenuAction {
+	private static final URI configureSection = URI
+			.create("http://taverna.sf.net/2009/contextMenu/configure");
+
+	protected Scufl2Tools scufl2Tools = new Scufl2Tools();
+	protected final URI activityType;
+
+	public AbstractConfigureActivityMenuAction(URI activityType) {
+		super(configureSection, 50);
+		this.activityType = activityType;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled() && findActivity() != null;
+	}
+
+	protected Activity findActivity() {
+		if (getContextualSelection() == null)
+			return null;
+		Object selection = getContextualSelection().getSelection();
+		if (selection instanceof Activity) {
+			Activity activity = (Activity) selection;
+			if (activity.getType().equals(activityType))
+				return activity;
+		}
+		if (selection instanceof Processor) {
+			Processor processor = (Processor) selection;
+			Profile profile = processor.getParent().getParent().getMainProfile();
+			for (ProcessorBinding processorBinding : scufl2Tools.processorBindingsForProcessor(processor, profile))
+				if (processorBinding.getBoundActivity().getType().equals(activityType))
+					return processorBinding.getBoundActivity();
+		}
+		return null;
+	}
+
+	protected Frame getParentFrame() {
+		return Utils.getParentFrame(getContextualSelection()
+				.getRelativeToComponent());
+	}
+
+	protected void addMenuDots(Action configAction) {
+		String oldName = (String) configAction.getValue(NAME);
+		if (!oldName.endsWith(".."))
+			configAction.putValue(NAME, oldName + "...");
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-api/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-configuration-api/pom.xml b/taverna-configuration-api/pom.xml
new file mode 100644
index 0000000..83ca7eb
--- /dev/null
+++ b/taverna-configuration-api/pom.xml
@@ -0,0 +1,37 @@
+<?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>taverna-configuration-ui-api</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Configuration Management UI API</name>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${taverna.osgi.version}</version>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/colour/ColourManager.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/colour/ColourManager.java b/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/colour/ColourManager.java
new file mode 100644
index 0000000..4d5356f
--- /dev/null
+++ b/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/colour/ColourManager.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (C) 2011 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.configuration.colour;
+
+import java.awt.Color;
+
+import uk.org.taverna.configuration.Configurable;
+
+/**
+ * @author David Withers
+ */
+public interface ColourManager extends Configurable {
+	/**
+	 * Builds a Color that has been configured and associated with the given
+	 * String (usually an object type).
+	 * 
+	 * @return the associated Color, or if nothing is associated returns
+	 *         {@link Color#WHITE}.
+	 */
+	Color getPreferredColour(String itemKey);
+
+	void setPreferredColour(String itemKey, Color colour);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/mimetype/MimeTypeManager.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/mimetype/MimeTypeManager.java b/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/mimetype/MimeTypeManager.java
new file mode 100644
index 0000000..f0ae0d3
--- /dev/null
+++ b/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/mimetype/MimeTypeManager.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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.configuration.mimetype;
+
+import java.util.Map;
+
+import uk.org.taverna.configuration.Configurable;
+
+public interface MimeTypeManager extends Configurable {
+	@Override
+	String getCategory();
+
+	@Override
+	Map<String, String> getDefaultPropertyMap();
+
+	@Override
+	String getUUID();
+
+	@Override
+	String getDisplayName();
+
+	@Override
+	String getFilePrefix();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/WorkbenchConfiguration.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/WorkbenchConfiguration.java b/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/WorkbenchConfiguration.java
new file mode 100644
index 0000000..461ba5c
--- /dev/null
+++ b/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/WorkbenchConfiguration.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (C) 2011 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.configuration.workbench;
+
+import uk.org.taverna.configuration.Configurable;
+
+/**
+ * @author David Withers
+ */
+public interface WorkbenchConfiguration extends Configurable {
+	boolean getCaptureConsole();
+
+	void setCaptureConsole(boolean captureConsole);
+
+	boolean getWarnInternalErrors();
+
+	void setWarnInternalErrors(boolean warnInternalErrors);
+
+	int getMaxMenuItems();
+
+	void setMaxMenuItems(int maxMenuItems);
+
+	String getDotLocation();
+
+	void setDotLocation(String dotLocation);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/ui/T2ConfigurationFrame.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/ui/T2ConfigurationFrame.java b/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/ui/T2ConfigurationFrame.java
new file mode 100644
index 0000000..577484f
--- /dev/null
+++ b/taverna-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/ui/T2ConfigurationFrame.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (C) 2013 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.configuration.workbench.ui;
+
+/**
+ * @author David Withers
+ */
+public interface T2ConfigurationFrame {
+	void showFrame();
+
+	void showConfiguration(String name);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/pom.xml b/taverna-configuration-impl/pom.xml
new file mode 100644
index 0000000..56b57df
--- /dev/null
+++ b/taverna-configuration-impl/pom.xml
@@ -0,0 +1,76 @@
+<?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>taverna-configuration-ui-impl</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Configuration Management UI Impl</name>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-menu-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-helper-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-configuration-ui-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${taverna.osgi.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-app-configuration-api</artifactId>
+			<version>${taverna.osgi.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>${junit.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-configuration-impl</artifactId>
+			<version>${taverna.osgi.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-app-configuration-impl</artifactId>
+			<version>${taverna.osgi.version}</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationImpl.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationImpl.java b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationImpl.java
new file mode 100644
index 0000000..0e63a4a
--- /dev/null
+++ b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationImpl.java
@@ -0,0 +1,210 @@
+/*******************************************************************************
+ * 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.ui.impl.configuration;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.AbstractConfigurable;
+import uk.org.taverna.configuration.ConfigurationManager;
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+/**
+ * An implementation of Configurable for general Workbench configuration
+ * properties
+ * 
+ * @author Stuart Owen
+ * @author Stian Soiland-Reyes
+ */
+public class WorkbenchConfigurationImpl extends AbstractConfigurable implements
+		WorkbenchConfiguration {
+	private static Logger logger = Logger
+			.getLogger(WorkbenchConfiguration.class);
+	private static final int DEFAULT_MAX_MENU_ITEMS = 20;
+	public static final String TAVERNA_DOTLOCATION = "taverna.dotlocation";
+	public static final String MAX_MENU_ITEMS = "taverna.maxmenuitems";
+	public static final String WARN_INTERNAL_ERRORS = "taverna.warninternal";
+	public static final String CAPTURE_CONSOLE = "taverna.captureconsole";
+	private static final String BIN = "bin";
+	private static final String BUNDLE_CONTENTS = "Contents";
+	private static final String BUNDLE_MAC_OS = "MacOS";
+	private static final String DOT_EXE = "dot.exe";
+	private static final String DOT_FALLBACK = "dot";
+	public static String uuid = "c14856f0-5967-11dd-ae16-0800200c9a66";
+	private static final String MAC_OS_X = "Mac OS X";
+	private static final String WIN32I386 = "win32i386";
+	private static final String WINDOWS = "Windows";
+
+	private ApplicationConfiguration applicationConfiguration;
+
+	/**
+	 * Constructs a new <code>WorkbenchConfigurationImpl</code>.
+	 * 
+	 * @param configurationManager
+	 */
+	public WorkbenchConfigurationImpl(ConfigurationManager configurationManager) {
+		super(configurationManager);
+	}
+
+	Map<String, String> defaultWorkbenchProperties = null;
+	Map<String, String> workbenchProperties = new HashMap<String, String>();
+
+	@Override
+	public String getCategory() {
+		return "general";
+	}
+
+	@Override
+	public Map<String, String> getDefaultPropertyMap() {
+		if (defaultWorkbenchProperties == null) {
+			defaultWorkbenchProperties = new HashMap<>();
+			String dotLocation = System.getProperty(TAVERNA_DOTLOCATION) != null ? System
+					.getProperty(TAVERNA_DOTLOCATION) : getDefaultDotLocation();
+			if (dotLocation != null)
+				defaultWorkbenchProperties
+						.put(TAVERNA_DOTLOCATION, dotLocation);
+			defaultWorkbenchProperties.put(MAX_MENU_ITEMS,
+					Integer.toString(DEFAULT_MAX_MENU_ITEMS));
+			defaultWorkbenchProperties.put(WARN_INTERNAL_ERRORS,
+					Boolean.FALSE.toString());
+			defaultWorkbenchProperties.put(CAPTURE_CONSOLE,
+					Boolean.TRUE.toString());
+		}
+		return defaultWorkbenchProperties;
+	}
+
+	@Override
+	public String getDisplayName() {
+		return "Workbench";
+	}
+
+	@Override
+	public String getFilePrefix() {
+		return "Workbench";
+	}
+
+	@Override
+	public String getUUID() {
+		return uuid;
+	}
+
+	@Override
+	public boolean getWarnInternalErrors() {
+		String property = getProperty(WARN_INTERNAL_ERRORS);
+		return Boolean.parseBoolean(property);
+	}
+
+	@Override
+	public boolean getCaptureConsole() {
+		String property = getProperty(CAPTURE_CONSOLE);
+		return Boolean.parseBoolean(property);
+	}
+
+	@Override
+	public void setWarnInternalErrors(boolean warnInternalErrors) {
+		setProperty(WARN_INTERNAL_ERRORS, Boolean.toString(warnInternalErrors));
+	}
+
+	@Override
+	public void setCaptureConsole(boolean captureConsole) {
+		setProperty(CAPTURE_CONSOLE, Boolean.toString(captureConsole));
+	}
+
+	@Override
+	public void setMaxMenuItems(int maxMenuItems) {
+		if (maxMenuItems < 2)
+			throw new IllegalArgumentException(
+					"Maximum menu items must be at least 2");
+		setProperty(MAX_MENU_ITEMS, Integer.toString(maxMenuItems));
+	}
+
+	@Override
+	public int getMaxMenuItems() {
+		String property = getProperty(MAX_MENU_ITEMS);
+		try {
+			int maxMenuItems = Integer.parseInt(property);
+			if (maxMenuItems >= 2)
+				return maxMenuItems;
+			logger.warn(MAX_MENU_ITEMS + " can't be less than 2");
+		} catch (NumberFormatException ex) {
+			logger.warn("Invalid number for " + MAX_MENU_ITEMS + ": "
+					+ property);
+		}
+		// We'll return the default instead
+		return DEFAULT_MAX_MENU_ITEMS;
+	}
+
+	@Override
+	public String getDotLocation() {
+		return getProperty(TAVERNA_DOTLOCATION);
+	}
+
+	@Override
+	public void setDotLocation(String dotLocation) {
+		setProperty(TAVERNA_DOTLOCATION, dotLocation);
+	}
+
+	private String getDefaultDotLocation() {
+		if (applicationConfiguration == null)
+			return null;
+		File startupDir = applicationConfiguration.getStartupDir();
+		if (startupDir == null)
+			return DOT_FALLBACK;
+
+		String os = System.getProperty("os.name");
+		if (os.equals(MAC_OS_X))
+			if (startupDir.getParentFile() != null) {
+				File contentsDir = startupDir.getParentFile().getParentFile();
+				if (contentsDir != null
+						&& contentsDir.getName().equalsIgnoreCase(
+								BUNDLE_CONTENTS)) {
+					File dot = new File(new File(contentsDir, BUNDLE_MAC_OS),
+							DOT_FALLBACK);
+					if (dot.exists())
+						return dot.getAbsolutePath();
+				}
+			} else if (os.startsWith(WINDOWS)) {
+				File binWin386Dir = new File(new File(startupDir, BIN),
+						WIN32I386);
+				File dot = new File(binWin386Dir, DOT_EXE);
+				if (dot.exists())
+					return dot.getAbsolutePath();
+			}
+		return DOT_FALLBACK;
+	}
+
+	/**
+	 * Sets the applicationConfiguration.
+	 * 
+	 * @param applicationConfiguration
+	 *            the new value of applicationConfiguration
+	 */
+	public void setApplicationConfiguration(
+			ApplicationConfiguration applicationConfiguration) {
+		this.applicationConfiguration = applicationConfiguration;
+		defaultWorkbenchProperties = null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationPanel.java b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationPanel.java
new file mode 100644
index 0000000..ecddc35
--- /dev/null
+++ b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationPanel.java
@@ -0,0 +1,266 @@
+/*******************************************************************************
+ * 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.ui.impl.configuration;
+
+import static java.awt.GridBagConstraints.*;
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.workbench.helper.Helper.showHelp;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.openIcon;
+
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
+
+import org.apache.log4j.Logger;
+
+@SuppressWarnings("serial")
+public class WorkbenchConfigurationPanel extends JPanel {
+	private static final String RESTART_MSG = "For the new configuration to be fully applied, it is advised to restart Taverna.";
+	private static final String DOT_PATH_MSG = "Path to Graphviz executable <code>dot</code>:";
+	private static final String CONTEXT_MENU_SIZE_MSG = "Maximum number of services/ports in right-click menu:";
+	private static Logger logger = Logger
+			.getLogger(WorkbenchConfigurationUIFactory.class);
+
+	private JTextField dotLocation = new JTextField(25);
+	private JTextField menuItems = new JTextField(10);
+	private JCheckBox warnInternal = new JCheckBox("Warn on internal errors");
+	private JCheckBox captureConsole = new JCheckBox(
+			"Capture output on stdout/stderr to log file");
+
+	private final WorkbenchConfiguration workbenchConfiguration;
+
+	public WorkbenchConfigurationPanel(
+			WorkbenchConfiguration workbenchConfiguration) {
+		super();
+		this.workbenchConfiguration = workbenchConfiguration;
+		initComponents();
+	}
+
+	private static JLabel htmlLabel(String html) {
+		return new JLabel("<html><body>" + html + "</body></html>");
+	}
+
+	private void initComponents() {
+		this.setLayout(new GridBagLayout());
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		// Title describing what kind of settings we are configuring here
+		JTextArea descriptionText = new JTextArea(
+				"General Workbench configuration");
+		descriptionText.setLineWrap(true);
+		descriptionText.setWrapStyleWord(true);
+		descriptionText.setEditable(false);
+		descriptionText.setFocusable(false);
+		descriptionText.setBorder(new EmptyBorder(10, 10, 10, 10));
+		gbc.anchor = WEST;
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.gridwidth = 2;
+		gbc.weightx = 1.0;
+		gbc.weighty = 0.0;
+		gbc.fill = HORIZONTAL;
+		this.add(descriptionText, gbc);
+
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.gridwidth = 2;
+		gbc.weightx = 0.0;
+		gbc.weighty = 0.0;
+		gbc.insets = new Insets(10, 5, 0, 0);
+		gbc.fill = NONE;
+		this.add(htmlLabel(DOT_PATH_MSG), gbc);
+
+		dotLocation.setText(workbenchConfiguration.getDotLocation());
+		gbc.gridy++;
+		gbc.gridwidth = 1;
+		gbc.weightx = 1.0;
+		gbc.insets = new Insets(0, 0, 0, 0);
+		gbc.fill = HORIZONTAL;
+		this.add(dotLocation, gbc);
+
+		JButton browseButton = new JButton();
+		gbc.gridx = 1;
+		gbc.weightx = 0.0;
+		gbc.fill = NONE;
+		this.add(browseButton, gbc);
+		browseButton.setAction(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				System.setProperty("com.apple.macos.use-file-dialog-packages",
+						"false");
+				JFileChooser fileChooser = new JFileChooser();
+				fileChooser.putClientProperty(
+						"JFileChooser.appBundleIsTraversable", "always");
+				fileChooser.putClientProperty(
+						"JFileChooser.packageIsTraversable", "always");
+
+				fileChooser.setDialogTitle("Browse for dot");
+
+				fileChooser.resetChoosableFileFilters();
+				fileChooser.setAcceptAllFileFilterUsed(false);
+
+				fileChooser.setMultiSelectionEnabled(false);
+
+				int returnVal = fileChooser
+						.showOpenDialog(WorkbenchConfigurationPanel.this);
+				if (returnVal == APPROVE_OPTION)
+					dotLocation.setText(fileChooser.getSelectedFile()
+							.getAbsolutePath());
+			}
+		});
+		browseButton.setIcon(openIcon);
+
+		gbc.gridx = 0;
+		gbc.gridy++;
+		gbc.gridwidth = 2;
+		gbc.weightx = 0.0;
+		gbc.weighty = 0.0;
+		gbc.insets = new Insets(10, 5, 0, 0);
+		gbc.fill = HORIZONTAL;
+		this.add(htmlLabel(CONTEXT_MENU_SIZE_MSG), gbc);
+
+		menuItems.setText(Integer.toString(workbenchConfiguration
+				.getMaxMenuItems()));
+		gbc.gridy++;
+		gbc.weightx = 1.0;
+		gbc.gridwidth = 1;
+		gbc.insets = new Insets(0, 0, 0, 0);
+		gbc.fill = HORIZONTAL;
+		this.add(menuItems, gbc);
+
+		gbc.gridx = 0;
+		gbc.gridy++;
+		gbc.gridwidth = 2;
+		gbc.weightx = 1.0;
+		gbc.fill = HORIZONTAL;
+		gbc.insets = new Insets(10, 0, 0, 0);
+		warnInternal
+				.setSelected(workbenchConfiguration.getWarnInternalErrors());
+		this.add(warnInternal, gbc);
+
+		gbc.gridy++;
+		gbc.insets = new Insets(0, 0, 10, 0);
+		captureConsole.setSelected(workbenchConfiguration.getCaptureConsole());
+		this.add(captureConsole, gbc);
+
+		// Add the buttons panel
+		gbc.gridx = 0;
+		gbc.gridy++;
+		gbc.gridwidth = 3;
+		gbc.weightx = 1.0;
+		gbc.weighty = 1.0;
+		gbc.fill = BOTH;
+		gbc.anchor = SOUTH;
+		this.add(getButtonsPanel(), gbc);
+	}
+
+	private Component getButtonsPanel() {
+		final JPanel panel = new JPanel();
+		panel.setLayout(new FlowLayout(FlowLayout.CENTER));
+
+		/**
+		 * The helpButton shows help about the current component
+		 */
+		JButton helpButton = new JButton(new AbstractAction("Help") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				showHelp(panel);
+			}
+		});
+		panel.add(helpButton);
+
+		/**
+		 * The resetButton changes the property values shown to those
+		 * corresponding to the configuration currently applied.
+		 */
+		JButton resetButton = new JButton(new AbstractAction("Reset") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				resetFields();
+			}
+		});
+		panel.add(resetButton);
+
+		JButton applyButton = new JButton(new AbstractAction("Apply") {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				String menus = menuItems.getText();
+				try {
+					workbenchConfiguration.setMaxMenuItems(Integer
+							.valueOf(menus));
+				} catch (IllegalArgumentException e) {
+					String message = "Invalid menu items number " + menus
+							+ ":\n" + e.getLocalizedMessage();
+					showMessageDialog(panel, message, "Invalid menu items",
+							WARNING_MESSAGE);
+					return;
+				}
+
+				workbenchConfiguration.setCaptureConsole(captureConsole
+						.isSelected());
+				workbenchConfiguration.setWarnInternalErrors(warnInternal
+						.isSelected());
+				workbenchConfiguration.setDotLocation(dotLocation.getText());
+				try {
+					showMessageDialog(panel, RESTART_MSG, "Restart adviced",
+							INFORMATION_MESSAGE);
+				} catch (Exception e) {
+					logger.error("Error storing updated configuration", e);
+				}
+			}
+		});
+		panel.add(applyButton);
+		return panel;
+	}
+
+	/**
+	 * Resets the shown field values to those currently set (last saved) in the
+	 * configuration.
+	 * 
+	 * @param configurable
+	 */
+	private void resetFields() {
+		menuItems.setText(Integer.toString(workbenchConfiguration
+				.getMaxMenuItems()));
+		dotLocation.setText(workbenchConfiguration.getDotLocation());
+		warnInternal
+				.setSelected(workbenchConfiguration.getWarnInternalErrors());
+		captureConsole.setSelected(workbenchConfiguration.getCaptureConsole());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationUIFactory.java
----------------------------------------------------------------------
diff --git a/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationUIFactory.java b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationUIFactory.java
new file mode 100644
index 0000000..263233f
--- /dev/null
+++ b/taverna-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationUIFactory.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * 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.ui.impl.configuration;
+
+import javax.swing.JPanel;
+
+import uk.org.taverna.configuration.Configurable;
+import uk.org.taverna.configuration.ConfigurationUIFactory;
+
+import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
+
+public class WorkbenchConfigurationUIFactory implements ConfigurationUIFactory {
+	private WorkbenchConfiguration workbenchConfiguration;
+
+	@Override
+	public boolean canHandle(String uuid) {
+		return uuid.equals(workbenchConfiguration.getUUID());
+	}
+
+	@Override
+	public JPanel getConfigurationPanel() {
+		return new WorkbenchConfigurationPanel(workbenchConfiguration);
+	}
+
+	@Override
+	public Configurable getConfigurable() {
+		return workbenchConfiguration;
+	}
+
+	public void setWorkbenchConfiguration(
+			WorkbenchConfiguration workbenchConfiguration) {
+		this.workbenchConfiguration = workbenchConfiguration;
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/ServiceListCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/ServiceListCellRenderer.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/ServiceListCellRenderer.java
new file mode 100644
index 0000000..eafe095
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/search_results/ServiceListCellRenderer.java
@@ -0,0 +1,291 @@
+package net.sf.taverna.biocatalogue.ui.search_results;
+
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.biocatalogue.x2009.xml.rest.Location;
+import org.biocatalogue.x2009.xml.rest.ResourceLinkWithString;
+import org.biocatalogue.x2009.xml.rest.Service;
+import org.biocatalogue.x2009.xml.rest.ServiceSummary.Provider;
+
+import net.sf.taverna.biocatalogue.model.LoadingExpandedResource;
+import net.sf.taverna.biocatalogue.model.LoadingResource;
+import net.sf.taverna.biocatalogue.model.Resource;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check.ServiceMonitoringStatusInterpreter;
+
+
+/**
+ * This list cell renderer is will display (web) Service items in the search results
+ * list box.
+ * 
+ * In the collapsed state, four data items will be shown per service: status (as per monitoring data),
+ * service type (SOAP / REST), the name of the service and its description.
+ * 
+ * In the expanded state, other details are added to the ones above: service categories, locations,
+ * endpoints and providers.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class ServiceListCellRenderer extends ExpandableOnDemandLoadedListCellRenderer
+{
+  private JLabel jlTypeIcon;
+  private JLabel jlItemStatus;
+  private JLabel jlItemTitle;
+  private JLabel jlDescription;
+  
+  private GridBagConstraints c;
+  
+  
+  public ServiceListCellRenderer() {
+    /* do nothing */
+  }
+  
+  
+  /**
+   * This entry can be in one of two states:
+   * -- containing only the name of the resource and NOT loading further details;
+   * -- containing only the name of the resource and LOADING further details.
+   * 
+   * @param itemToRender
+   * @return
+   */
+  protected GridBagConstraints prepareInitiallyLoadingEntry(Object itemToRender)
+  {
+    TYPE resourceType = determineResourceType(itemToRender);
+    LoadingResource resource = (LoadingResource)itemToRender;
+    
+    jlTypeIcon = new JLabel(resourceType.getIcon());
+    jlItemStatus = new JLabel(ResourceManager.getImageIcon(ResourceManager.SERVICE_STATUS_UNKNOWN_ICON));
+    
+    jlItemTitle = new JLabel(Resource.getDisplayNameForResource(resource), JLabel.LEFT);
+    jlItemTitle.setForeground(Color.decode("#AD0000"));  // very dark red
+    jlItemTitle.setFont(jlItemTitle.getFont().deriveFont(Font.PLAIN, jlItemTitle.getFont().getSize() + 2));
+    
+    jlDescription = resource.isLoading() ? loaderBarAnimationGrey : loaderBarAnimationGreyStill;
+    
+    return (arrangeLayout(false));
+  }
+  
+  
+  /**
+   * 
+   * @param itemToRender
+   * @param expandedView <code>true</code> to indicate that this method generates the top
+   *                     fragment of the expanded list entry for this SOAP operation / REST method.
+   * @return
+   */
+  protected GridBagConstraints prepareLoadedEntry(Object itemToRender, boolean selected)
+  {
+    TYPE resourceType = determineResourceType(itemToRender);
+    Service service = (Service)itemToRender;;
+    
+    // service type
+    if (service.getServiceTechnologyTypes() != null && service.getServiceTechnologyTypes().getTypeList().size() > 0)
+    {
+      if (service.getServiceTechnologyTypes().getTypeList().size() > 1 &&
+           !(service.getServiceTechnologyTypes().getTypeList().get(0).toString().equalsIgnoreCase("SOAP")) && 
+             service.getServiceTechnologyTypes().getTypeList().get(1).toString().equalsIgnoreCase("SOAPLAB")
+         )
+      {
+        jlTypeIcon = new JLabel(ResourceManager.getImageIcon(ResourceManager.SERVICE_TYPE_MULTITYPE_ICON));
+      }
+      else if (service.getServiceTechnologyTypes().getTypeArray(0).toString().equalsIgnoreCase("SOAP")) {
+        jlTypeIcon = new JLabel(ResourceManager.getImageIcon(ResourceManager.SERVICE_TYPE_SOAP_ICON));
+      }
+      else if (service.getServiceTechnologyTypes().getTypeArray(0).toString().equalsIgnoreCase("REST")) {
+        jlTypeIcon = new JLabel(ResourceManager.getImageIcon(ResourceManager.SERVICE_TYPE_REST_ICON));
+      }
+    }
+    else {
+      // can't tell the type - just show as a service of no particular type
+      jlTypeIcon = new JLabel(resourceType.getIcon());
+    }
+    
+    
+    // service status
+    jlItemStatus = new JLabel(ServiceMonitoringStatusInterpreter.getStatusIcon(service, true));
+    
+    jlItemTitle = new JLabel(Resource.getDisplayNameForResource(service), JLabel.LEFT);
+    jlItemTitle.setForeground(Color.decode("#AD0000"));  // very dark red
+    jlItemTitle.setFont(jlItemTitle.getFont().deriveFont(Font.PLAIN, jlItemTitle.getFont().getSize() + 2));
+    
+    int descriptionMaxLength = DESCRIPTION_MAX_LENGTH_EXPANDED;
+    String strDescription = Util.stripAllHTML(service.getDescription());
+    strDescription = (strDescription == null || strDescription.length() == 0 ?
+                             "<font color=\"gray\">no description</font>" :
+                            	 StringEscapeUtils.escapeHtml(Util.ensureLineLengthWithinString(strDescription, LINE_LENGTH, false)));
+    
+    if (strDescription.length() > descriptionMaxLength) {
+      strDescription = strDescription.substring(0, descriptionMaxLength) + "<font color=\"gray\">(...)</font>";
+    }
+    strDescription = "<html><b>Description: </b>" + strDescription + "</html>";
+    jlDescription = new JLabel(strDescription);
+    
+    return (arrangeLayout(true));
+  }
+  
+  
+  /**
+   * @return Final state of the {@link GridBagConstraints} instance
+   *         that was used to lay out components in the panel.
+   */
+  private GridBagConstraints arrangeLayout(boolean showActionButtons)
+  {
+    // POPULATE PANEL WITH PREPARED COMPONENTS
+    this.setLayout(new GridBagLayout());
+    c = new GridBagConstraints();
+    c.anchor = GridBagConstraints.NORTHWEST;
+    c.fill = GridBagConstraints.HORIZONTAL;
+    
+    c.gridx = 0;
+    c.gridy = 0;
+    c.weightx = 0;
+    c.insets = new Insets(10, 6, 6, 3);
+    this.add(jlTypeIcon, c);
+    
+    c.gridx++;
+    c.insets = new Insets(10, 3, 6, 3);
+    this.add(jlItemStatus, c);
+    
+    c.gridx++;
+    c.weightx = 1.0;
+    c.insets = new Insets(10, 3, 6, 3);
+    this.add(jlItemTitle, c);
+    
+    if (showActionButtons) {
+      c.gridx++;
+      c.gridheight = 3;
+      c.weightx = 0;
+      c.weighty = 1.0;
+      jlExpand = new JLabel(ResourceManager.getImageIcon(ResourceManager.FOLD_ICON));
+      this.add(jlExpand, c);
+    }
+    
+    c.gridx = 2;
+    c.gridy++;
+    c.gridheight = 1;
+    c.weightx = 1.0;
+    c.weighty = 0;
+    c.insets = new Insets(3, 3, 3, 3);
+    this.add(jlDescription, c);
+    
+    return (c);
+  }
+  
+  
+  
+  protected void prepareLoadingExpandedEntry(Object itemToRender)
+  {
+    LoadingExpandedResource expandedResource = (LoadingExpandedResource) itemToRender;
+    GridBagConstraints c = prepareLoadedEntry(expandedResource.getAssociatedObj(), false);
+    
+    if (expandedResource.isLoading())
+    {
+      c.gridx = 0;
+      c.gridy++;
+      c.gridwidth = 3;
+      c.anchor = GridBagConstraints.CENTER;
+      c.fill = GridBagConstraints.HORIZONTAL;
+      c.weightx = 1.0;
+      this.add(loaderBarAnimationOrange, c);
+    }
+    else
+    {
+      // *** additional data for this Web Service operations ***
+      Service service = (Service) expandedResource.getAssociatedObj();
+      
+      
+      // -- categories --
+      int categoryCount = service.getSummary().getCategoryList().size();
+      String categoryString = "";
+      if (categoryCount > 0) {
+        List<String> categoryNames = new ArrayList<String>();
+        for (ResourceLinkWithString category : service.getSummary().getCategoryList()) {
+          categoryNames.add(category.getStringValue());
+        }
+        categoryString = "<html><b>" + Util.pluraliseNoun("Category", categoryCount) + ": </b>" + StringEscapeUtils.escapeHtml(Util.join(categoryNames, ", ")) + "</html>";
+      }
+      else {
+        categoryString = "<html><b>Category: </b><font color=\"gray\">unknown</font></html>";
+      }
+      
+      c.gridy++;
+      this.add(new JLabel(categoryString),c);
+      
+      
+      // -- endpoints --
+      int endpointCount = service.getSummary().getEndpointList().size();
+      String endpointString = "";
+      if (endpointCount > 0) {
+        endpointString = "<html><b>" + Util.pluraliseNoun("Endpoint", endpointCount) + ": </b>" +
+        StringEscapeUtils.escapeHtml(Util.join(service.getSummary().getEndpointList(), ", ")) + "</html>";
+      }
+      else {
+        endpointString = "<html><b>Endpoint: </b><font color=\"gray\">unknown</font></html>";
+      }
+      
+      c.gridy++;
+      this.add(new JLabel(endpointString), c);
+      
+      
+      // -- providers --
+      int providerCount = service.getSummary().getProviderList().size();
+      String providerString = "";
+      if (providerCount > 0) {
+        List<String> providerNames = new ArrayList<String>();
+        for (Provider serviceProvider : service.getSummary().getProviderList()) {
+          providerNames.add(serviceProvider.getName());
+        }
+        providerString = "<html><b>" + Util.pluraliseNoun("Provider", providerCount) + ": </b>" + StringEscapeUtils.escapeHtml(Util.join(providerNames, ", ")) + "</html>";
+      }
+      else {
+        providerString = "<html><b>Provider: </b><font color=\"gray\">unknown</font></html>";
+      }
+      
+      c.gridy++;
+      this.add(new JLabel(providerString),c);
+      
+      
+      // -- locations --
+      int locationCount = service.getSummary().getLocationList().size();
+      String locationString = "";
+      List<String> locations = new ArrayList<String>();
+      if (locationCount > 0) {
+        for (Location location : service.getSummary().getLocationList()) {
+          List<String> locationNameFragments = new ArrayList<String>();
+          locationNameFragments.add(location.getCity());
+          locationNameFragments.add(location.getCountry());
+          locations.add(Util.join(locationNameFragments, ", "));
+        }
+      }
+      locationString = "<html><b>" + Util.pluraliseNoun("Location", locations.size()) + ": </b>" +
+      (locations.size() > 0 ? StringEscapeUtils.escapeHtml(Util.join(locations, "; ")) : "<font color=\"gray\">unknown</font>") +
+      "</html>";
+      
+      c.gridy++;
+      c.insets = new Insets(3, 3, 12, 3);
+      this.add(new JLabel(locationString),c);
+    }
+  }
+
+
+@Override
+boolean shouldBeHidden(Object itemToRender) {
+	return false;
+}
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/JTriStateTree.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/JTriStateTree.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/JTriStateTree.java
new file mode 100644
index 0000000..304e50a
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/JTriStateTree.java
@@ -0,0 +1,631 @@
+package net.sf.taverna.biocatalogue.ui.tristatetree;
+
+import java.awt.Component;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JPopupMenu;
+import javax.swing.JTree;
+import javax.swing.SwingUtilities;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.MutableTreeNode;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class JTriStateTree extends JTree
+{
+  // This is used to manage location and padding of tooltips on long items
+  // that don't fit into the visible part of this tree.
+  private static final int JCHECKBOX_WIDTH = 16;
+  
+  private JTriStateTree instanceOfSelf;
+  private JPopupMenu contextualMenu;
+  
+  private TriStateTreeNode root;
+  
+  // will enable/disable checkboxes - when disabled the selection
+  // will remain, but will appear as read-only
+  private boolean bCheckingEnabled;
+  
+  private List<Action> contextualMenuActions; 
+  private Action expandAllAction;
+  private Action collapseAllAction;
+  private Action selectAllAction;
+  private Action deselectAllAction;
+  
+  private Set<TriStateTreeCheckingListener> checkingListeners;
+  
+  
+  @SuppressWarnings("serial")
+public JTriStateTree(TriStateTreeNode root)
+  {
+    super(root);
+    this.root = root;
+    this.instanceOfSelf = this;
+    
+    // by default checking is allowed
+    this.bCheckingEnabled = true;
+    
+    // initially, no checking listeners
+    checkingListeners = new HashSet<TriStateTreeCheckingListener>();
+    
+    // initially set to show the [+]/[-] icons for expanding collapsing top-level nodes
+    this.setShowsRootHandles(true);
+    
+    // use the cell rendered which understands the three states of the
+    // nodes of this tree
+    this.setCellRenderer(new TriStateCheckBoxTreeCellRenderer());
+    
+    
+    // create all necessary actions for the popup menu: selecting/deselecting and expanding/collapsing all nodes
+    this.selectAllAction = new AbstractAction("Select all", ResourceManager.getImageIcon(ResourceManager.SELECT_ALL_ICON))
+    {
+      // Tooltip
+      { this.putValue(SHORT_DESCRIPTION, "Select all nodes in the tree"); }
+      
+      public void actionPerformed(ActionEvent e) {
+        selectAllNodes(true);
+      }
+    };
+    
+    // Use the Taverna untick icon
+    this.deselectAllAction = new AbstractAction("Deselect all", ResourceManager.getImageIcon(ResourceManager.UNCHECKED_ICON))
+    {
+      // Tooltip
+      { this.putValue(SHORT_DESCRIPTION, "Deselect all nodes in the tree"); }
+      
+      public void actionPerformed(ActionEvent e) {
+        selectAllNodes(false);
+      }
+    };
+    
+    
+//    this.expandAllAction = new AbstractAction("Expand all", ResourceManager.getImageIcon(ResourceManager.EXPAND_ALL_ICON))
+    // Use the standard Taverna plus icon
+    this.expandAllAction = new AbstractAction("Expand all", WorkbenchIcons.plusIcon)
+    {
+      // Tooltip
+      { this.putValue(SHORT_DESCRIPTION, "Expand all nodes in the tree"); }
+      
+      public void actionPerformed(ActionEvent e) {
+        expandAll();
+      }
+    };
+    
+//    this.collapseAllAction = new AbstractAction("Collapse all", ResourceManager.getImageIcon(ResourceManager.COLLAPSE_ALL_ICON))
+    // Use the standard Taverna minus icon
+    this.collapseAllAction = new AbstractAction("Collapse all", WorkbenchIcons.minusIcon)
+    {
+      // Tooltip
+      { this.putValue(SHORT_DESCRIPTION, "Collapse all expanded nodes in the tree"); }
+      
+      public void actionPerformed(ActionEvent e) {
+        collapseAll();
+      }
+    };
+    
+    
+    // populate the popup menu with created menu items
+    contextualMenuActions = Arrays.asList(new Action[] {expandAllAction, collapseAllAction, deselectAllAction});
+    
+    contextualMenu = new JPopupMenu();
+    contextualMenu.add(expandAllAction);
+    contextualMenu.add(collapseAllAction);
+    contextualMenu.add(new JPopupMenu.Separator());
+    //contextualMenu.add(selectAllAction);
+    contextualMenu.add(deselectAllAction);
+    
+    
+    this.addMouseListener(new MouseAdapter() {
+      // use mousePressed, not mouseClicked to make sure that
+      // quick successive clicks get processed correctly, otherwise
+      // some clicks are disregarded
+      public void mousePressed(MouseEvent e)
+      {
+        // only listen to checkbox checking requests if this is
+        // a correct type of mouse event for this
+        if (!e.isPopupTrigger() && e.getButton() == MouseEvent.BUTTON1)
+        {
+          int clickedRow = instanceOfSelf.getRowForLocation(e.getX(), e.getY());
+          
+          // only make changes to node selections if checking is enabled in the tree and
+          // it was a node which was clicked, not [+]/[-] or blank space
+          if (bCheckingEnabled && clickedRow != -1)
+          {
+            Object clickedObject = instanceOfSelf.getPathForRow(clickedRow).getLastPathComponent();
+            if (clickedObject instanceof TriStateTreeNode) {
+              TriStateTreeNode node = ((TriStateTreeNode)clickedObject);
+              
+              // toggle state of the clicked node + propagate the changes to
+              // the checking state of all nodes
+              node.toggleState(true);
+              
+              // repaint the whole tree
+              SwingUtilities.invokeLater(new Runnable() {
+                public void run() {
+                  instanceOfSelf.repaint();
+                }
+              });
+              
+              // notify all listeners
+              notifyCheckingListeners();
+            }
+          }
+        }
+        else {
+          // not a checking action - instead, bring up a popup menu
+          contextualMenu.show(instanceOfSelf, e.getX(), e.getY());
+        }
+      }
+      
+      public void mouseReleased(MouseEvent e)
+      {
+        if (e.isPopupTrigger()) {
+          // another way a popup menu may be called on different systems
+          contextualMenu.show(instanceOfSelf, e.getX(), e.getY());
+        }
+      }
+      
+      
+      /**
+       * This method enables tooltips on this instance of JTriStateTree
+       * when mouse enters its bounds. Custom tooltips will be used, but
+       * this notifies ToolTipManager that tooltips must be shown on this
+       * tree. 
+       */
+      public void mouseEntered(MouseEvent e) {
+        instanceOfSelf.setToolTipText("Filter tree");
+      }
+      
+      /**
+       * Removes tooltips from this JTriStateTree when mouse leaves its bounds.
+       */
+      public void mouseExited(MouseEvent e) {
+        instanceOfSelf.setToolTipText(null);
+      }
+      
+    });
+    
+  }
+  
+  
+  /**
+   * This method is used to determine tooltip location.
+   * 
+   * Helps to ensure that the tooltip appears directly over the
+   * text in the row over which the mouse currently hovers.
+   */
+  public Point getToolTipLocation(MouseEvent e)
+  {
+    int iRowIndex = this.getRowForLocation(e.getX(), e.getY());
+    if (iRowIndex != -1) {
+      // mouse hovers over one of the rows - make top-left corner of
+      // the tooltip to appear over the top-left corner of that row
+      Rectangle bounds = this.getRowBounds(iRowIndex);
+      return (new Point(bounds.x + JCHECKBOX_WIDTH, bounds.y));
+    }
+    else {
+      // let ToolTipManager determine where to show the tooltip (if it will be shown)
+      return null;
+    }
+  }
+  
+  
+  /**
+   * Supports dynamic tooltips for the contents of this JTriStateTree -
+   * the tooltips will only be shown for those tree nodes that don't
+   * fully fit within the visible bounds of the tree.
+   * 
+   * For other nodes no tooltip will be shown.
+   */
+  public String getToolTipText(MouseEvent e)
+  {
+    String strTooltip = null;
+    
+    Object correspondingObject = getTreeNodeObject(e);
+    if (correspondingObject != null) {
+      // mouse is hovering over some row in the tree, not a blank space --
+      // obtain a component that is identical to the one which is currently displayed at the identified row in the tree
+      Component rendering = this.getCellRenderer().getTreeCellRendererComponent(this, correspondingObject, false, false,
+                                                                true, this.getRowForLocation(e.getX(), e.getY()), false);
+      
+      if (rendering.getPreferredSize().width + getToolTipLocation(e).x - JCHECKBOX_WIDTH > this.getVisibleRect().width) {
+        // if the component is not fully visible, the tooltip will be displayed -
+        // tooltip text matches the one for this row in the tree, will just be shown in full
+        strTooltip = correspondingObject.toString();
+      }
+    }
+    
+    // return either tooltip text or 'null' if no tooltip is currently required
+    return (strTooltip);
+  }
+  
+  
+  /**
+   * Check whether a {@link MouseEvent} happened in such a location
+   * in the {@link JTriStateTree} that corresponds to some node or a
+   * blank space.
+   * 
+   * @param e
+   * @return Object contained in the tree node that corresponds to the
+   *         location of specified {@link MouseEvent} <code>e</code>;
+   *         or <code>null</code> if the event happened over a blank space.
+   */
+  public Object getTreeNodeObject(MouseEvent e)
+  {
+    int iRowIndex = this.getRowForLocation(e.getX(), e.getY());
+    if (iRowIndex != -1) {
+      // mouse is hovering over some row in the tree, not a blank space
+      return (this.getPathForRow(iRowIndex).getLastPathComponent());
+    }
+    
+    return (null);
+  }
+  
+  
+  /**
+   * @return List of the highest-level nodes of the tree that have full (not partial) selection and,
+   *         therefore, act as roots of checked paths.
+   */
+  public List<TreePath> getRootsOfCheckedPaths()
+  {
+    return getRootsOfCheckedPaths(this.root);
+  }
+  
+  /**
+   * A recursive version of the getCheckedRootsOfCheckedPaths().
+   * Performs all the work for a given node and returns result to
+   * the caller.
+   * 
+   * @param startNode Node to start with.
+   * @return
+   */
+  private List<TreePath> getRootsOfCheckedPaths(TriStateTreeNode startNode)
+  {
+    ArrayList<TreePath> pathsToRootsOfCheckings = new ArrayList<TreePath>();
+    
+    Object currentNode = null;
+    for (Enumeration e = startNode.children(); e.hasMoreElements(); )
+    {
+      currentNode = e.nextElement();
+      if (currentNode instanceof TriStateTreeNode) {
+        TriStateTreeNode curTriStateNode = (TriStateTreeNode)currentNode;
+        
+        if (curTriStateNode.getState().equals(TriStateCheckBox.State.CHECKED)) {
+          pathsToRootsOfCheckings.add(new TreePath(curTriStateNode.getPath()));
+        }
+        else if (curTriStateNode.getState().equals(TriStateCheckBox.State.PARTIAL)) {
+          pathsToRootsOfCheckings.addAll(getRootsOfCheckedPaths(curTriStateNode));
+        }
+      }
+    }
+    
+    return (pathsToRootsOfCheckings);
+  }
+  
+  
+  /**
+   * @return List of TreePath objects, where the last component in each
+   *         path is the root of an unchecked path in this tree. In other
+   *         words each of those last components is either an unchecked
+   *         leaf node or a node, none of whose children (and itself as
+   *         well) are checked.
+   */
+  public List<TreePath> getRootsOfUncheckedPaths()
+  {
+    return (getRootsOfUncheckedPaths(this.root));
+  }
+  
+  
+  /**
+   * Recursive worker method for <code>getRootsOfUncheckedPaths()</code>.
+   */
+  private List<TreePath> getRootsOfUncheckedPaths(TreeNode startNode)
+  {
+    List<TreePath> rootNodesOfUncheckedPaths = new ArrayList<TreePath>();
+    
+    Object currentNode = null;
+    for (Enumeration e = startNode.children(); e.hasMoreElements(); )
+    {
+      currentNode = e.nextElement();
+      if (!(currentNode instanceof TriStateTreeNode) ||
+          ((TriStateTreeNode)currentNode).getState().equals(TriStateCheckBox.State.UNCHECKED))
+      {
+        rootNodesOfUncheckedPaths.add(new TreePath(((DefaultMutableTreeNode)currentNode).getPath()));
+      }
+      else {
+        rootNodesOfUncheckedPaths.addAll(getRootsOfUncheckedPaths((TreeNode)currentNode));
+      }
+    }
+    
+    return (rootNodesOfUncheckedPaths);
+  }
+  
+  
+  /**
+   * @return List of TreePath objects, that point to all "leaf"
+   *         nodes in the tree that are checked - in other words
+   *         this method returns a collection of paths to all "deepest"
+   *         nodes in this tree that are checked and do not have any
+   *         (checked) children.
+   */
+  public List<TreePath> getLeavesOfCheckedPaths() {
+    return (getLeavesOfCheckedPaths(this.root));
+  }
+  
+  
+  /**
+   * Recursive worker method for {@link JTriStateTree#getLeavesOfCheckedPaths()}
+   */
+  private List<TreePath> getLeavesOfCheckedPaths(TriStateTreeNode startNode)
+  {
+    List<TreePath> leavesOfCheckedPaths = new ArrayList<TreePath>();
+    
+    // this node is only relevant if it is checked itself - if not,
+    // it must be the first-level child of another node that is checked
+    // and is only considered here on the recursive pass (but will be discarded)
+    if (startNode.getState().equals(TriStateCheckBox.State.CHECKED) ||
+        startNode.getState().equals(TriStateCheckBox.State.PARTIAL))
+    {
+      // "ask" all children to do the same...
+      Object currentNode = null;
+      for (Enumeration e = startNode.children(); e.hasMoreElements(); ) {
+        currentNode = e.nextElement();
+        if (currentNode instanceof TriStateTreeNode) {
+          leavesOfCheckedPaths.addAll(getLeavesOfCheckedPaths((TriStateTreeNode)currentNode));
+        }
+      }
+      
+      // ...if we have a list of leaf nodes, then this node can't be a leaf;
+      // -> but alternatively, if the list is empty, it means that this node is
+      //    itself a leaf node and must be added to the result
+      if (leavesOfCheckedPaths.isEmpty()) {
+        leavesOfCheckedPaths.add(new TreePath(startNode.getPath()));
+      }
+    }
+    
+    return (leavesOfCheckedPaths);
+  }
+  
+  
+  /**
+   * @return List of all contextual menu actions that are available for this tree.
+   */
+  public List<Action> getContextualMenuActions() {
+    return this.contextualMenuActions;
+  }
+  
+  
+  /**
+   * Enables or disables all actions in the contextual menu
+   * @param actionsAreEnabled
+   */
+  public void enableAllContextualMenuAction(boolean actionsAreEnabled) {
+    for (Action a : getContextualMenuActions()) {
+      a.setEnabled(actionsAreEnabled);
+    }
+  }
+  
+  
+  /**
+   * Selects or deselects all nodes.
+   * @param selectAll True - to select all; false - to reset all selections.
+   */
+  public void selectAllNodes(boolean selectAll) {
+    root.setState(selectAll ? TriStateCheckBox.State.CHECKED : TriStateCheckBox.State.UNCHECKED);
+    root.updateStateOfRelatedNodes();
+    this.repaint();
+    
+    // even though this isn't a click in the tree, the selection has changed -
+    // notify all listeners
+    notifyCheckingListeners();
+  }
+  
+  
+  /**
+   * TODO - this method doesn't take into account a possibility that the
+   *        filter tree might have changed
+   * 
+   * @param rootsOfCheckedPaths A list of TreePath objects which represent a checking state of
+   * the nodes in this tree (as returned by <code>getRootsOfCheckedPaths()</code>).
+   * 
+   * The last node of each path is the one that should have <code>TriStateCheckBox.State.CHECKED</code>
+   * state (so that last node is a root of checked path that start at that node). Related partial
+   * checkings for the UI can be computed from that by the tree checking model.
+   * 
+   * Therefore, a single "real" checking per provided TreePath from <code>rootsOfCheckedPaths</code> is
+   * made.
+   */
+  public void restoreFilterCheckingSettings(List<TreePath> rootsOfCheckedPaths)
+  {
+    // start with removing all selections
+    this.selectAllNodes(false);
+    
+    for (TreePath p : rootsOfCheckedPaths) {
+      restoreTreePathCheckingSettings(this.root, p);
+    }
+  }
+  
+  /**
+   * A worker method for <code>restoreFilterCheckingSettings(List<TreePath> rootsOfCheckedPaths)</code>.
+   * See that method for further details.
+   * 
+   * @param startNode A node of this tree.
+   * @param pathFromStartNode A TreePath object from the stored filter, where the first node must be 
+   *                          equals to <code>startNode</code> (based on the <code>userObject</code>,
+   *                          but not the checking state), should the traversal of the tree result in
+   *                          checking the last node of this TreePath eventually - which is the goal
+   *                          of this method.
+   * @return True if traversal of <code>pathFromStartNode</code> succeeded and a node in this tree was checked;
+   *         false if traversal couldn't find a matching node in this tree, and so no checking was made.
+   */
+  private boolean restoreTreePathCheckingSettings(TriStateTreeNode startNode, TreePath pathFromStartNode)
+  {
+    if (startNode == null || pathFromStartNode == null || pathFromStartNode.getPathCount() == 0) {
+      // no match - no data to work with
+      return (false);
+    }
+    
+    // compare the "roots" - provided start node and the root of the provided path
+    // (based on the 'user object', but not the selection state)
+    if (startNode.equals(pathFromStartNode.getPathComponent(0)))
+    {
+      if (pathFromStartNode.getPathCount() == 1) {
+        // provided startNode is equals to the only node in the provided tree path -
+        // so it is the node to mark as checked; also - make sure that this selection
+        // propagates through tree
+        startNode.setState(TriStateCheckBox.State.CHECKED, true);
+        
+        // we've found the required node in this path - no further search needed,
+        // so terminate this method
+        return (true);
+      }
+      else {
+        // provided startNode is equals to the first node of the provided tree path -
+        // meaning that at this stage we need to traverse all children of the startNode
+        // and look for the child that would match the next element in the provided tree path
+        //
+        // to do this, produce a new tree path from the provided one that doesn't contain
+        // the first node - then proceed recursively
+        Object[] currentPathComponents = pathFromStartNode.getPath();
+        Object[] reducedPathComponents = new Object[currentPathComponents.length - 1];
+        System.arraycopy(currentPathComponents, 1, reducedPathComponents, 0, currentPathComponents.length - 1);
+        
+        Enumeration children = startNode.children();
+        while (children.hasMoreElements()) {
+          TriStateTreeNode currentChild = (TriStateTreeNode)children.nextElement();
+          
+          // if recursive call succeeds, no need to iterate any further
+          if (restoreTreePathCheckingSettings(currentChild, new TreePath(reducedPathComponents))) return (true);
+        }
+      }
+    }
+    
+    // the startNode doesn't match the the first element in the provided tree path
+    // or no match could be found during recursive search for the node to "check"
+    return (false);
+  }
+  
+  
+  /**
+   * Expands all paths in this tree.
+   */
+  public void expandAll()
+  {
+    // this simply expands all tree nodes
+    // TODO - this actually "freezes" the UI if there are many nodes in the tree
+    //        some better solution to be found (e.g. expand the nodes in the model, then update UI, or similar)
+    for (int i = 0; i < getRowCount(); i++) {
+      instanceOfSelf.expandRow(i);
+    }
+  }
+  
+  
+  /**
+   * Collapses all paths in this tree.
+   */
+  public void collapseAll()
+  {
+    // this simply collapses all expanded nodes - this is very quick, execute just as it is
+    for (int i = getRowCount() - 1; i >= 0; i--) {
+      instanceOfSelf.collapseRow(i);
+    }
+  }
+  
+  
+  /**
+   * Removes all nodes in this tree that are unchecked.
+   * 
+   * It doesn't iterate through *all* nodes - if some node is
+   * indeed unchecked, it removes that node and any children that
+   * it has (because unchecked node is the root of an unchecked path).
+   */
+  public void removeAllUncheckedNodes()
+  {
+    // get the tree model first - will be used to remove the nodes
+    DefaultTreeModel theTreeModel = (DefaultTreeModel)this.treeModel;
+    
+    // remove unchecked nodes
+    List<TreePath> allNodesToRemove = this.getRootsOfUncheckedPaths();
+    for (TreePath p : allNodesToRemove) {
+      theTreeModel.removeNodeFromParent((MutableTreeNode)p.getLastPathComponent());
+    }
+  }
+  
+  
+  /**
+   * Provides access to the contextual menu of this JTriStateTree.
+   * 
+   * @return Reference to the contextual menu.
+   */
+  public JPopupMenu getContextualMenu() {
+    return contextualMenu;
+  }
+  
+  
+  public void setCheckingEnabled(boolean bCheckingEnabled) {
+    this.bCheckingEnabled = bCheckingEnabled;
+  }
+  
+  /**
+   * @return True if the current state of this JTriStateTree
+   *         allows making changes to checking of checkboxes
+   *         in its nodes.
+   */
+  public boolean isCheckingEnabled() {
+    return bCheckingEnabled;
+  }
+  
+  
+  /**
+   * @param listener New tree checking listener to register for updates
+   *                 to tree node selections.
+   */
+  public void addCheckingListener(TriStateTreeCheckingListener listener) {
+    if (listener != null) {
+      this.checkingListeners.add(listener);
+    }
+  }
+  
+  
+  /**
+   * @param listener Tree checking listener to remove.
+   */
+  public void removeCheckingListener(TriStateTreeCheckingListener listener) {
+    if (listener != null) {
+      this.checkingListeners.remove(listener);
+    }
+  }
+  
+  
+  /**
+   * Sends a signal to all listeners to check the state of the tree,
+   * as it has changed. 
+   */
+  private void notifyCheckingListeners() {
+    for (TriStateTreeCheckingListener listener : this.checkingListeners) {
+      listener.triStateTreeCheckingChanged(instanceOfSelf);
+    }
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/Swing - Tristate CheckBox.7z
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/Swing - Tristate CheckBox.7z b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/Swing - Tristate CheckBox.7z
new file mode 100644
index 0000000..8c60d87
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/Swing - Tristate CheckBox.7z differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/Test.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/Test.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/Test.java
new file mode 100644
index 0000000..6d58db3
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/Test.java
@@ -0,0 +1,67 @@
+package net.sf.taverna.biocatalogue.ui.tristatetree;
+
+import java.awt.BorderLayout;
+
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class Test extends JFrame
+{
+  public Test() {
+    this.setLayout(new BorderLayout());
+    
+    this.add(new TriStateCheckBox("LOL", TriStateCheckBox.State.PARTIAL), BorderLayout.NORTH);
+    
+    TriStateTreeNode root = new TriStateTreeNode("root");
+    TriStateTreeNode c1 = new TriStateTreeNode("child 1");
+    TriStateTreeNode c2 = new TriStateTreeNode("child 2");
+    TriStateTreeNode c3 = new TriStateTreeNode("child 3");
+    
+    TriStateTreeNode c1_1 = new TriStateTreeNode("child 1_1");
+    TriStateTreeNode c1_2 = new TriStateTreeNode("child 1_2");
+    TriStateTreeNode c1_3 = new TriStateTreeNode("child 1_3");
+    
+    TriStateTreeNode c2_1 = new TriStateTreeNode("child 2_1");
+    TriStateTreeNode c2_2 = new TriStateTreeNode("child 2_2");
+    TriStateTreeNode c2_3 = new TriStateTreeNode("child 2_3");
+    
+    TriStateTreeNode c3_1 = new TriStateTreeNode("child 3_1");
+    TriStateTreeNode c3_2 = new TriStateTreeNode("child 3_2");
+    TriStateTreeNode c3_3 = new TriStateTreeNode("child 3_3");
+    
+    TriStateTreeNode c1_1_1 = new TriStateTreeNode("child 1_1_1");
+    TriStateTreeNode c1_1_2 = new TriStateTreeNode("child 1_1_2");
+    TriStateTreeNode c1_1_3 = new TriStateTreeNode("child 1_1_3");
+    
+    // adding second level children
+    root.add(c1); root.add(c2); root.add(c3);
+    
+    // adding third-level children
+    c1.add(c1_1); c1.add(c1_2); c1.add(c1_3);
+    c2.add(c2_1); c2.add(c2_2); c2.add(c2_3);
+    c3.add(c3_1); c3.add(c3_2); c3.add(c3_3);
+    
+    // adding fourth-level children
+    c1_1.add(c1_1_1); c1_1.add(c1_1_2); c1_1.add(c1_1_3);
+    
+    
+    // NB! important to create the tree when 'root' is already populated with children
+    JTriStateTree tree = new JTriStateTree(root);
+    tree.setRootVisible(false);
+    tree.setShowsRootHandles(true);
+    this.add(new JScrollPane(tree), BorderLayout.CENTER);
+    
+    this.setDefaultCloseOperation(EXIT_ON_CLOSE);
+    this.pack();
+  }
+  
+
+  public static void main(String[] args) {
+    JFrame a = new Test();
+    a.setVisible(true);
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateCheckBox.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateCheckBox.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateCheckBox.java
new file mode 100644
index 0000000..361dfc3
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateCheckBox.java
@@ -0,0 +1,172 @@
+package net.sf.taverna.biocatalogue.ui.tristatetree;
+
+/*
+ * Taken from: http://72.5.124.102/thread.jspa?threadID=721308&messageID=9955637
+ * Data webpage accessed: 07/February/2010
+ * 
+ * Modified by Sergejs Aleksejevs
+ */
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.GradientPaint;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+ 
+import javax.swing.Icon;
+import javax.swing.JCheckBox;
+import javax.swing.JToggleButton;
+import javax.swing.UIManager;
+ 
+public class TriStateCheckBox extends JCheckBox
+{
+  private static final long serialVersionUID = 1L;
+  
+  public static enum State {
+    CHECKED, UNCHECKED, PARTIAL
+  };
+  
+  
+  // these colors will be used for painting the 'partial' state of the checkbox -
+  // a gradient painting will be made from top-left to bottom-right
+  private Color partialStateTopLeftColor = Color.GREEN.darker().darker().darker();
+  private Color partialStateBottomRightColor = Color.GREEN.brighter().brighter().brighter();
+  
+  
+  /**
+   * Creates an initially unselected check box button with no text, no icon.
+   */
+  public TriStateCheckBox() {
+    this(null, State.UNCHECKED);
+  }
+ 
+  /**
+   * Creates a check box with text and icon, and specifies whether or not it is initially
+   * selected.
+   * 
+   * @param text
+   *            The text of the check box.
+   * @param initial
+   *            The initial state
+   */
+  public TriStateCheckBox(String text, State initial) {
+    super.setText(text);
+    setModel(new TriStateModel(initial));
+    setIcon(new TriStateIcon(this));
+    // some UI settings
+    setRolloverEnabled(true);
+  }
+ 
+  /**
+   * Set the new state to either CHECKED, PARTIAL or UNCHECKED.
+   */
+  public void setState(State state) {
+    ((TriStateModel) model).setState(state);
+  }
+ 
+  /**
+   * Return the current state, which is determined by the selection status of the model.
+   */
+  public State getState() {
+    return ((TriStateModel) model).getState();
+  }
+ 
+  public void setSelected(boolean selected) {
+    ((TriStateModel) model).setSelected(selected);
+  }
+ 
+  /** The model for the button */
+  private static class TriStateModel extends JToggleButton.ToggleButtonModel {
+ 
+    private static final long serialVersionUID = 1L;
+    protected State state;
+ 
+    public TriStateModel(State state) {
+      this.state = state;
+    }
+ 
+    public boolean isSelected() {
+      return state == State.CHECKED;
+    }
+ 
+    public State getState() {
+      return state;
+    }
+ 
+    public void setState(State state) {
+      this.state = state;
+      fireStateChanged();
+    }
+ 
+    public void setPressed(boolean pressed) {
+      if (pressed) {
+        switch (state) {
+          case UNCHECKED:
+            state = State.CHECKED;
+            break;
+          case PARTIAL:
+            state = State.UNCHECKED;
+            break;
+          case CHECKED:
+            state = State.PARTIAL;
+            break;
+        }
+      }
+ 
+    }
+ 
+    public void setSelected(boolean selected) {
+      if (selected) {
+        this.state = State.CHECKED;
+      } else {
+        this.state = State.UNCHECKED;
+      }
+    }
+  }
+ 
+  private class TriStateIcon implements Icon
+  {
+    private Icon checkBoxIcon;
+    private TriStateCheckBox triStateCheckBox;
+    public TriStateIcon(TriStateCheckBox triStateCheckBox) {
+      this.triStateCheckBox = triStateCheckBox;
+      this.checkBoxIcon = UIManager.getIcon("CheckBox.icon");
+      
+      return;
+    }
+ 
+    public final int getIconHeight() {
+      return this.checkBoxIcon.getIconHeight();
+ 
+    }
+ 
+    public final int getIconWidth() {
+      return this.checkBoxIcon.getIconWidth();
+ 
+    }
+    
+    
+    public void paintIcon(Component c, Graphics g, int x, int y)
+		{
+			checkBoxIcon.paintIcon(triStateCheckBox, g, x, y);
+			if (triStateCheckBox.getState().equals(TriStateCheckBox.State.PARTIAL))
+			{
+			  // this is changed to create the gradient paint dynamically every time;
+			  // this makes sure that the gradient is relative to the actual position of the checkbox,
+			  // rather than in the absolute coordinates of the parent component
+			  GradientPaint gradient = new GradientPaint(x, y, partialStateTopLeftColor, x + 8, y + 8, partialStateBottomRightColor, false);
+      
+				Graphics2D g2d = (Graphics2D) g;
+				g2d.setPaint(gradient);
+				final int deltaX = 2;
+				final int deltaY = 2;
+				final int xNew = x + deltaX;
+				final int yNew = y + deltaY;
+				final int width = getIconWidth() - 2*deltaX;
+				final int height = getIconHeight() - 2*deltaY;
+				g2d.fillRect(xNew, yNew, width, height);
+			}
+		}
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateCheckBoxTreeCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateCheckBoxTreeCellRenderer.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateCheckBoxTreeCellRenderer.java
new file mode 100644
index 0000000..dd78bf7
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateCheckBoxTreeCellRenderer.java
@@ -0,0 +1,62 @@
+package net.sf.taverna.biocatalogue.ui.tristatetree;
+
+import java.awt.Color;
+import java.awt.Component;
+
+import javax.swing.JLabel;
+import javax.swing.JTree;
+import javax.swing.border.Border;
+import javax.swing.tree.DefaultTreeCellRenderer;
+
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+
+
+/**
+ * Provides a mechanism for rendering tri-state tree nodes.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class TriStateCheckBoxTreeCellRenderer extends DefaultTreeCellRenderer
+{
+  public Component getTreeCellRendererComponent(JTree tree, Object value,
+      boolean selected, boolean expanded, boolean leaf, int row,
+      boolean hasFocus)
+  {
+    Border treeNodePanelBorder = null; // will be obtained from default rendering and applied to the new one
+    Color backgroundColor = null;      // likewise: will be applied to all constituents of the new rendering
+    
+    // obtain the default rendering, we'll then customize it
+    Component defaultRendering = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
+    
+    // it is most likely that the default rendering will be a JLabel, check just to be safe
+    if (defaultRendering instanceof JLabel)
+    {
+      JLabel defaultRenderedLabel = ((JLabel)defaultRendering);
+      
+      // if this is not the case, it kind of undermines the whole purpose
+      // of using this tree cell renderer, but check just to be sure
+      if (value instanceof TriStateTreeNode) {
+        // a state value from within the TriStateTreeNode will be used to
+        // set the correct state in its rendering
+        switch (((TriStateTreeNode)value).getState()) {
+          case CHECKED: 
+            if (((TriStateTreeNode)value).getPath().length > 2) {
+              // only allow CHECKED state icon for nodes that are deeper than second
+              // level in the tree - that is for any nodes that do not represent categories
+              // in the tree (root is not shown, so nodes that represent categories are
+              // effectively multiple category "roots" that have actual contents inside them)
+              defaultRenderedLabel.setIcon(ResourceManager.getImageIcon(ResourceManager.TRISTATE_CHECKBOX_CHECKED_ICON));
+              break;
+            }
+            // else -- 'fall through' to PARTIAL icon: this was a CHECKED state for the category node
+          case PARTIAL: defaultRenderedLabel.setIcon(ResourceManager.getImageIcon(ResourceManager.TRISTATE_CHECKBOX_PARTIAL_ICON)); break;
+          case UNCHECKED: defaultRenderedLabel.setIcon(ResourceManager.getImageIcon(ResourceManager.TRISTATE_CHECKBOX_UNCHECKED_ICON)); break;
+          default: defaultRenderedLabel.setIcon(null); break;
+        }
+      }
+    }
+    
+    return (defaultRendering);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateTreeCheckingListener.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateTreeCheckingListener.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateTreeCheckingListener.java
new file mode 100644
index 0000000..e612e54
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateTreeCheckingListener.java
@@ -0,0 +1,12 @@
+package net.sf.taverna.biocatalogue.ui.tristatetree;
+
+/**
+ * A simple interface to enable tracking tree checking
+ * changes.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public interface TriStateTreeCheckingListener
+{
+  public void triStateTreeCheckingChanged(JTriStateTree source);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateTreeNode.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateTreeNode.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateTreeNode.java
new file mode 100644
index 0000000..248cdf8
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/ui/tristatetree/TriStateTreeNode.java
@@ -0,0 +1,246 @@
+package net.sf.taverna.biocatalogue.ui.tristatetree;
+
+import java.util.Enumeration;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+
+/**
+ * This class models tri-state nodes in the tree. Effectively
+ * it associates a tri-state checkbox with each tree node.
+ * 
+ * Useful for partial selections of hierarchical data -
+ * partial selection of a node indicates that some of the
+ * children of that node are selected.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class TriStateTreeNode extends DefaultMutableTreeNode
+{
+  private TriStateCheckBox.State state;
+  
+  /**
+   * Creates a regular tree node; associated tri-state checkbox state is set to UNCHECKED.
+   *  
+   * @param userObject The object this tree node will represent.
+   */
+  public TriStateTreeNode(Object userObject) {
+    this(userObject, TriStateCheckBox.State.UNCHECKED);
+  }
+  
+  /**
+   * Creates a regular tree node; associated tri-state checkbox state is set to the provided <code>initialState</code> value.
+   *  
+   * @param userObject The object this tree node will represent.
+   * @param initialState One of the enum values of <code>TriStateCheckBox.State</code>.
+   */
+  public TriStateTreeNode(Object userObject, TriStateCheckBox.State initialState) {
+    super(userObject);
+    this.state = initialState;
+  }
+  
+  
+  /**
+   * Compares based on the user object, not the state of this node.
+   */
+  public boolean equals(Object other) {
+    if (other instanceof TriStateTreeNode) {
+      return (this.userObject.equals(((TriStateTreeNode)other).userObject));
+    }
+    else {
+      return (false);
+    }
+  }
+  
+  
+  /**
+   * Sets the state of the current node and (optionally) propagates
+   * those changes through the tree.
+   * 
+   * @param state The new state to set - value from <code>TriStateCheckBox.State</code> enum.
+   * @param propagateChangesToRelatedNodes True - to use the tree checking model to
+   *                 propagate changes of the state of the current tree node to the
+   *                 other related tree nodes (e.g. all descendants and ancestors) -
+   *                 up and down the tree hierarchy. False - to only update the current
+   *                 node and make no changes to the rest of the tree.
+   */
+  public void setState(TriStateCheckBox.State state, boolean propagateChangesToRelatedNodes)
+  {
+    this.state = state;
+    
+    // check if the tree checking model should be activated
+    if (propagateChangesToRelatedNodes) {
+      updateStateOfRelatedNodes();
+    }
+  }
+  
+  
+  /**
+   * Sets the state of the current node.
+   * 
+   * @param state The new state to set - value from <code>TriStateCheckBox.State</code> enum.
+   */
+  public void setState(TriStateCheckBox.State state) {
+    setState(state, false);
+  }
+  
+  
+  public TriStateCheckBox.State getState() {
+    return state;
+  }
+  
+  
+  /**
+   * Toggles the state of the associated tri-state checkbox.
+   * State transitions are as follows:</br>
+   * <code>
+   * TriStateCheckBox.State.CHECKED -> TriStateCheckBox.State.UNCHECKED
+   * TriStateCheckBox.State.PARTIAL -> TriStateCheckBox.State.UNCHECKED
+   * TriStateCheckBox.State.UNCHECKED -> TriStateCheckBox.State.CHECKED
+   * </code>
+   *
+   * @param propagateChangesToRelatedNodes True - to use the tree checking model to
+   *                 propagate changes of the state of the current tree node to the
+   *                 other related tree nodes (e.g. all descendants and ancestors) -
+   *                 up and down the tree hierarchy. False - to only update the current
+   *                 node and make no changes to the rest of the tree. 
+   * @return The value of the new state.
+   */
+  public TriStateCheckBox.State toggleState(boolean propagateChangesToRelatedNodes)
+  {
+    if (state.equals(TriStateCheckBox.State.CHECKED) || state.equals(TriStateCheckBox.State.PARTIAL)) {
+      state = TriStateCheckBox.State.UNCHECKED;
+    }
+    else if (state.equals(TriStateCheckBox.State.UNCHECKED)) {
+      state = TriStateCheckBox.State.CHECKED;
+    }
+    
+    // check if the tree checking model should be activated
+    if (propagateChangesToRelatedNodes) {
+      updateStateOfRelatedNodes();
+    }
+    
+    return (state);
+  }
+  
+  
+  /* 
+   * === The tree CHECKING MODEL ===
+   * 
+   * Effectively, this defines the way the tree reacts to it's nodes
+   * being checked / unchecked. Only one model is implemented at the
+   * moment, therefore it's not extracted into a separate class, but
+   * remains to be a part of the TriStateTreeNode.
+   * 
+   * Could possibly be better placed within the JTriStateTree, rather
+   * than TriStateTreeNode.
+   */
+  
+  /**
+   * The entry point - must be invoked to traverse the tree and make
+   * changes to checking states of related tree nodes. 
+   */
+  public void updateStateOfRelatedNodes()
+  {
+    updateStateOfAncestors(this.getParent());
+    updateStateOfDescendants(this);
+  }
+  
+  
+  /**
+   * Recursively visits all ancestors of the <code>parentNode</code>
+   * and decides on their checking states based on the states of their
+   * children nodes.
+   * 
+   * @param parentNode Initially - parent node of the current node (i.e. the one,
+   *                   for which a state update has been made); then updated for
+   *                   recursive calls.
+   */
+  private void updateStateOfAncestors(TreeNode parentNode)
+  {
+    // reached root of the tree, do nothing - return
+    if (parentNode == null) {
+      return;
+    }
+    
+    if (parentNode instanceof TriStateTreeNode) {
+      TriStateTreeNode parentTriStateNode = (TriStateTreeNode)parentNode;
+      
+      // explicitly fetch children into a new enumeration - this is
+      // to make sure that we work with the same enumeration, rather
+      // than obtaining a fresh one with every reference to 'parentTriStateNode.children()'
+      Enumeration childNodes = parentTriStateNode.children();
+      
+      // go through all the children and count the number of selected ones
+      int iChildrenCount = 0;
+      int iPartiallySelectedChildren = 0;
+      int iSelectedChildren = 0;
+      
+      while(childNodes.hasMoreElements()) {
+        Object node = childNodes.nextElement();
+        if (node instanceof TriStateTreeNode) {
+          TriStateTreeNode currentNode = (TriStateTreeNode)node;
+          iChildrenCount++;
+          if (currentNode.getState().equals(TriStateCheckBox.State.CHECKED)) {
+            iSelectedChildren++;
+          }
+          else if (currentNode.getState().equals(TriStateCheckBox.State.PARTIAL)) {
+            iPartiallySelectedChildren++;
+          }
+        }
+      }
+      
+      
+      // decide on the state of the 'parentNode' based on the checking state of its children
+      if (iSelectedChildren == 0 && iPartiallySelectedChildren == 0) {
+        // no children are selected
+        parentTriStateNode.setState(TriStateCheckBox.State.UNCHECKED);
+      }
+      else if ((iSelectedChildren + iPartiallySelectedChildren) > 0 && iSelectedChildren < iChildrenCount) {
+        // some children are selected (either partially or fully)
+        parentTriStateNode.setState(TriStateCheckBox.State.PARTIAL);
+      }
+      else if (iSelectedChildren > 0 && iSelectedChildren == iChildrenCount) {
+        // all children are selected
+        parentTriStateNode.setState(TriStateCheckBox.State.CHECKED);
+      }
+      
+      
+      // repeat the same recursively up the hierarchy
+      updateStateOfAncestors(parentTriStateNode.getParent());
+    }
+  }
+
+  /**
+   * Recursively traverses all descendants of the <code>parentNode</code>
+   * to set their checking state to the value of the state of the <code>parentNode</code>. 
+   * 
+   * @param parentNode Initially - the tree node for which the state
+   *                   change was made; then updated for recursive calls.
+   */
+  private void updateStateOfDescendants(TriStateTreeNode parentNode)
+  {
+    // explicitly fetch children into a new enumeration - this is
+    // to make sure that we work with the same enumeration, rather
+    // than obtaining a fresh one with every reference to 'parentNode.children()'
+    Enumeration childNodes = parentNode.children();
+    
+    // for all child nodes do 2 things:
+    // - set their state as that of the parent;
+    // - repeat the same recursively with their children
+    while(childNodes.hasMoreElements()) {
+      Object node = childNodes.nextElement();
+      if (node instanceof TriStateTreeNode) {
+        TriStateTreeNode currentNode = (TriStateTreeNode) node; 
+        currentNode.setState(parentNode.getState());
+        currentNode.updateStateOfDescendants(currentNode);
+      }
+    }
+  }
+  
+  /*
+   * === End of CHECKING MODEL implementation.
+   */
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/BioCataloguePerspective.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/BioCataloguePerspective.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/BioCataloguePerspective.java
new file mode 100644
index 0000000..ac170dc
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/BioCataloguePerspective.java
@@ -0,0 +1,70 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue;
+
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.swing.ImageIcon;
+
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI;
+
+import org.jdom.Element;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class BioCataloguePerspective implements PerspectiveSPI
+{
+  private MainComponent perspectiveMainComponent;
+	private boolean visible = true;
+
+	public ImageIcon getButtonIcon()
+	{
+		return ResourceManager.getImageIcon(ResourceManager.FAVICON);
+	}
+
+	public InputStream getLayoutInputStream() {
+	  return getClass().getResourceAsStream("biocatalogue-perspective.xml");
+	}
+
+	public String getText() {
+		return "Service Catalogue";
+	}
+
+	public boolean isVisible() {
+		return visible;
+	}
+
+	public int positionHint()
+	{
+	  // this determines position of perspective in the
+    // bar with perspective buttons (currently makes it the last in
+    // the list)
+    return 40;
+	}
+
+	public void setVisible(boolean visible) {
+		this.visible = visible;
+		
+	}
+
+	public void update(Element layoutElement) {
+		// TODO Auto-generated method stub
+		
+		// Not sure what to do here
+	}
+	
+  public void setMainComponent(MainComponent component)
+  {
+    this.perspectiveMainComponent = component;
+  }
+  
+  /**
+   * Returns the instance of the main component of this perspective.
+   */
+  public MainComponent getMainComponent()
+  {
+    return this.perspectiveMainComponent;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponent.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponent.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponent.java
new file mode 100644
index 0000000..eeb1cba
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponent.java
@@ -0,0 +1,285 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.GridLayout;
+
+import javax.swing.ImageIcon;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.ToolTipManager;
+import javax.swing.border.LineBorder;
+
+import net.sf.taverna.biocatalogue.model.BioCataloguePluginConstants;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.biocatalogue.ui.BioCatalogueExplorationTab;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+
+import org.apache.log4j.Logger;
+
+/*
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public final class MainComponent extends JPanel implements UIComponentSPI //, ChangeListener
+{
+//  private static final String windowBaseTitle = "BioCatalogue API Demo";
+//  private HashMap<String, String> windowTitleMap;
+  
+  private MainComponent pluginPerspectiveMainComponent;
+  private final Logger logger = Logger.getLogger(MainComponent.class);
+  
+  //private JTabbedPane tpMainTabs;
+  private BioCatalogueExplorationTab jpBioCatalogueExplorationTab;
+//  private BioCataloguePluginAbout jpAboutTab;
+  
+  public static JFrame dummyOwnerJFrame;
+  static {
+    // this is only to have a nice icon on all Dialog boxes - can be removed at any time
+    dummyOwnerJFrame = new JFrame();
+    dummyOwnerJFrame.setIconImage(ResourceManager.getImageIcon(ResourceManager.FAVICON).getImage());
+  }
+  
+  
+  /**
+   * This constructor is protected, and so is only available to the classes in its package -
+   * i.e. Taverna integration classes. Other parts of the plugin must use <code>MainComponentFactory.getSharedInstance()</code>
+   * to get the shared instance of this class.
+   */
+	protected MainComponent()
+	{
+	  super();
+	  initialiseEnvironment();
+	  initialisePerspectiveUI();
+	}
+	
+	
+  // *** Methods implementing UIComponentSPI interface ***
+	
+	public ImageIcon getIcon() {
+		return WorkbenchIcons.databaseIcon;
+	}
+
+	@Override
+	public String getName() {
+		return "Service Catalogue Perspective Main Component";
+	}
+
+	public void onDisplay() {
+		// TODO Auto-generated method stub
+	}
+
+	public void onDispose() {
+		// TODO Auto-generated method stub
+	}
+	
+	// *** End of methods implementing UIComponentSPI interface ***
+	
+	
+	private void initialiseEnvironment()
+	{
+	  // before anything else - store a reference to self for use during
+	  // initialisation of other components
+	  pluginPerspectiveMainComponent = this;
+	  
+	  
+	  // pre-load classes for FlyingSaucer XHTML renderer - this will make sure
+    // that the first time it is used, there will be no delay while classes
+    // are loaded by Java
+    new Thread("class pre-loading") {
+      public void run() {
+        try {
+          Class.forName("org.xhtmlrenderer.simple.FSScrollPane");
+          Class.forName("org.xhtmlrenderer.simple.XHTMLPanel");
+        }
+        catch (ClassNotFoundException e) {
+          logger.error("Problem while pre-loading classes for FlyingSaucer XHTML renderer", e);
+        }
+      }
+    }.start();
+    
+    
+    // determine what folder is to be used for config files
+    if (!Util.isRunningInTaverna()) {
+      // running outside Taverna, place config file and log into the user's home directory
+      BioCataloguePluginConstants.CONFIG_FILE_FOLDER = 
+        new java.io.File(System.getProperty("user.home"), BioCataloguePluginConstants.CONFIG_FILE_FOLDER_WHEN_RUNNING_STANDALONE);
+      BioCataloguePluginConstants.LOG_FILE_FOLDER = 
+        new java.io.File(System.getProperty("user.home"), BioCataloguePluginConstants.CONFIG_FILE_FOLDER_WHEN_RUNNING_STANDALONE);
+    }
+    
+    
+    // this makes sure that tooltips will stay displayed for longer than default 
+    ToolTipManager.sharedInstance().setDismissDelay(BioCataloguePluginConstants.DEFAULT_TOOLTIP_DURATION);
+    
+    // these components must be accessed by all other components, hence need
+    // to be initialised before any other initialisation is done
+
+//    windowTitleMap = new HashMap<String,String>();
+	}
+	
+	private void initialisePerspectiveUI()
+	{
+	  // set the loader icon to show that the perspective is loading
+	  this.setLayout(new GridLayout(1,1));
+	  this.add(new JLabel(ResourceManager.getImageIcon(ResourceManager.BAR_LOADER_ORANGE)));
+	  
+	  new Thread("Initialise Service Catalogue Perspective UI")
+	  {
+	    public void run() {
+	      // create all tabs prior to putting them inside the tabbed pane
+	      jpBioCatalogueExplorationTab = new BioCatalogueExplorationTab();
+//	      jpServiceFilteringTab = new ServiceFilteringTab(pluginPerspectiveMainComponent, client, logger);
+//	      jpSearchTab = new SearchTab(pluginPerspectiveMainComponent, client, logger);
+//	      jpAboutTab = new BioCataloguePluginAbout(pluginPerspectiveMainComponent, client, logger);
+	      
+	      // create main tabs
+//	      tpMainTabs = new JTabbedPane();
+//	      tpMainTabs.add("Explore BioCatalogue", jpBioCatalogueExplorationTab);
+//	      tpMainTabs.add("Search", jpSearchTab);
+//	      tpMainTabs.add("Filter Services", jpServiceFilteringTab);
+//	      tpMainTabs.add("About", jpAboutTab);
+	      
+	      SwingUtilities.invokeLater(new Runnable() {
+          public void run()
+          {
+            // add main tabs and the status bar into the perspective
+            pluginPerspectiveMainComponent.removeAll();
+            pluginPerspectiveMainComponent.setLayout(new BorderLayout());
+            pluginPerspectiveMainComponent.setBorder(new LineBorder(Color.BLACK));
+            pluginPerspectiveMainComponent.add(jpBioCatalogueExplorationTab, BorderLayout.CENTER);
+            
+            // everything is prepared -- need to focus default component on the first tab
+            // (artificially generate change event on the tabbed pane to perform focus request)
+//            tpMainTabs.setSelectedIndex(1);
+//            tpMainTabs.addChangeListener(pluginPerspectiveMainComponent);
+//           tpMainTabs.setSelectedIndex(0);
+          }
+        });
+	    }
+	  }.start();
+	}
+	
+	/**
+   * Determines whether the specified tab is currently active in the main tabbed pane.
+   * @param strTabClassName Class name of the tab which is tested for being active.
+   * @return True if specified tab is currently active.
+   */
+/*  
+ private boolean isTabActive(String strTabClassName)
+  {
+    if (tpMainTabs == null) return (false);
+    
+    // if an anonymous thread within the main tab component class will call
+    // this method, we want to store the main tab's class, rather than
+    // the full class name of the anonymous worker thread
+    String strCurSelectedTabClassName = tpMainTabs.getSelectedComponent().getClass().getName();
+    
+    // get the real class name to match
+    String strBaseClassName = Util.getBaseClassName(strTabClassName);
+    
+    // compare the two class names
+    return (strBaseClassName.equals(strCurSelectedTabClassName));
+  }
+  */
+  
+  
+  /**
+   * This method "selects" the tab represented by the component provided as a parameter.
+   * 
+   * @param c Component that represents one of the tabs in the main tabbed pane.
+   *          If <code>c</code> is not found within the components of the tabbed pane, nothing will happen.
+   */
+/*  
+  public void setTabActive(Component c)
+  {
+    try {
+      tpMainTabs.setSelectedComponent(c);
+    }
+    catch (IllegalArgumentException e) {
+      // do nothing, can't activate component which is not in the tabbed pane
+    }
+  }
+  */
+  
+  
+  /**
+   * Sets title of the main perspective window for a specified tab,
+   * thus supporting different window titles for different tabs;
+   * new title will only be displayed immediately if the specified tab.
+   * 
+   * @param strTabClassName Class name of the tab, for which window title should be updated.
+   * @param strTitle New title to set.
+   */
+/* 
+  public void setWindowTitle(String strTabClassName, String strTitle)
+  {
+    // if an anonymous thread within the main tab component class will call
+    // this method, we want to store the main tab's class, rather than
+    // the full class name of the anonymous worker thread
+    String strBaseClassName = Util.getBaseClassName(strTabClassName);
+    
+    // store the new title for for the specified tab
+    windowTitleMap.put(strBaseClassName, strTitle);
+    
+    // if the specified tab is active, update window title immediately
+    if (isTabActive(strBaseClassName)) displayWindowTitle(strBaseClassName);
+  }
+  */
+  
+  
+  /** 
+   * Displays window title that corresponds to specified tab.
+   * 
+   * @param strTabClassName Class name of the tab for which the window title is to be set.
+   */
+/*
+  public void displayWindowTitle(String strTabClassName)
+  {
+    // if an anonymous thread within the main tab component class will call
+    // this method, we want to store the main tab's class, rather than
+    // the full class name of the anonymous worker thread
+    String strBaseClassName = Util.getBaseClassName(strTabClassName);
+    
+    if (windowTitleMap.containsKey(strBaseClassName)) {
+      // title for specified tab found - show it
+      // TODO - disabled until this info will be shown in the status bar
+      //this.setTitle(windowBaseTitle + " :: " + windowTitleMap.get(strBaseClassName));
+    }
+    else {
+      // tab not found - display standard title
+      // TODO - disabled until this info will be shown in the status bar
+      //this.setTitle(windowBaseTitle);
+    }
+  }
+  */
+  
+  // *** Getters for various components ***
+  
+  /**
+   * @return Reference to the component that represents the BioCatalogue Exploration
+   *         tab in the BioCatalogue Perspective.
+   */
+  public BioCatalogueExplorationTab getBioCatalogueExplorationTab() {
+    return (this.jpBioCatalogueExplorationTab);
+  }
+    
+  // *** Callbacks for ChangeListener interface ***
+  
+/*  public void stateChanged(ChangeEvent e)
+  {
+    if (e.getSource().equals(tpMainTabs)) {
+      // will get called when selected tab changes - need to focus default component in the active tab
+      // and also set a correct window title
+      if (tpMainTabs.getSelectedComponent() instanceof HasDefaultFocusCapability) {
+        ((HasDefaultFocusCapability)tpMainTabs.getSelectedComponent()).focusDefaultComponent();
+        this.displayWindowTitle(tpMainTabs.getSelectedComponent().getClass().getName());
+      }
+    }
+  }*/
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponentFactory.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponentFactory.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponentFactory.java
new file mode 100644
index 0000000..c213600
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponentFactory.java
@@ -0,0 +1,46 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue;
+
+import javax.swing.ImageIcon;
+
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class MainComponentFactory implements UIComponentFactorySPI
+{
+  // this is to ensure that the whole perspective is not re-created
+  // each time it is being activated in Taverna, rather it will only
+  // happen once during the execution
+  private static MainComponent mainPerspectiveComponent = null;
+  
+	public static MainComponent getSharedInstance()
+	{
+	  // double-check on existence of the 'mainPerspectiveComponent' ensures
+    // that it is really created only once
+    if (mainPerspectiveComponent == null) {
+      synchronized(MainComponentFactory.class) {
+        if (mainPerspectiveComponent == null) {
+          mainPerspectiveComponent = new MainComponent();
+        }
+      }
+    }
+    return (mainPerspectiveComponent);
+	}
+	
+	public UIComponentSPI getComponent() {
+    return (getSharedInstance());
+  }
+	
+	
+	public ImageIcon getIcon() {
+		return WorkbenchIcons.databaseIcon;
+	}
+
+	public String getName() {
+		return "Service Catalogue Main Component Factory";
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponentShutdownHook.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponentShutdownHook.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponentShutdownHook.java
new file mode 100644
index 0000000..6c2e844
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/MainComponentShutdownHook.java
@@ -0,0 +1,49 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue;
+
+import com.thoughtworks.xstream.XStream;
+
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.config.BioCataloguePluginConfiguration;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.service_panel.BioCatalogueServiceProvider;
+import net.sf.taverna.t2.workbench.ShutdownSPI;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class MainComponentShutdownHook implements ShutdownSPI
+{
+  public int positionHint()
+  {
+    // all custom plugins are suggested to return a value of > 100;
+    // this affects when in the termination process will this plugin
+    // be shutdown;
+    return 100;
+  }
+  
+  public boolean shutdown()
+  {
+      // Do not save service providers in BioCatalogue's conf file - they should be saved by Taverna together with 
+      // other service providers
+	  
+//      // store services that were added to the Service Panel - both REST and SOAP
+//      XStream xstream = new XStream();
+//      
+//	  BioCataloguePluginConfiguration configuration = BioCataloguePluginConfiguration.getInstance();
+//      
+//      configuration.setProperty(
+//          BioCataloguePluginConfiguration.SOAP_OPERATIONS_IN_SERVICE_PANEL,
+//          xstream.toXML(BioCatalogueServiceProvider.getRegisteredSOAPOperations()));
+//      configuration.setProperty(
+//          BioCataloguePluginConfiguration.REST_METHODS_IN_SERVICE_PANEL,
+//          xstream.toXML(BioCatalogueServiceProvider.getRegisteredRESTMethods()));
+//      
+//      // save all the plugin's configuration 
+//      configuration.store();
+//      
+//      
+//      // close API operation log
+//      MainComponentFactory.getSharedInstance().getBioCatalogueClient().getAPILogWriter().close();
+//      
+      return true;
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/TestJFrameForLocalLaunch.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/TestJFrameForLocalLaunch.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/TestJFrameForLocalLaunch.java
new file mode 100644
index 0000000..28a52c7
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/TestJFrameForLocalLaunch.java
@@ -0,0 +1,68 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+
+import javax.swing.JFrame;
+
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class TestJFrameForLocalLaunch extends JFrame implements ComponentListener
+{
+  private static final int DEFAULT_POSITION_X = 225;
+  private static final int DEFAULT_POSITION_Y = 150;
+  
+  private static final int DEFAULT_WIDTH = 800;
+  private static final int DEFAULT_HEIGHT = 500;
+  
+  
+	private TestJFrameForLocalLaunch()
+  {
+    // set window title and icon
+	  this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+	  this.setTitle("Service Catalogue API Demo"/* TODO - windowBaseTitle */);
+    this.setIconImage(ResourceManager.getImageIcon(ResourceManager.FAVICON).getImage());
+    this.addComponentListener(this);
+    
+    // get content pane
+    Container contentPane = this.getContentPane();
+    contentPane.setLayout(new BorderLayout());
+    
+    // put main tabs into the content pane
+    contentPane.add(MainComponentFactory.getSharedInstance(), BorderLayout.CENTER);
+    
+    this.pack();
+  }
+	
+	
+  // *** Callbacks for ComponentListener interface ***
+  
+  public void componentResized(ComponentEvent e) { /* do nothing */ }
+  public void componentMoved(ComponentEvent e) { /* do nothing */ }
+  public void componentHidden(ComponentEvent e) { /* do nothing */ }
+  public void componentShown(ComponentEvent e) {
+    this.setLocation(DEFAULT_POSITION_X, DEFAULT_POSITION_Y);
+  }
+	
+  
+  /**
+   * This is a simple test class for launching BioCatalogue perspective
+   * from outside Taverna. At some point it will be not usable anymore,
+   * when proper integration of BioCatalogue plugin is made.
+   * 
+   * @author Sergejs Aleksejevs
+   */
+  public static void main(String[] args)
+  {
+    TestJFrameForLocalLaunch standaloneFrame = new TestJFrameForLocalLaunch();
+    standaloneFrame.setMinimumSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
+    standaloneFrame.setVisible(true);
+  }
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewCertDetailsDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewCertDetailsDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewCertDetailsDialog.java
new file mode 100644
index 0000000..953ed2d
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewCertDetailsDialog.java
@@ -0,0 +1,509 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.Font.BOLD;
+import static java.awt.Font.PLAIN;
+import static java.awt.GridBagConstraints.LINE_START;
+import static javax.security.auth.x500.X500Principal.RFC2253;
+import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
+import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED;
+import static javax.swing.SwingUtilities.invokeLater;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.math.BigInteger;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser;
+import net.sf.taverna.t2.security.credentialmanager.ParsedDistinguishedName;
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+/**
+ * Displays the details of a X.509 certificate.
+ * 
+ * Inspired by the Portlecle tool (http://portecle.sourceforge.net/). and the
+ * view certificate dialog from Firefox's Certificate Manager.
+ */
+@SuppressWarnings("serial")
+public class ViewCertDetailsDialog extends NonBlockedHelpEnabledDialog {
+	// Logger
+	//private static Logger logger = Logger.getLogger(ViewCertDetailsDialog.class);
+	
+	/** Stores certificate to display*/
+	private X509Certificate cert;
+	/** Stores list of serviceURLs to display*/
+	private ArrayList<String> serviceURLs;
+	private final DistinguishedNameParser dnParser;
+
+    /**
+     * Creates new ViewCertDetailsDialog dialog where the parent is a frame.
+     */
+	public ViewCertDetailsDialog(JFrame parent, String title, boolean modal,
+			X509Certificate crt, ArrayList<String> serviceURLs,
+			DistinguishedNameParser dnParser) throws CMException {
+		super(parent, title, modal);
+		this.cert = crt;
+		this.serviceURLs = serviceURLs;
+		this.dnParser = dnParser;
+		initComponents();
+	}
+
+	/**
+	 * Creates new ViewCertDetailsDialog dialog where the parent is a dialog.
+	 */
+	public ViewCertDetailsDialog(JDialog parent, String title, boolean modal,
+			X509Certificate crt, ArrayList<String> urlList,
+			DistinguishedNameParser dnParser) throws CMException {
+		super(parent, title, modal);
+		cert = crt;
+		serviceURLs = urlList;
+		this.dnParser = dnParser;
+		initComponents();
+	}
+
+	/**
+	 * Initialise the dialog's GUI components.
+	 * 
+	 * @throws CMException
+	 *             A problem was encountered getting the certificates' details
+	 */
+	private void initComponents() throws CMException {
+        // Certificate details:
+
+        // Grid Bag Constraints templates for labels (column 1) and 
+    	// values (column 2) of certificate details
+        GridBagConstraints gbcLabel = new GridBagConstraints();
+        gbcLabel.gridx = 0;
+        gbcLabel.ipadx = 20;
+        gbcLabel.gridwidth = 1;
+        gbcLabel.gridheight = 1;
+        gbcLabel.insets = new Insets(2, 15, 2, 2);
+        gbcLabel.anchor = LINE_START;
+
+        GridBagConstraints gbcValue = new GridBagConstraints();
+        gbcValue.gridx = 1;
+        gbcValue.gridwidth = 1;
+        gbcValue.gridheight = 1;
+        gbcValue.insets = new Insets(2, 5, 2, 2);
+        gbcValue.anchor = LINE_START;
+
+        /*
+		 * Netscape Certificate Type non-critical extension (if any) defines the
+		 * intended uses of the certificate - to make it look like firefox's
+		 * view certificate dialog. From openssl's documentation: "The [above]
+		 * extension is non standard, Netscape specific and largely obsolete.
+		 * Their use in new applications is discouraged."
+		 * 
+		 * TODO replace with "basicConstraints, keyUsage and extended key usage
+		 * extensions which are now used instead."
+		 */
+//        byte[] intendedUses = cert.getExtensionValue("2.16.840.1.113730.1.1"); //Netscape Certificate Type OID/*
+//        JLabel jlIntendedUses = null;
+//        JTextField jtfIntendedUsesValue = null;
+//        JPanel jpUses = null;
+//        GridBagConstraints gbc_jpUses = null;
+//        if (intendedUses != null)
+//        {
+//         	jlIntendedUses = new JLabel("This certificate has been approved for the following uses:");
+//         	jlIntendedUses.setFont(new Font(null, Font.BOLD, 11));
+//         	jlIntendedUses.setBorder(new EmptyBorder(5,5,5,5));
+//         	
+//         	jtfIntendedUsesValue = new JTextField(45);
+//         	jtfIntendedUsesValue.setText(CMUtils.getIntendedCertificateUses(intendedUses));
+//        	jtfIntendedUsesValue.setEditable(false);
+//        	jtfIntendedUsesValue.setFont(new Font(null, Font.PLAIN, 11));
+//             
+//        	jpUses = new JPanel(new BorderLayout()); 
+//        	jpUses.add(jlIntendedUses, BorderLayout.NORTH);
+//        	jpUses.add(jtfIntendedUsesValue, BorderLayout.CENTER);
+//        	JSeparator jsp = new JSeparator(JSeparator.HORIZONTAL);
+//        	jpUses.add(jsp, BorderLayout.SOUTH);
+//        	
+//        	gbc_jpUses = (GridBagConstraints) gbcLabel.clone();
+//        	gbc_jpUses.gridy = 0;
+//        	gbc_jpUses.gridwidth = 2; //takes two columns
+//        	gbc_jpUses.insets = new Insets(5, 5, 5, 5);//has slightly bigger insets
+//
+//        }
+
+        //Issued To
+        JLabel jlIssuedTo = new JLabel("Issued To");
+        jlIssuedTo.setFont(new Font(null, Font.BOLD, 11));
+        GridBagConstraints gbc_jlIssuedTo = (GridBagConstraints) gbcLabel.clone();
+        gbc_jlIssuedTo.gridy = 1;
+        gbc_jlIssuedTo.gridwidth = 2; //takes two columns
+        gbc_jlIssuedTo.insets = new Insets(5, 5, 5, 5);//has slightly bigger insets
+
+        // Distinguished Name (DN)
+        String sDN = cert.getSubjectX500Principal().getName(RFC2253);
+        ParsedDistinguishedName parsedDN = dnParser.parseDN(sDN);       
+        // Extract the CN, O, OU and EMAILADDRESS fields
+        String sCN = parsedDN.getCN();
+        String sOrg = parsedDN.getO();
+        String sOU = parsedDN.getOU();
+        //String sEMAILADDRESS = CMX509Util.getEmilAddress();
+
+        // Common Name (CN)
+        JLabel jlCN = new JLabel("Common Name (CN)");
+        jlCN.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlCN = (GridBagConstraints) gbcLabel.clone();
+        gbc_jlCN.gridy = 2;
+        JLabel jlCNValue = new JLabel(sCN);
+        jlCNValue.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlCNValue = (GridBagConstraints) gbcValue.clone();
+        gbc_jlCNValue.gridy = 2;
+
+        // Organisation (O)
+        JLabel jlOrg = new JLabel("Organisation (O)");
+        jlOrg.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlOrg = (GridBagConstraints) gbcLabel.clone();
+        gbc_jlOrg.gridy = 3;
+        JLabel jlOrgValue = new JLabel(sOrg);
+        jlOrgValue.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlOrgValue = (GridBagConstraints) gbcValue.clone();
+        gbc_jlOrgValue.gridy = 3;
+
+        // Organisation Unit (OU)
+        JLabel jlOU = new JLabel("Organisation Unit (OU)");
+        jlOU.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlOU = (GridBagConstraints) gbcLabel.clone();
+        gbc_jlOU.gridy = 4;
+        JLabel jlOUValue = new JLabel(sOU);
+        jlOUValue.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlOUValue = (GridBagConstraints) gbcValue.clone();
+        gbc_jlOUValue.gridy = 4;
+
+        // E-mail Address
+        //JLabel jlEmail = new JLabel("E-mail Address");
+        //jlEmail.setFont(new Font(null, PLAIN, 11));
+        //GridBagConstraints gbc_jlEmail = (GridBagConstraints) gbcLabel.clone();
+        //gbc_jlEmail.gridy = 5;
+        //JLabel jlEmailValue = new JLabel(sEMAILADDRESS);
+        //jlEmailValue.setFont(new Font(null, PLAIN, 11));
+        //GridBagConstraints gbc_jlEmailValue = (GridBagConstraints) gbcValue.clone();
+        //gbc_jlEmailValue.gridy = 5;
+
+        // Serial Number
+        JLabel jlSN = new JLabel("Serial Number");
+        jlSN.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlSN = (GridBagConstraints) gbcLabel.clone();
+        gbc_jlSN.gridy = 6;
+        JLabel jlSNValue = new JLabel();
+        // Get the hexadecimal serial number
+        StringBuilder strBuff = new StringBuilder(new BigInteger(1,
+                cert.getSerialNumber().toByteArray()).toString(16).toUpperCase());
+        // Place colons at every two hexadecimal characters
+        if (strBuff.length() > 2)
+            for (int iCnt = 2; iCnt < strBuff.length(); iCnt += 3)
+                strBuff.insert(iCnt, ':');
+        jlSNValue.setText(strBuff.toString());
+        jlSNValue.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlSNValue = (GridBagConstraints) gbcValue.clone();
+        gbc_jlSNValue.gridy = 6;
+
+        // Version
+        JLabel jlVersion = new JLabel("Version");
+        jlVersion.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlVersion = (GridBagConstraints) gbcLabel.clone();
+        gbc_jlVersion.gridy = 7;
+        JLabel jlVersionValue = new JLabel(Integer.toString(cert.getVersion()));
+        jlVersionValue.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlVersionValue = (GridBagConstraints) gbcValue.clone();
+        gbc_jlVersionValue.gridy = 7;
+
+        // Issued By
+        JLabel jlIssuedBy = new JLabel("Issued By");
+        jlIssuedBy.setFont(new Font(null, BOLD, 11));
+        GridBagConstraints gbc_jlIssuedBy = (GridBagConstraints) gbcLabel.clone();
+        gbc_jlIssuedBy.gridy = 8;
+        gbc_jlIssuedBy.gridwidth = 2; //takes two columns 
+        gbc_jlIssuedBy.insets = new Insets(5, 5, 5, 5);//has slightly bigger insets        
+
+        // Distinguished Name (DN)
+		String iDN = cert.getIssuerX500Principal().getName(RFC2253);
+		parsedDN = dnParser.parseDN(iDN);
+		// Extract the CN, O and OU fields
+		String iCN = parsedDN.getCN();
+		String iOrg = parsedDN.getO();
+		String iOU = parsedDN.getOU();
+
+		// Common Name (CN)
+		JLabel jlICN = new JLabel("Common Name (CN)");
+		jlICN.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_jlICN = (GridBagConstraints) gbcLabel.clone();
+		gbc_jlICN.gridy = 9;
+		JLabel jlICNValue = new JLabel(iCN);
+		jlICNValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_jlICNValue = (GridBagConstraints) gbcValue
+				.clone();
+		gbc_jlICNValue.gridy = 9;
+
+		// Organisation (O)
+		JLabel jlIOrg = new JLabel("Organisation (O)");
+		jlIOrg.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_jlIOrg = (GridBagConstraints) gbcLabel.clone();
+		gbc_jlIOrg.gridy = 10;
+		JLabel jlIOrgValue = new JLabel(iOrg);
+		jlIOrgValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_jlIOrgValue = (GridBagConstraints) gbcValue
+				.clone();
+		gbc_jlIOrgValue.gridy = 10;
+
+		// Organisation Unit (OU)
+		JLabel jlIOU = new JLabel("Organisation Unit (OU)");
+		jlIOU.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_jlIOU = (GridBagConstraints) gbcLabel.clone();
+		gbc_jlIOU.gridy = 11;
+		JLabel jlIOUValue = new JLabel(iOU);
+		jlIOUValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_jlIOUValue = (GridBagConstraints) gbcValue
+				.clone();
+		gbc_jlIOUValue.gridy = 11;
+
+		// Validity
+		JLabel jlValidity = new JLabel("Validity");
+		jlValidity.setFont(new Font(null, BOLD, 11));
+		GridBagConstraints gbc_jlValidity = (GridBagConstraints) gbcLabel
+				.clone();
+		gbc_jlValidity.gridy = 12;
+		gbc_jlValidity.gridwidth = 2; // takes two columns
+		gbc_jlValidity.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets
+
+		// Issued On
+		JLabel jlIssuedOn = new JLabel("Issued On");
+		jlIssuedOn.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_jlIssuedOn = (GridBagConstraints) gbcLabel
+				.clone();
+		gbc_jlIssuedOn.gridy = 13;
+		JLabel jlIssuedOnValue = new JLabel(cert.getNotBefore().toString());
+		jlIssuedOnValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_jlIssuedOnValue = (GridBagConstraints) gbcValue
+				.clone();
+		gbc_jlIssuedOnValue.gridy = 13;
+
+		// Expires On
+		JLabel jlExpiresOn = new JLabel("Expires On");
+		jlExpiresOn.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_jlExpiresOn = (GridBagConstraints) gbcLabel
+				.clone();
+		gbc_jlExpiresOn.gridy = 14;
+		JLabel jlExpiresOnValue = new JLabel(cert.getNotAfter().toString());
+		jlExpiresOnValue.setFont(new Font(null, PLAIN, 11));
+		GridBagConstraints gbc_jlExpiresOnValue = (GridBagConstraints) gbcValue
+				.clone();
+		gbc_jlExpiresOnValue.gridy = 14;
+
+		// Fingerprints
+		byte[] certBinaryEncoding;
+		try {
+			certBinaryEncoding = cert.getEncoded();
+		} catch (CertificateEncodingException ex) {
+			throw new CMException(
+					"Could not get the encoded form of the certificate.", ex);
+		}
+        JLabel jlFingerprints = new JLabel("Fingerprints");
+        jlFingerprints.setFont(new Font(null, BOLD, 11));
+        GridBagConstraints gbc_jlFingerprints = (GridBagConstraints) gbcLabel.clone();
+        gbc_jlFingerprints.gridy = 15;
+        gbc_jlFingerprints.gridwidth = 2; //takes two columns  
+        gbc_jlFingerprints.insets = new Insets(5, 5, 5, 5);//has slightly bigger insets
+
+        // SHA-1 Fingerprint
+        JLabel jlSHA1Fingerprint = new JLabel("SHA1 Fingerprint");
+        jlSHA1Fingerprint.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlSHA1Fingerprint = (GridBagConstraints) gbcLabel.clone();
+        gbc_jlSHA1Fingerprint.gridy = 16;
+        JLabel jlSHA1FingerprintValue = new JLabel(dnParser.getMessageDigestAsFormattedString(certBinaryEncoding, "SHA1"));
+        jlSHA1FingerprintValue.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlSHA1FingerprintValue = (GridBagConstraints) gbcValue.clone();
+        gbc_jlSHA1FingerprintValue.gridy = 16;
+
+        // MD5 Fingerprint
+        JLabel jlMD5Fingerprint = new JLabel("MD5 Fingerprint");
+        jlMD5Fingerprint.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlMD5Fingerprint = (GridBagConstraints) gbcLabel.clone();
+        gbc_jlMD5Fingerprint.gridy = 17;
+        JLabel jlMD5FingerprintValue = new JLabel(dnParser.getMessageDigestAsFormattedString(certBinaryEncoding, "MD5"));
+        jlMD5FingerprintValue.setFont(new Font(null, PLAIN, 11));
+        GridBagConstraints gbc_jlMD5FingerprintValue = (GridBagConstraints) gbcValue.clone();
+        gbc_jlMD5FingerprintValue.gridy = 17;
+        
+		/*
+		 * Empty label to add a bit space at the bottom of the panel to make it
+		 * look like firefox's view certificate dialog
+		 */
+        JLabel jlEmpty = new JLabel("");
+		GridBagConstraints gbc_jlEmpty = (GridBagConstraints) gbcLabel.clone();
+		gbc_jlEmpty.gridy = 18;
+		gbc_jlEmpty.gridwidth = 2; // takes two columns
+		gbc_jlEmpty.ipady = 40;
+
+		JPanel jpCertificate = new JPanel(new GridBagLayout());
+		jpCertificate.setBorder(new CompoundBorder(new EmptyBorder(15, 15, 15,
+				15), new EtchedBorder()));
+
+//        if (intendedUses != null){
+//        	jpCertificate.add(jpUses, gbc_jpUses);
+//        }
+        jpCertificate.add(jlIssuedTo, gbc_jlIssuedTo); // Issued To
+        jpCertificate.add(jlCN, gbc_jlCN);
+        jpCertificate.add(jlCNValue, gbc_jlCNValue);
+        jpCertificate.add(jlOrg, gbc_jlOrg);
+        jpCertificate.add(jlOrgValue, gbc_jlOrgValue);        
+        jpCertificate.add(jlOU, gbc_jlOU);
+        jpCertificate.add(jlOUValue, gbc_jlOUValue);
+        //jpCertificate.add(jlEmail, gbc_jlEmail);
+        //jpCertificate.add(jlEmailValue, gbc_jlEmailValue);
+        jpCertificate.add(jlSN, gbc_jlSN);
+        jpCertificate.add(jlSNValue, gbc_jlSNValue);
+        jpCertificate.add(jlVersion, gbc_jlVersion);
+        jpCertificate.add(jlVersionValue, gbc_jlVersionValue);
+        jpCertificate.add(jlIssuedBy, gbc_jlIssuedBy); //Issued By
+        jpCertificate.add(jlICN, gbc_jlICN);
+        jpCertificate.add(jlICNValue, gbc_jlICNValue);
+        jpCertificate.add(jlIOrg, gbc_jlIOrg);
+        jpCertificate.add(jlIOrgValue, gbc_jlIOrgValue);        
+        jpCertificate.add(jlIOU, gbc_jlIOU);
+        jpCertificate.add(jlIOUValue, gbc_jlIOUValue);
+        jpCertificate.add(jlValidity, gbc_jlValidity); //Validity
+        jpCertificate.add(jlIssuedOn, gbc_jlIssuedOn);
+        jpCertificate.add(jlIssuedOnValue, gbc_jlIssuedOnValue);
+        jpCertificate.add(jlExpiresOn, gbc_jlExpiresOn);
+        jpCertificate.add(jlExpiresOnValue, gbc_jlExpiresOnValue); 
+        jpCertificate.add(jlFingerprints, gbc_jlFingerprints); //Fingerprints
+        jpCertificate.add(jlSHA1Fingerprint, gbc_jlSHA1Fingerprint);
+        jpCertificate.add(jlSHA1FingerprintValue, gbc_jlSHA1FingerprintValue);
+        jpCertificate.add(jlMD5Fingerprint, gbc_jlMD5Fingerprint);
+        jpCertificate.add(jlMD5FingerprintValue, gbc_jlMD5FingerprintValue);
+        jpCertificate.add(jlEmpty, gbc_jlEmpty); //Empty label to get some vertical space on the frame
+
+        // List of serviceURLs
+        JPanel jpURLs  = null; // Panel to hold the URL list
+		if (serviceURLs != null) { //if service serviceURLs are not null (even if empty - show empty list)
+
+        	jpURLs = new JPanel(new BorderLayout());
+        	jpURLs.setBorder(new CompoundBorder(
+                    new EmptyBorder(0, 15, 0, 15), new EtchedBorder()));
+            // Label
+            JLabel jlServiceURLs = new JLabel ("Service URLs this key pair will be used for:");
+            jlServiceURLs.setFont(new Font(null, Font.BOLD, 11));
+            jlServiceURLs.setBorder(new EmptyBorder(5,5,5,5));    
+      
+            // New empty service serviceURLs list
+			DefaultListModel<String> jltModel = new DefaultListModel<>();
+			JList<String> jltServiceURLs = new JList<>(jltModel);
+			for (String url : serviceURLs)
+				jltModel.addElement(url);
+			// don't show more than 5 otherwise the window is too big
+            jltServiceURLs.setVisibleRowCount(5);
+            
+			// Scroll pane for service serviceURLs
+			JScrollPane jspServiceURLs = new JScrollPane(jltServiceURLs,
+					VERTICAL_SCROLLBAR_AS_NEEDED,
+					HORIZONTAL_SCROLLBAR_AS_NEEDED);
+			jspServiceURLs.getViewport().setBackground(
+					jltServiceURLs.getBackground());
+
+			jpURLs.add(jlServiceURLs, NORTH);
+			jpURLs.add(jspServiceURLs, CENTER);
+
+			// Put it on the main content pane
+			getContentPane().add(jpURLs, CENTER);
+		}
+
+		// OK button
+		JPanel jpOK = new JPanel(new FlowLayout(FlowLayout.CENTER));
+
+		final JButton jbOK = new JButton("OK");
+		jbOK.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				okPressed();
+			}
+		});
+
+		jpOK.add(jbOK);
+
+		/*
+		 * Put it all together (panel with URL list is already added, if it was
+		 * not null)
+		 */
+		getContentPane().add(jpCertificate, NORTH);
+		getContentPane().add(jpOK, SOUTH);
+
+		// Resizing wreaks havoc
+		setResizable(false);
+
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeDialog();
+			}
+		});
+
+		getRootPane().setDefaultButton(jbOK);
+
+		pack();
+
+		invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				jbOK.requestFocus();
+			}
+		});
+	}
+
+	private void okPressed() {
+		closeDialog();
+	}
+
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewUsernamePasswordEntryDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewUsernamePasswordEntryDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewUsernamePasswordEntryDialog.java
new file mode 100644
index 0000000..7c92842
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewUsernamePasswordEntryDialog.java
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.WEST;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+/**
+ * Dialog used for viewing service URL, username and password.
+ * 
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class ViewUsernamePasswordEntryDialog extends
+		NonBlockedHelpEnabledDialog {
+	/** Service URL field */
+	private JTextField serviceURLField;
+	/** Username field */
+	private JTextField usernameField;
+	/** Password field */
+	private JTextField passwordField;
+	/** Service URL value */
+	private String serviceURL;
+	/** Service username value */
+	private String username;
+	/** Service password value */
+	private String password;
+
+	public ViewUsernamePasswordEntryDialog(JFrame parent, String currentURL,
+			String currentUsername, String currentPassword) {
+		super(parent, "View username and password for a service", true);
+		serviceURL = currentURL;
+		username = currentUsername;
+		password = currentPassword;
+		initComponents();
+	}
+
+	public ViewUsernamePasswordEntryDialog(JDialog parent, String currentURL,
+			String currentUsername, String currentPassword) {
+		super(parent, "View username and password for a service", true);
+		serviceURL = currentURL;
+		username = currentUsername;
+		password = currentPassword;
+		initComponents();
+	}
+
+	private void initComponents() {
+		getContentPane().setLayout(new BorderLayout());
+
+		JLabel serviceURLLabel = new JLabel("Service URL");
+		serviceURLLabel.setBorder(new EmptyBorder(0, 5, 0, 0));
+		JLabel usernameLabel = new JLabel("Username");
+		usernameLabel.setBorder(new EmptyBorder(0, 5, 0, 0));
+		JLabel passwordLabel = new JLabel("Password");
+		passwordLabel.setBorder(new EmptyBorder(0, 5, 0, 0));
+
+		// Populate the fields with values and disable user input
+		serviceURLField = new JTextField();
+		serviceURLField.setText(serviceURL);
+		serviceURLField.setEditable(false);
+
+		usernameField = new JTextField(15);
+		usernameField.setText(username);
+		usernameField.setEditable(false);
+
+		passwordField = new JTextField(15);
+		passwordField.setText(password);
+		passwordField.setEditable(false);
+
+		JButton okButton = new JButton("OK");
+		okButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				closeDialog();
+			}
+		});
+
+		JPanel fieldsPanel = new JPanel(new GridBagLayout());
+
+		GridBagConstraints gbc = new GridBagConstraints();
+		gbc.weighty = 0.0;
+
+		gbc.weightx = 0.0;
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.fill = NONE;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 0);
+		fieldsPanel.add(serviceURLLabel, gbc);
+
+		gbc.weightx = 1.0;
+		gbc.gridx = 1;
+		gbc.gridy = 0;
+		gbc.fill = HORIZONTAL;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 5);
+		fieldsPanel.add(serviceURLField, gbc);
+
+		gbc.weightx = 0.0;
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.fill = NONE;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 0);
+		fieldsPanel.add(usernameLabel, gbc);
+
+		gbc.weightx = 1.0;
+		gbc.gridx = 1;
+		gbc.gridy = 1;
+		gbc.fill = HORIZONTAL;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 5);
+		fieldsPanel.add(usernameField, gbc);
+
+		gbc.weightx = 0.0;
+		gbc.gridx = 0;
+		gbc.gridy = 2;
+		gbc.fill = NONE;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 0);
+		fieldsPanel.add(passwordLabel, gbc);
+
+		gbc.weightx = 1.0;
+		gbc.gridx = 1;
+		gbc.gridy = 2;
+		gbc.fill = HORIZONTAL;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 5);
+		fieldsPanel.add(passwordField, gbc);
+
+		fieldsPanel.setBorder(new CompoundBorder(
+				new EmptyBorder(10, 10, 10, 10), new EtchedBorder()));
+
+		JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		buttonsPanel.add(okButton);
+
+		getContentPane().add(fieldsPanel, CENTER);
+		getContentPane().add(buttonsPanel, SOUTH);
+
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeDialog();
+			}
+		});
+
+		// setResizable(false);
+		getRootPane().setDefaultButton(okButton);
+		pack();
+	}
+
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/WarnUserAboutJCEPolicyDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/WarnUserAboutJCEPolicyDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/WarnUserAboutJCEPolicyDialog.java
new file mode 100644
index 0000000..c826c8f
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/WarnUserAboutJCEPolicyDialog.java
@@ -0,0 +1,223 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.Desktop.getDesktop;
+import static javax.swing.border.EtchedBorder.LOWERED;
+import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED;
+import static org.apache.commons.io.FileUtils.touch;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.GraphicsEnvironment;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.IOException;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JEditorPane;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import javax.swing.text.Document;
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.StyleSheet;
+
+import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser;
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+/**
+ * Dialog that warns user that they need to install unlimited cryptography
+ * strength policy for Java.
+ * 
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class WarnUserAboutJCEPolicyDialog extends NonBlockedHelpEnabledDialog {
+	private static final Logger logger = Logger
+			.getLogger(WarnUserAboutJCEPolicyDialog.class);
+
+	private JCheckBox doNotWarnMeAgainCheckBox;
+	private final ApplicationConfiguration applicationConfiguration;
+	private final DistinguishedNameParser dnParser;
+
+	public WarnUserAboutJCEPolicyDialog(
+			ApplicationConfiguration applicationConfiguration,
+			DistinguishedNameParser dnParser) {
+		super((Frame) null,
+				"Java Unlimited Strength Cryptography Policy Warning", true);
+		this.applicationConfiguration = applicationConfiguration;
+		this.dnParser = dnParser;
+		initComponents();
+	}
+
+	// For testing
+	public static void main(String[] args) {
+		WarnUserAboutJCEPolicyDialog dialog = new WarnUserAboutJCEPolicyDialog(
+				null, null);
+		dialog.setVisible(true);
+	}
+
+	private void initComponents() {
+		// Base font for all components on the form
+		Font baseFont = new JLabel("base font").getFont().deriveFont(11f);
+
+		// Message saying that updates are available
+		JPanel messagePanel = new JPanel(new BorderLayout());
+		messagePanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10,
+				10), new EtchedBorder(LOWERED)));
+
+		JEditorPane message = new JEditorPane();
+		message.setEditable(false);
+		message.setBackground(this.getBackground());
+		message.setFocusable(false);
+		HTMLEditorKit kit = new HTMLEditorKit();
+		message.setEditorKit(kit);
+		StyleSheet styleSheet = kit.getStyleSheet();
+		//styleSheet.addRule("body {font-family:"+baseFont.getFamily()+"; font-size:"+baseFont.getSize()+";}"); // base font looks bigger when rendered as HTML
+		styleSheet.addRule("body {font-family:" + baseFont.getFamily()
+				+ "; font-size:10px;}");
+		Document doc = kit.createDefaultDocument();
+		message.setDocument(doc);
+		message.setText("<html><body>In order for Taverna's security features to function properly - you need to install<br>"
+				+ "'Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy'. <br><br>"
+				+ "If you do not already have it, for <b>Java 6</b> you can get it from:<br>"
+				+ "<a href=\"http://www.oracle.com/technetwork/java/javase/downloads/index.html\">http://www.oracle.com/technetwork/java/javase/downloads/index.html</a><br<br>"
+				+ "Installation instructions are contained in the bundle you download."
+				+ "</body><html>");
+		message.addHyperlinkListener(new HyperlinkListener() {
+			@Override
+			public void hyperlinkUpdate(HyperlinkEvent he) {
+				HyperlinkEvent.EventType type = he.getEventType();
+				if (type == ACTIVATED)
+					// Open a Web browser
+					try {
+						getDesktop().browse(he.getURL().toURI());
+//						BrowserLauncher launcher = new BrowserLauncher();
+//						launcher.openURLinBrowser(he.getURL().toString());
+					} catch (Exception ex) {
+						logger.error("Failed to launch browser to fetch JCE "
+								+ he.getURL());
+					}
+			}
+		});
+		message.setBorder(new EmptyBorder(5, 5, 5, 5));
+		messagePanel.add(message, CENTER);
+
+		doNotWarnMeAgainCheckBox = new JCheckBox("Do not warn me again");
+		doNotWarnMeAgainCheckBox.setFont(baseFont.deriveFont(12f));
+		messagePanel.add(doNotWarnMeAgainCheckBox, SOUTH);
+
+		// Buttons
+		JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		JButton okButton = new JButton("OK");
+		okButton.setFont(baseFont);
+		okButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				okPressed();
+			}
+		});
+
+		buttonsPanel.add(okButton);
+
+		getContentPane().setLayout(new BorderLayout());
+		getContentPane().add(messagePanel, CENTER);
+		getContentPane().add(buttonsPanel, SOUTH);
+
+		pack();
+		setResizable(false);
+		// Center the dialog on the screen (we do not have the parent)
+		Dimension dimension = getToolkit().getScreenSize();
+		Rectangle abounds = getBounds();
+		setLocation((dimension.width - abounds.width) / 2,
+				(dimension.height - abounds.height) / 2);
+		setSize(getPreferredSize());
+	}
+
+	private static final String DO_NOT_WARN_ABOUT_JCE_POLICY = "do_not_warn_about_JCE_policy";
+	public static boolean warnedUser = false; // have we already warned user for
+												// this run
+
+	/**
+	 * Warn user that they need to install Java Cryptography Extension (JCE)
+	 * Unlimited Strength Jurisdiction Policy if they want Credential Manager to
+	 * function properly.
+	 */
+	public static void warnUserAboutJCEPolicy(
+			ApplicationConfiguration applicationConfiguration,
+			DistinguishedNameParser dnParser) {
+		/*
+		 * Do not pop up a dialog if we are running headlessly. If we have
+		 * warned the user and they do not want us to remind them again - exit.
+		 */
+		if (warnedUser || GraphicsEnvironment.isHeadless()
+				|| doNotWarnFile(applicationConfiguration, dnParser).exists())
+			return;
+
+		WarnUserAboutJCEPolicyDialog warnDialog = new WarnUserAboutJCEPolicyDialog(
+				applicationConfiguration, dnParser);
+		warnDialog.setVisible(true);
+		warnedUser = true;
+	}
+
+	private static File doNotWarnFile(
+			ApplicationConfiguration applicationConfiguration,
+			DistinguishedNameParser dnParser) {
+		return new File(
+				dnParser.getCredentialManagerDefaultDirectory(applicationConfiguration),
+				DO_NOT_WARN_ABOUT_JCE_POLICY);
+	}
+
+	protected void okPressed() {
+		try {
+			if (doNotWarnMeAgainCheckBox.isSelected())
+				touch(doNotWarnFile(applicationConfiguration, dnParser));
+		} catch (IOException e) {
+			logger.error(
+					"Failed to touch the 'Do not want me about JCE unilimited security policy' file.",
+					e);
+		}
+		closeDialog();
+	}
+
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/action/CredentialManagerAction.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/action/CredentialManagerAction.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/action/CredentialManagerAction.java
new file mode 100644
index 0000000..c86ad27
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/action/CredentialManagerAction.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager.action;
+
+import static javax.swing.SwingUtilities.invokeLater;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.ImageIcon;
+
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser;
+import net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI;
+
+//import javax.swing.SwingUtilities;
+
+@SuppressWarnings("serial")
+public class CredentialManagerAction extends AbstractAction {
+	private static ImageIcon ICON = new ImageIcon(
+			CredentialManagerAction.class
+					.getResource("/images/cred_manager16x16.png"));
+
+	private CredentialManagerUI cmUI;
+	private final CredentialManager credentialManager;
+	private final DistinguishedNameParser dnParser;
+
+	public CredentialManagerAction(CredentialManager credentialManager,
+			DistinguishedNameParser dnParser) {
+		super("Credential Manager", ICON);
+		this.credentialManager = credentialManager;
+		this.dnParser = dnParser;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		if (cmUI != null) {
+			cmUI.setVisible(true);
+			return;
+		}
+
+		invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				cmUI = new CredentialManagerUI(credentialManager, dnParser);
+				cmUI.setVisible(true);
+			}
+		});
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/menu/CredentialManagerMenu.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/menu/CredentialManagerMenu.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/menu/CredentialManagerMenu.java
new file mode 100644
index 0000000..1eaf82b
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/menu/CredentialManagerMenu.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager.menu;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+//import org.apache.log4j.Logger;
+
+//import net.sf.taverna.t2.security.credentialmanager.CMException;
+//import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser;
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.ui.credentialmanager.action.CredentialManagerAction;
+
+public class CredentialManagerMenu extends AbstractMenuAction {
+	private static final String MENU_URI = "http://taverna.sf.net/2008/t2workbench/menu#advanced";
+
+	private CredentialManager credentialManager;
+	private DistinguishedNameParser dnParser;
+
+	// private static Logger logger = Logger.getLogger(CredentialManagerMenu.class);
+
+	public CredentialManagerMenu() {
+		super(URI.create(MENU_URI), 60);
+		/* This is now done in the initialise SSL startup hook - no need to do it here.
+		// Force initialisation at startup
+		try {
+			CredentialManager.getInstance();
+		} catch (CMException e) {
+			logger.error("Could not initialise SSL properties for SSL connections from Taverna.", e);
+		}
+		*/
+	}
+
+	@Override
+	protected Action createAction() {
+		return new CredentialManagerAction(credentialManager, dnParser);
+	}
+
+	public void setCredentialManager(CredentialManager credentialManager) {
+		this.credentialManager = credentialManager;
+	}
+
+	/**
+	 * @param dnParser
+	 *            the dnParser to set
+	 */
+	public void setDistinguishedNameParser(DistinguishedNameParser dnParser) {
+		this.dnParser = dnParser;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserJavaTruststorePasswordProvider.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserJavaTruststorePasswordProvider.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserJavaTruststorePasswordProvider.java
new file mode 100644
index 0000000..9ddd9a7
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserJavaTruststorePasswordProvider.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (C) 2008-2010 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.ui.credentialmanager.password;
+
+import net.sf.taverna.t2.security.credentialmanager.JavaTruststorePasswordProvider;
+
+/**
+ * An implementation of the {@link JavaTruststorePasswordProvider} that pops up a
+ * dialog and asks the user to provide the password.
+ * 
+ * @author Alex Nenadic
+ *
+ */
+public class AskUserJavaTruststorePasswordProvider implements JavaTruststorePasswordProvider{
+
+	@Override
+	public String getJavaTruststorePassword() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public void setJavaTruststorePassword(String password) {
+		// TODO Auto-generated method stub
+		
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserMasterPasswordProvider.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserMasterPasswordProvider.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserMasterPasswordProvider.java
new file mode 100644
index 0000000..55b7d5b
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserMasterPasswordProvider.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (C) 2008-2010 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.ui.credentialmanager.password;
+
+import net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider;
+
+public class AskUserMasterPasswordProvider implements MasterPasswordProvider{
+
+//	@Override
+//	public boolean canProvideMasterPassword() {
+//		// TODO Auto-generated method stub
+//		return false;
+//	}
+	private int priority = 100;
+
+	@Override
+	public String getMasterPassword(boolean firstTime) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public int getProviderPriority() {
+		return priority;
+	}
+
+	@Override
+	public void setMasterPassword(String password) {
+		// TODO Auto-generated method stub	
+	}
+	
+//	@Override
+//	public void setProviderPriority(int priority) {
+//		this.priority = priority;
+//	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserServiceUsernameAndPasswordProvider.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserServiceUsernameAndPasswordProvider.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserServiceUsernameAndPasswordProvider.java
new file mode 100644
index 0000000..b43d184
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserServiceUsernameAndPasswordProvider.java
@@ -0,0 +1,23 @@
+package net.sf.taverna.t2.workbench.ui.credentialmanager.password;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.security.credentialmanager.ServiceUsernameAndPasswordProvider;
+import net.sf.taverna.t2.security.credentialmanager.UsernamePassword;
+
+public class AskUserServiceUsernameAndPasswordProvider implements ServiceUsernameAndPasswordProvider{
+
+	@Override
+	public UsernamePassword getServiceUsernameAndPassword(URI serviceURI, String requestMessage) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public void setServiceUsernameAndPassword(URI serviceURI,
+			UsernamePassword usernamePassword) {
+		// TODO Auto-generated method stub
+		
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserTrustConfirmationProvider.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserTrustConfirmationProvider.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserTrustConfirmationProvider.java
new file mode 100644
index 0000000..824764d
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserTrustConfirmationProvider.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (C) 2008-2010 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.ui.credentialmanager.password;
+
+import java.security.cert.X509Certificate;
+
+import net.sf.taverna.t2.security.credentialmanager.TrustConfirmationProvider;
+
+public class AskUserTrustConfirmationProvider implements TrustConfirmationProvider {
+
+	@Override
+	public Boolean shouldTrustCertificate(X509Certificate[] chain) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/GetPasswordDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/GetPasswordDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/GetPasswordDialog.java
new file mode 100644
index 0000000..851e900
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/GetPasswordDialog.java
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager.password;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.FlowLayout.LEFT;
+import static java.awt.FlowLayout.RIGHT;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+/**
+ * Dialog for entering user's username and password.
+ * 
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class GetPasswordDialog extends NonBlockedHelpEnabledDialog {
+	/**
+	 * Whether we should ask user to save their username and password using
+	 * Credential Manager
+	 */
+	private boolean shouldAskUserToSave;
+	/** Username field */
+	private JTextField usernameField;
+	/** Password field */
+	private JPasswordField passwordField;
+	/**
+	 * Whether user wished to save the username and password using Credential
+	 * Manager
+	 */
+	private JCheckBox saveCheckBox;
+	/** The entered username */
+	private String username;
+	/** The entered password */
+	private String password;
+	/** Instructions to the user */
+	private String instructions;
+
+	public GetPasswordDialog(String instructions, boolean shouldAskUserToSave) {
+		super((Frame) null, "Enter username and password", true);
+		this.instructions = instructions;
+		this.shouldAskUserToSave = shouldAskUserToSave;
+		initComponents();
+	}
+
+	private void initComponents() {
+		getContentPane().setLayout(new BorderLayout());
+
+		JLabel instructionsLabel = new JLabel(instructions);
+		instructionsLabel.setBorder(new EmptyBorder(5, 5, 5, 5));
+		JPanel jpInstructions = new JPanel(new FlowLayout(LEFT));
+		jpInstructions.add(instructionsLabel);
+
+		JLabel usernameLabel = new JLabel("Username");
+		usernameLabel.setBorder(new EmptyBorder(5, 5, 5, 5));
+		JLabel passwordLabel = new JLabel("Password");
+		passwordLabel.setBorder(new EmptyBorder(5, 5, 5, 5));
+
+		usernameField = new JTextField(15);
+		passwordField = new JPasswordField(15);
+
+		JButton okButton = new JButton("OK");
+		okButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				okPressed();
+			}
+		});
+
+		JButton cancelButton = new JButton("Cancel");
+		cancelButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				cancelPressed();
+			}
+		});
+
+        // Central panel with username/password fields and a "Do you want to Save?" checkbox
+		JPanel mainPanel = new JPanel(new BorderLayout());
+
+		JPanel passwordPanel = new JPanel(new GridLayout(2, 2, 5, 5));
+		passwordPanel.add(usernameLabel);
+		passwordPanel.add(usernameField);
+		passwordPanel.add(passwordLabel);
+		passwordPanel.add(passwordField);
+		mainPanel.add(passwordPanel, CENTER);
+
+		// If user wants to save this username and password
+		saveCheckBox = new JCheckBox();
+		saveCheckBox.setBorder(new EmptyBorder(5, 5, 5, 5));
+		saveCheckBox.setSelected(true);
+		saveCheckBox
+				.setText("Use Credential Manager to save this username and password");
+		if (shouldAskUserToSave) {
+			JPanel jpSaveCheckBox = new JPanel(new FlowLayout(LEFT));
+			jpSaveCheckBox.add(saveCheckBox);
+			mainPanel.add(jpSaveCheckBox, SOUTH);
+		}
+
+		passwordPanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10,
+				10), new EtchedBorder()));
+
+		JPanel buttonsPanel = new JPanel(new FlowLayout(RIGHT));
+		buttonsPanel.add(okButton);
+		buttonsPanel.add(cancelButton);
+
+		passwordPanel.setMinimumSize(new Dimension(300, 100));
+
+		getContentPane().add(jpInstructions, NORTH);
+		getContentPane().add(mainPanel, CENTER);
+		getContentPane().add(buttonsPanel, SOUTH);
+
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeDialog();
+			}
+		});
+
+		setResizable(false);
+		getRootPane().setDefaultButton(okButton);
+		pack();
+	}
+
+	public String getUsername() {
+		return username;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	/**
+	 * Check if user wishes to save username and pasword using the Credential
+	 * Manager.
+	 */
+	public boolean shouldSaveUsernameAndPassword() {
+		return saveCheckBox.isSelected();
+	}
+
+	private boolean checkControls() {
+		username = usernameField.getText();
+		if (username.length() == 0) {
+			showMessageDialog(this, "Username cannot be empty", "Warning",
+					WARNING_MESSAGE);
+			return false;
+		}
+
+		password = new String(passwordField.getPassword());
+		if (password.length() == 0) { // password empty
+			showMessageDialog(this, "Password cannot be empty", "Warning",
+					WARNING_MESSAGE);
+
+			return false;
+		}
+
+		return true;
+	}
+
+	private void okPressed() {
+		if (checkControls())
+			closeDialog();
+	}
+
+	private void cancelPressed() {
+		// Set all fields to null to indicate that cancel button was pressed
+		username = null;
+		password = null;
+		closeDialog();
+	}
+
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+
+	public void setUsername(String username) {
+		this.username = username;
+		usernameField.setText(username);
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+		passwordField.setText(password);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/SimpleMasterPasswordProvider.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/SimpleMasterPasswordProvider.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/SimpleMasterPasswordProvider.java
new file mode 100644
index 0000000..4b9950e
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/SimpleMasterPasswordProvider.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (C) 2008-2010 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.ui.credentialmanager.password;
+
+import net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider;
+
+/**
+ * A simple implementation of {@link MasterPasswordProvider} that just provides
+ * a master password that can be obtained and set from outside the provider.
+ * 
+ * @author Alex Nenadic
+ */
+public class SimpleMasterPasswordProvider implements MasterPasswordProvider {
+	private String masterPassword;
+	private int priority = 200;
+	
+	@Override
+	public String getMasterPassword(boolean firstTime) {
+		return masterPassword;
+	}
+	
+	@Override
+	public void setMasterPassword(String masterPassword){
+		this.masterPassword = masterPassword;
+	}
+
+	@Override
+	public int getProviderPriority() {
+		return priority;
+	}
+
+//	@Override
+//	public void setProviderPriority(int priority) {
+//		this.priority = priority;		
+//	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIMasterPasswordProvider.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIMasterPasswordProvider.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIMasterPasswordProvider.java
new file mode 100644
index 0000000..81b2e5c
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIMasterPasswordProvider.java
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (C) 2009-2010 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.ui.credentialmanager.password;
+
+import java.awt.GraphicsEnvironment;
+
+import javax.swing.JFrame;
+import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+import net.sf.taverna.t2.security.credentialmanager.JavaTruststorePasswordProvider;
+import net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider;
+import net.sf.taverna.t2.workbench.ui.credentialmanager.GetMasterPasswordDialog;
+import net.sf.taverna.t2.workbench.ui.credentialmanager.SetMasterPasswordDialog;
+import net.sf.taverna.t2.workbench.ui.credentialmanager.WarnUserAboutJCEPolicyDialog;
+
+/**
+ * A UI pop-up that asks user for a master password for Credential Manager.
+ *
+ * @author Alex Nenadic
+ * @author Stian Soiland-Reyes
+ *
+ */
+public class UIMasterPasswordProvider implements MasterPasswordProvider, JavaTruststorePasswordProvider {
+
+	private ApplicationConfiguration applicationConfiguration;
+
+        private DistinguishedNameParser dnParser;
+        
+	@Override
+	public String getJavaTruststorePassword() {
+		if (GraphicsEnvironment.isHeadless()) {
+			return null;
+		}
+
+		GetMasterPasswordDialog getPasswordDialog = new GetMasterPasswordDialog(
+				"Credential Manager needs to copy certificates from Java truststore. "
+						+ "Please enter your password.");
+		getPasswordDialog.setLocationRelativeTo(null);
+		getPasswordDialog.setVisible(true);
+		String javaTruststorePassword = getPasswordDialog.getPassword();
+		return javaTruststorePassword;
+	}
+
+	@Override
+	public void setJavaTruststorePassword(String password) {
+	}
+
+	@Override
+	public String getMasterPassword(boolean firstTime) {
+
+		// Check if this Taverna run is headless (i.e. Taverna Server or Taverna
+		// from command line) - do not do anything here if it is as we do not
+		// want
+		// any windows popping up even if they could
+		if (GraphicsEnvironment.isHeadless()) {
+			return null;
+		}
+
+		// Pop up a warning about Java Cryptography Extension (JCE)
+		// Unlimited Strength Jurisdiction Policy
+		WarnUserAboutJCEPolicyDialog.warnUserAboutJCEPolicy(applicationConfiguration, dnParser);
+
+		if (firstTime) {
+			// Ask user to set the master password for Credential Manager (only
+			// the first time)
+			SetMasterPasswordDialog setPasswordDialog = new SetMasterPasswordDialog(
+					(JFrame) null, "Set master password", true,
+					"Set master password for Credential Manager");
+			setPasswordDialog.setLocationRelativeTo(null);
+			setPasswordDialog.setVisible(true);
+			return setPasswordDialog.getPassword();
+		} else {
+			// Ask user to provide a master password for Credential Manager
+			GetMasterPasswordDialog getPasswordDialog = new GetMasterPasswordDialog(
+			"Enter master password for Credential Manager");
+			getPasswordDialog.setLocationRelativeTo(null);
+			getPasswordDialog.setVisible(true);
+			return getPasswordDialog.getPassword();
+		}
+	}
+
+	@Override
+	public void setMasterPassword(String password) {
+	}
+
+	@Override
+	public int getProviderPriority() {
+		return 100;
+	}
+
+	/**
+	 * Sets the applicationConfiguration.
+	 *
+	 * @param applicationConfiguration the new value of applicationConfiguration
+	 */
+	public void setApplicationConfiguration(ApplicationConfiguration applicationConfiguration) {
+		this.applicationConfiguration = applicationConfiguration;
+	}
+        
+        /**
+	 * @param dnParser the dnParser to set
+	 */
+	public void setDistinguishedNameParser(DistinguishedNameParser dnParser) {
+		this.dnParser = dnParser;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIUsernamePasswordProvider.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIUsernamePasswordProvider.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIUsernamePasswordProvider.java
new file mode 100644
index 0000000..60318d0
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIUsernamePasswordProvider.java
@@ -0,0 +1,92 @@
+package net.sf.taverna.t2.workbench.ui.credentialmanager.password;
+
+import static java.awt.GraphicsEnvironment.isHeadless;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser;
+import net.sf.taverna.t2.security.credentialmanager.ServiceUsernameAndPasswordProvider;
+import net.sf.taverna.t2.security.credentialmanager.UsernamePassword;
+
+import org.apache.log4j.Logger;
+
+public class UIUsernamePasswordProvider implements
+		ServiceUsernameAndPasswordProvider {
+	private static final Logger logger = Logger
+			.getLogger(UIUsernamePasswordProvider.class);
+
+	private DistinguishedNameParser dnParser;
+
+	public boolean canProvideUsernamePassword(URI serviceURI) {
+		return !isHeadless();
+	}
+
+	@Override
+	public UsernamePassword getServiceUsernameAndPassword(URI serviceURI,
+			String requestingPrompt) {
+		URI displayURI = serviceURI;
+
+		try {
+			displayURI = dnParser.setFragmentForURI(displayURI, null);
+			displayURI = dnParser.setUserInfoForURI(displayURI, null);
+		} catch (URISyntaxException e) {
+			logger.warn("Could not strip fragment/userinfo from " + serviceURI,
+					e);
+		}
+
+		StringBuilder message = new StringBuilder();
+		message.append("<html><body>The Taverna Credential Manager could not find a ");
+		message.append("username and password for the service at:");
+		message.append("<br><br><code>");
+		message.append(displayURI);
+		message.append("</code>");
+		if (requestingPrompt != null && !requestingPrompt.isEmpty()) {
+			message.append("<p><i>");
+			message.append(requestingPrompt);
+			message.append("</i>");
+		}
+		message.append("<br><br>Please provide the username and password.</body></html>");
+
+		GetPasswordDialog getPasswordDialog = new GetPasswordDialog(
+				message.toString(), true);
+		getPasswordDialog.setLocationRelativeTo(null);
+		if (serviceURI.getRawUserInfo() != null
+				&& serviceURI.getRawUserInfo().length() > 1) {
+			String userInfo = serviceURI.getRawUserInfo();
+			String[] userPassword = userInfo.split(":", 2);
+			if (userPassword.length == 2) {
+				getPasswordDialog.setUsername(userPassword[0]);
+				getPasswordDialog.setPassword(userPassword[1]);
+			}
+		}
+		getPasswordDialog.setVisible(true);
+
+		String username = getPasswordDialog.getUsername(); // get username
+		String password = getPasswordDialog.getPassword(); // get password
+		boolean shouldSaveUsernameAndPassword = getPasswordDialog
+				.shouldSaveUsernameAndPassword();
+		if (username == null || password == null)
+			// user cancelled - any of the above two variables is null
+			return null;
+
+		UsernamePassword credential = new UsernamePassword();
+		credential.setUsername(username);
+		credential.setPassword(password.toCharArray());
+		credential.setShouldSave(shouldSaveUsernameAndPassword);
+		return credential;
+	}
+
+	@Override
+	public void setServiceUsernameAndPassword(URI serviceURI,
+			UsernamePassword usernamePassword) {
+	}
+
+	/**
+	 * @param dnParser
+	 *            the dnParser to set
+	 */
+	public void setDistinguishedNameParser(DistinguishedNameParser dnParser) {
+		this.dnParser = dnParser;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/InitialiseSSLStartupHook.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/InitialiseSSLStartupHook.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/InitialiseSSLStartupHook.java
new file mode 100644
index 0000000..1e4073c
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/InitialiseSSLStartupHook.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ *  Copyright (C) 2007-2010 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.ui.credentialmanager.startup;
+
+import org.apache.log4j.Logger;
+
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.workbench.StartupSPI;
+
+/**
+ * 
+ * Startup hook to initialise SSL socket factory used by Taverna for creating
+ * HTTPS connections.
+ * 
+ * @author Alex Nenadic
+ * @author Stian Soiland-Reyes
+ */
+public class InitialiseSSLStartupHook implements StartupSPI {
+	private static final Logger logger = Logger
+			.getLogger(InitialiseSSLStartupHook.class);
+
+	private CredentialManager credManager;
+
+	@Override
+	public int positionHint() {
+		return 25;
+	}
+
+	@Override
+	public boolean startup() {
+		logger.info("Initialising SSL socket factory for SSL connections from Taverna.");
+		try {
+			credManager.initializeSSL();
+		} catch (CMException e) {
+			logger.error(
+					"Could not initialise the SSL socket factory (for creating SSL connections)"
+							+ " using Taverna's keystores.", e);
+		}
+		return true;
+	}
+
+	public void setCredentialManager(CredentialManager credManager) {
+		this.credManager = credManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/SetCredManAuthenticatorStartupHook.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/SetCredManAuthenticatorStartupHook.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/SetCredManAuthenticatorStartupHook.java
new file mode 100644
index 0000000..29ec9b6
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/SetCredManAuthenticatorStartupHook.java
@@ -0,0 +1,24 @@
+package net.sf.taverna.t2.workbench.ui.credentialmanager.startup;
+
+import java.net.Authenticator;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.workbench.StartupSPI;
+
+public class SetCredManAuthenticatorStartupHook implements StartupSPI {
+	private CredentialManager credManager;
+
+	@Override
+	public int positionHint() {
+		return 50;
+	}
+
+	@Override
+	public boolean startup() {
+		Authenticator.setDefault(credManager.getAuthenticator());
+		return true;
+	}
+
+	public void setCredentialManager(CredentialManager credManager) {
+		this.credManager = credManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarAction.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarAction.java
new file mode 100644
index 0000000..d515809
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarAction.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager.toolbar;
+
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.toolbar.CredentialManagerToolbarSection.CREDENTIAL_MANAGER_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.ui.credentialmanager.action.CredentialManagerAction;
+
+public class CredentialManagerToolbarAction extends AbstractMenuAction {
+	private static final String ENTRY_URI = "http://taverna.sf.net/2008/t2workbench/toolbar#credentialManagerAction";
+
+	public CredentialManagerToolbarAction() {
+		super(CREDENTIAL_MANAGER_TOOLBAR_SECTION, 100, URI.create(ENTRY_URI));
+	}
+
+	@Override
+	protected Action createAction() {
+		// need to add CredentialManager if toolbar is ever used
+		return new CredentialManagerAction(null, null);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarSection.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarSection.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarSection.java
new file mode 100644
index 0000000..e5367be
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarSection.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager.toolbar;
+
+import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+public class CredentialManagerToolbarSection extends AbstractMenuSection {
+	private static final String ENTRY_URI = "http://taverna.sf.net/2008/t2workbench/toolbar#credentialManagerSection";
+	/** {@value #ENTRY_URI} */
+	public static URI CREDENTIAL_MANAGER_TOOLBAR_SECTION = URI
+			.create(ENTRY_URI);
+
+	public CredentialManagerToolbarSection() {
+		super(DEFAULT_TOOL_BAR, 300, CREDENTIAL_MANAGER_TOOLBAR_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.security.credentialmanager.CredentialProviderSPI
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.security.credentialmanager.CredentialProviderSPI b/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.security.credentialmanager.CredentialProviderSPI
new file mode 100644
index 0000000..725aa11
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.security.credentialmanager.CredentialProviderSPI
@@ -0,0 +1,3 @@
+net.sf.taverna.t2.workbench.ui.credentialmanager.password.UIUsernamePasswordProvider
+net.sf.taverna.t2.workbench.ui.credentialmanager.password.UIMasterPasswordProvider
+net.sf.taverna.t2.workbench.ui.credentialmanager.ConfirmTrustedCertificateUI

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..3743c2f
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,3 @@
+net.sf.taverna.t2.workbench.ui.credentialmanager.menu.CredentialManagerMenu
+#net.sf.taverna.t2.workbench.ui.credentialmanager.toolbar.CredentialManagerToolbarAction
+#net.sf.taverna.t2.workbench.ui.credentialmanager.toolbar.CredentialManagerToolbarSection
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.StartupSPI
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.StartupSPI b/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.StartupSPI
new file mode 100644
index 0000000..b43772c
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.StartupSPI
@@ -0,0 +1,2 @@
+net.sf.taverna.t2.workbench.ui.credentialmanager.startup.InitialiseSSLStartupHook
+net.sf.taverna.t2.workbench.ui.credentialmanager.startup.SetCredManAuthenticatorStartupHook


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 8071aff..c4f10e9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,58 +40,58 @@
     <module>taverna-disabled-activity-ui</module>
     <module>taverna-stringconstant-activity-ui</module>
     <module>taverna-unrecognized-activity-ui</module>
-    <module>taverna-workbench-activity-icons-api</module>
-    <module>taverna-workbench-activity-palette-api</module>
-    <module>taverna-workbench-activity-palette-impl</module>
-    <module>taverna-workbench-activity-palette-ui</module>
-    <module>taverna-workbench-activity-tools</module>
-    <module>taverna-workbench-configuration-api</module>
-    <module>taverna-workbench-configuration-impl</module>
-    <module>taverna-workbench-contextual-views</module>
-    <module>taverna-workbench-contextual-views-api</module>
-    <module>taverna-workbench-contextual-views-impl</module>
-    <module>taverna-workbench-credential-manager-ui</module>
-    <module>taverna-workbench-data-management-config-ui</module>
-    <module>taverna-workbench-design-ui</module>
-    <module>taverna-workbench-edits-api</module>
-    <module>taverna-workbench-edits-impl</module>
-    <module>taverna-workbench-file-api</module>
-    <module>taverna-workbench-file-impl</module>
-    <module>taverna-workbench-graph-model</module>
-    <module>taverna-workbench-graph-view</module>
-    <module>taverna-workbench-helper-api</module>
-    <module>taverna-workbench-httpproxy-config</module>
-    <module>taverna-workbench-iteration-strategy-ui</module>
-    <module>taverna-workbench-loop-ui</module>
-    <module>taverna-workbench-menu-api</module>
-    <module>taverna-workbench-menu-impl</module>
-    <module>taverna-workbench-menu-items</module>
-    <module>taverna-workbench-monitor-view</module>
-    <module>taverna-workbench-parallelize-ui</module>
-    <module>taverna-workbench-perspective-biocatalogue</module>
-    <module>taverna-workbench-perspective-design</module>
-    <module>taverna-workbench-perspective-myexperiment</module>
-    <module>taverna-workbench-perspective-results</module>
-    <module>taverna-workbench-plugin-manager</module>
-    <module>taverna-workbench-plugins-gui</module>
-    <module>taverna-workbench-reference-ui</module>
-    <module>taverna-workbench-renderers-api</module>
-    <module>taverna-workbench-renderers-exts</module>
-    <module>taverna-workbench-renderers-impl</module>
-    <module>taverna-workbench-report-api</module>
-    <module>taverna-workbench-report-explainer</module>
-    <module>taverna-workbench-report-impl</module>
-    <module>taverna-workbench-report-view</module>
-    <module>taverna-workbench-results-view</module>
-    <module>taverna-workbench-retry-ui</module>
-    <module>taverna-workbench-run-ui</module>
-    <module>taverna-workbench-selection-api</module>
-    <module>taverna-workbench-selection-impl</module>
-    <module>taverna-workbench-update-manager</module>
-    <module>taverna-workbench-workbench-api</module>
-    <module>taverna-workbench-workbench-impl</module>
-    <module>taverna-workbench-workflow-explorer</module>
-    <module>taverna-workbench-workflow-view</module>
+    <module>taverna-activity-icons-api</module>
+    <module>taverna-activity-palette-api</module>
+    <module>taverna-activity-palette-impl</module>
+    <module>taverna-activity-palette-ui</module>
+    <module>taverna-activity-tools</module>
+    <module>taverna-configuration-api</module>
+    <module>taverna-configuration-impl</module>
+    <module>taverna-contextual-views</module>
+    <module>taverna-contextual-views-api</module>
+    <module>taverna-contextual-views-impl</module>
+    <module>taverna-credential-manager-ui</module>
+    <module>taverna-data-management-config-ui</module>
+    <module>taverna-design-ui</module>
+    <module>taverna-edits-api</module>
+    <module>taverna-edits-impl</module>
+    <module>taverna-file-api</module>
+    <module>taverna-file-impl</module>
+    <module>taverna-graph-model</module>
+    <module>taverna-graph-view</module>
+    <module>taverna-helper-api</module>
+    <module>taverna-httpproxy-config</module>
+    <module>taverna-iteration-strategy-ui</module>
+    <module>taverna-loop-ui</module>
+    <module>taverna-menu-api</module>
+    <module>taverna-menu-impl</module>
+    <module>taverna-menu-items</module>
+    <module>taverna-monitor-view</module>
+    <module>taverna-parallelize-ui</module>
+    <module>taverna-perspective-biocatalogue</module>
+    <module>taverna-perspective-design</module>
+    <module>taverna-perspective-myexperiment</module>
+    <module>taverna-perspective-results</module>
+    <module>taverna-plugin-manager</module>
+    <module>taverna-plugins-gui</module>
+    <module>taverna-reference-ui</module>
+    <module>taverna-renderers-api</module>
+    <module>taverna-renderers-exts</module>
+    <module>taverna-renderers-impl</module>
+    <module>taverna-report-api</module>
+    <module>taverna-report-explainer</module>
+    <module>taverna-report-impl</module>
+    <module>taverna-report-view</module>
+    <module>taverna-results-view</module>
+    <module>taverna-retry-ui</module>
+    <module>taverna-run-ui</module>
+    <module>taverna-selection-api</module>
+    <module>taverna-selection-impl</module>
+    <module>taverna-update-manager</module>
+    <module>taverna-workbench-api</module>
+    <module>taverna-workbench-impl</module>
+    <module>taverna-workflow-explorer</module>
+    <module>taverna-workflow-view</module>
 		<module>taverna-beaninfo</module>
 		<module>taverna-io</module>
 		<module>taverna-ui</module>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-icons-api/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-activity-icons-api/pom.xml b/taverna-activity-icons-api/pom.xml
new file mode 100644
index 0000000..2bf3ffc
--- /dev/null
+++ b/taverna-activity-icons-api/pom.xml
@@ -0,0 +1,37 @@
+<?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>taverna-activity-icons-api</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Activity icon manager API</name>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+			<version>${taverna.language.version}</version>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconManager.java
----------------------------------------------------------------------
diff --git a/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconManager.java b/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconManager.java
new file mode 100644
index 0000000..d2c8fff
--- /dev/null
+++ b/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconManager.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (C) 2011 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.activityicons;
+
+import java.net.URI;
+
+import javax.swing.Icon;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+
+/**
+ * Manager for activities' icons.
+ * 
+ * @author David Withers
+ */
+public interface ActivityIconManager {
+	/** Returns an icon for the Activity. */
+	Icon iconForActivity(URI activityType);
+
+	Icon iconForActivity(Activity activity);
+
+	void resetIcon(URI activityType);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconSPI.java
----------------------------------------------------------------------
diff --git a/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconSPI.java b/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconSPI.java
new file mode 100644
index 0000000..7270dfc
--- /dev/null
+++ b/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconSPI.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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.activityicons;
+
+import java.net.URI;
+
+import javax.swing.Icon;
+
+/**
+ * Defines an interface for getting an icon for an Activity.
+ * 
+ * @author Alex Nenadic
+ */
+public interface ActivityIconSPI {
+	/**
+	 * A return value for {@link canProvideIconScore()} indicating an SPI cannot
+	 * provide an icon for a given activity.
+	 */
+	int NO_ICON = 0;
+
+	/**
+	 * {@link DefaultActivityIcon} returns this value that will be used when an
+	 * activity that has no other SPI providing an icon for. Any SPI shour
+	 * return value of at least DEFAULT_ICON + 1 if they want to 'override' the
+	 * default icon.
+	 */
+	int DEFAULT_ICON = 10;
+
+	/**
+	 * Returns a positive number if the class can provide an icon for the given
+	 * activity or {@link NO_ICON} otherwise. Out of two SPIs capable of
+	 * providing an icon for the same activity, the one returning a higher score
+	 * will be used.
+	 */
+	int canProvideIconScore(URI activityType);
+
+	/** Returns an icon for the Activity. */
+	Icon getIcon(URI activityType);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/DefaultActivityIcon.java
----------------------------------------------------------------------
diff --git a/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/DefaultActivityIcon.java b/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/DefaultActivityIcon.java
new file mode 100644
index 0000000..c474e69
--- /dev/null
+++ b/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/DefaultActivityIcon.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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.activityicons;
+
+import java.net.URI;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+
+public class DefaultActivityIcon implements ActivityIconSPI {
+	private static final String ICON_RESOURCE = "/default-activity-icon.png";
+
+	@Override
+	public int canProvideIconScore(URI activityType) {
+		// For any activity we can provide a default icon
+		return DEFAULT_ICON;
+	}
+
+	@Override
+	public Icon getIcon(URI activityType) {
+		return getDefaultIcon();
+	}
+	
+	public static Icon getDefaultIcon() {
+		return IconLoader.icon;
+	}
+
+	private static class IconLoader {
+		static final Icon icon = loadDefaultIcon();
+
+		private static Icon loadDefaultIcon() {
+			return new ImageIcon(
+					DefaultActivityIcon.class.getResource(ICON_RESOURCE));
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/impl/ActivityIconManagerImpl.java
----------------------------------------------------------------------
diff --git a/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/impl/ActivityIconManagerImpl.java b/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/impl/ActivityIconManagerImpl.java
new file mode 100644
index 0000000..f8294fc
--- /dev/null
+++ b/taverna-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/impl/ActivityIconManagerImpl.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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.activityicons.impl;
+
+import static net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI.NO_ICON;
+
+import java.net.URI;
+import java.util.List;
+import java.util.WeakHashMap;
+
+import javax.swing.Icon;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager;
+import net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI;
+
+/**
+ * Manager for activities' icons.
+ *
+ * @author Alex Nenadic
+ * @author Alan R Williams
+ */
+public class ActivityIconManagerImpl implements ActivityIconManager {
+	/** Cache of already obtained icons; maps activities to their icons*/
+	private WeakHashMap<URI, Icon> iconsMap = new WeakHashMap<>();
+
+	private List<ActivityIconSPI> activityIcons;
+
+	/** Returns an icon for the Activity. */
+	@Override
+	public Icon iconForActivity(URI activityType) {
+		Icon icon = iconsMap.get(activityType);
+		if (icon != null)
+			return icon;
+		int bestScore = NO_ICON;
+		ActivityIconSPI bestSPI = null;
+		for (ActivityIconSPI spi : activityIcons) {
+			int spiScore = spi.canProvideIconScore(activityType);
+			if (spiScore > bestScore) {
+				bestSPI = spi;
+				bestScore = spiScore;
+			}
+		}
+		if (bestSPI == null)
+			return null;
+		icon = bestSPI.getIcon(activityType);
+		iconsMap.put(activityType, icon);
+		return icon;
+	}
+
+	@Override
+	public Icon iconForActivity(Activity activity) {
+		return iconForActivity(activity.getType());
+	}
+
+	@Override
+	public void resetIcon(URI activityType) {
+		Icon icon = iconsMap.get(activityType);
+		if (icon != null)
+			iconsMap.remove(activityType);
+		iconForActivity(activityType);
+	}
+
+	public void setActivityIcons(List<ActivityIconSPI> activityIcons) {
+		this.activityIcons = activityIcons;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-icons-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI
----------------------------------------------------------------------
diff --git a/taverna-activity-icons-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI b/taverna-activity-icons-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI
new file mode 100644
index 0000000..d268c81
--- /dev/null
+++ b/taverna-activity-icons-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.activityicons.DefaultActivityIcon
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context-osgi.xml b/taverna-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context-osgi.xml
new file mode 100644
index 0000000..5c67640
--- /dev/null
+++ b/taverna-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context-osgi.xml
@@ -0,0 +1,15 @@
+<?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="DefaultActivityIcon" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI" />
+
+	<service ref="ActivityIconManager" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconManager" />
+
+	<list id="activityIcons" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI" cardinality="0..N" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context.xml
----------------------------------------------------------------------
diff --git a/taverna-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context.xml b/taverna-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context.xml
new file mode 100644
index 0000000..93c98c4
--- /dev/null
+++ b/taverna-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context.xml
@@ -0,0 +1,13 @@
+<?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="DefaultActivityIcon" class="net.sf.taverna.t2.workbench.activityicons.DefaultActivityIcon" />
+
+	<bean id="ActivityIconManager" class="net.sf.taverna.t2.workbench.activityicons.impl.ActivityIconManagerImpl">
+		<property name="activityIcons" ref="activityIcons" />
+	</bean>
+
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-icons-api/src/main/resources/default-activity-icon.png
----------------------------------------------------------------------
diff --git a/taverna-activity-icons-api/src/main/resources/default-activity-icon.png b/taverna-activity-icons-api/src/main/resources/default-activity-icon.png
new file mode 100644
index 0000000..b7ed3e9
Binary files /dev/null and b/taverna-activity-icons-api/src/main/resources/default-activity-icon.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/pom.xml b/taverna-activity-palette-api/pom.xml
new file mode 100644
index 0000000..eb21d63
--- /dev/null
+++ b/taverna-activity-palette-api/pom.xml
@@ -0,0 +1,63 @@
+<?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>taverna-activity-palette-api</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Activity Palette API</name>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-edits-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-beans</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${taverna.osgi.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+			<version>${taverna.language.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>${junit.version}</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractConfigurableServiceProvider.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractConfigurableServiceProvider.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractConfigurableServiceProvider.java
new file mode 100644
index 0000000..18cb176
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractConfigurableServiceProvider.java
@@ -0,0 +1,53 @@
+package net.sf.taverna.t2.servicedescriptions;
+
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+
+public abstract class AbstractConfigurableServiceProvider extends
+		IdentifiedObject implements ConfigurableServiceProvider {
+	protected Configuration serviceProviderConfig;
+
+	/**
+	 * Construct configurable service provider.
+	 * 
+	 * @param configTemplate
+	 *            Template configuration
+	 */
+	public AbstractConfigurableServiceProvider(Configuration configTemplate) {
+		if (configTemplate == null)
+			throw new NullPointerException("Default config can't be null");
+		serviceProviderConfig = configTemplate;
+	}
+
+	/**
+	 * Package access constructor - only used with {@link #clone()} - otherwise
+	 * use {@link #AbstractConfigurableServiceProvider(Object)}
+	 */
+	AbstractConfigurableServiceProvider() {
+	}
+
+	@Override
+	public AbstractConfigurableServiceProvider clone() {
+		AbstractConfigurableServiceProvider provider = (AbstractConfigurableServiceProvider) newInstance();
+		Configuration configuration = getConfiguration();
+		if (configuration != null)
+			provider.configure(configuration);
+		return provider;
+	}
+
+	@Override
+	public synchronized void configure(Configuration conf) {
+		if (conf == null)
+			throw new IllegalArgumentException("Config can't be null");
+		this.serviceProviderConfig = conf;
+	}
+
+	@Override
+	public Configuration getConfiguration() {
+		return serviceProviderConfig;
+	}
+
+	@Override
+	public String toString() {
+		return getName() + " " + getConfiguration();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractTemplateService.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractTemplateService.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractTemplateService.java
new file mode 100644
index 0000000..d4909b1
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractTemplateService.java
@@ -0,0 +1,85 @@
+package net.sf.taverna.t2.servicedescriptions;
+
+import static java.util.Collections.singleton;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.Icon;
+
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+
+public abstract class AbstractTemplateService implements
+		ServiceDescriptionProvider {
+	protected TemplateServiceDescription templateService = new TemplateServiceDescription();
+
+	@Override
+	public void findServiceDescriptionsAsync(
+			FindServiceDescriptionsCallBack callBack) {
+		callBack.partialResults(singleton(templateService));
+		callBack.finished();
+	}
+
+	@Override
+	public abstract Icon getIcon();
+
+	public URI getActivityType() {
+		return null;
+	}
+
+	public abstract Configuration getActivityConfiguration();
+
+	public class TemplateServiceDescription extends ServiceDescription {
+		@Override
+		public Icon getIcon() {
+			return AbstractTemplateService.this.getIcon();
+		}
+
+		@Override
+		public String getName() {
+			return AbstractTemplateService.this.getName();
+		}
+
+		@Override
+		public List<String> getPath() {
+			return Arrays.asList(SERVICE_TEMPLATES);
+		}
+
+		@Override
+		public boolean isTemplateService() {
+			return true;
+		}
+
+		@Override
+		protected List<Object> getIdentifyingData() {
+			// Do it by object identity
+			return null;
+		}
+
+		@Override
+		public URI getActivityType() {
+			return AbstractTemplateService.this.getActivityType();
+		}
+
+		@Override
+		public Configuration getActivityConfiguration() {
+			return AbstractTemplateService.this.getActivityConfiguration();
+		}
+
+		@Override
+		public String getDescription() {
+			return AbstractTemplateService.this.getDescription();
+		}
+	}
+
+	@Override
+	public String toString() {
+		return "Template service " + getName();
+	}
+
+	public String getDescription() {
+		// Default to an empty string
+		return "";
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ConfigurableServiceProvider.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ConfigurableServiceProvider.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ConfigurableServiceProvider.java
new file mode 100644
index 0000000..0bf01bd
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ConfigurableServiceProvider.java
@@ -0,0 +1,10 @@
+package net.sf.taverna.t2.servicedescriptions;
+
+import uk.org.taverna.scufl2.api.common.Configurable;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+
+public interface ConfigurableServiceProvider extends
+		ServiceDescriptionProvider, Configurable, Cloneable {
+	void configure(Configuration configuration) throws Exception;
+	Configuration getConfiguration();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/CustomizedConfigurePanelProvider.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/CustomizedConfigurePanelProvider.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/CustomizedConfigurePanelProvider.java
new file mode 100644
index 0000000..8bb5331
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/CustomizedConfigurePanelProvider.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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.servicedescriptions;
+
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+
+public interface CustomizedConfigurePanelProvider extends
+		ConfigurableServiceProvider {
+	void createCustomizedConfigurePanel(CustomizedConfigureCallBack callBack);
+
+	interface CustomizedConfigureCallBack {
+		void newProviderConfiguration(Configuration providerConfig);
+
+		Configuration getTemplateConfig();
+
+		ServiceDescriptionRegistry getServiceDescriptionRegistry();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/IdentifiedObject.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/IdentifiedObject.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/IdentifiedObject.java
new file mode 100644
index 0000000..596f502
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/IdentifiedObject.java
@@ -0,0 +1,30 @@
+package net.sf.taverna.t2.servicedescriptions;
+
+import java.util.List;
+
+import net.sf.taverna.t2.lang.beans.PropertyAnnotated;
+
+public abstract class IdentifiedObject extends PropertyAnnotated {
+	@Override
+	public boolean equals(Object obj) {
+		if (!(obj instanceof IdentifiedObject))
+			return false;
+		List<? extends Object> myIdentifyingData = getIdentifyingData();
+		if (myIdentifyingData == null)
+			return super.equals(obj);
+		if (!getClass().isInstance(obj) && obj.getClass().isInstance(this))
+			return false;
+		IdentifiedObject id = (IdentifiedObject) obj;
+		return myIdentifyingData.equals(id.getIdentifyingData());
+	}
+
+	@Override
+	public int hashCode() {
+		List<? extends Object> identifyingData = getIdentifyingData();
+		if (identifyingData == null)
+			return super.hashCode();
+		return identifyingData.hashCode();
+	}
+
+	protected abstract List<? extends Object> getIdentifyingData();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescription.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescription.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescription.java
new file mode 100644
index 0000000..8551934
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescription.java
@@ -0,0 +1,80 @@
+package net.sf.taverna.t2.servicedescriptions;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.swing.Icon;
+
+import net.sf.taverna.t2.lang.beans.PropertyAnnotation;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public abstract class ServiceDescription extends IdentifiedObject {
+	public static final String SERVICE_TEMPLATES = "Service templates";
+	private static final String NAME = "Name";
+	private static final String SERVICE_CONFIGURATION = "Service configuration";
+	private static final String SERVICE_IMPLEMENTATION_URI = "Service implementation URI";
+	private static final String DESCRIPTION = "Description";
+	public static final String LOCAL_SERVICES = "Local services";
+
+	private String description = "";
+
+	@PropertyAnnotation(expert = true, displayName = SERVICE_IMPLEMENTATION_URI)
+	public abstract URI getActivityType();
+
+	@PropertyAnnotation(expert = true, displayName = SERVICE_CONFIGURATION)
+	public Configuration getActivityConfiguration() {
+		Configuration configuration = new Configuration();
+		configuration.setType(getActivityType().resolve("#Config"));
+		return configuration;
+	}
+
+	@PropertyAnnotation(displayName = DESCRIPTION)
+	public String getDescription() {
+		return this.description;
+	}
+
+	@PropertyAnnotation(expert = true)
+	public abstract Icon getIcon();
+
+	@PropertyAnnotation(displayName = NAME)
+	public abstract String getName();
+
+	@PropertyAnnotation(expert = true)
+	public abstract List<? extends Comparable<?>> getPath();
+
+	@PropertyAnnotation(hidden = true)
+	public boolean isTemplateService() {
+		return false;
+	}
+
+	/**
+	 * @param description
+	 *            the description to set
+	 */
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	@Override
+	public String toString() {
+		return "Service description " + getName();
+	}
+
+	/**
+	 * Any additional edit that needs to be performed upon insertion of an
+	 * instance of the ServiceDescription into the {@link Workflow} within the
+	 * specified {@link Processor}
+	 * 
+	 * @param dataflow
+	 * @param p
+	 * @param a
+	 * @return
+	 */
+	public Edit<?> getInsertionEdit(Workflow dataflow, Processor p, Activity a) {
+		return null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionProvider.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionProvider.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionProvider.java
new file mode 100644
index 0000000..8170819
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionProvider.java
@@ -0,0 +1,61 @@
+package net.sf.taverna.t2.servicedescriptions;
+
+import java.util.Collection;
+
+import javax.swing.Icon;
+
+import net.sf.taverna.t2.lang.beans.PropertyAnnotation;
+
+/**
+ * A provider of service descriptions
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public interface ServiceDescriptionProvider {
+	/**
+	 * Get all service descriptions.
+	 * 
+	 * @param callBack
+	 */
+	void findServiceDescriptionsAsync(FindServiceDescriptionsCallBack callBack);
+
+	/**
+	 * @author stain
+	 */
+	interface FindServiceDescriptionsCallBack {
+		void partialResults(
+				Collection<? extends ServiceDescription> serviceDescriptions);
+
+		void status(String message);
+
+		void warning(String message);
+
+		void finished();
+
+		void fail(String message, Throwable ex);
+	}
+
+	/**
+	 * Name of this service description provider, for instance "BioCatalogue" or
+	 * "WSDL". This name is typically used in a "Add service..." menu.
+	 * 
+	 * @return Name of provider
+	 */
+	String getName();
+
+	@PropertyAnnotation(expert = true)
+	abstract Icon getIcon();
+
+	/**
+	 * @return unique id of this provider.
+	 */
+	String getId();
+
+	/**
+	 * Create a new copy of this service provider. It <i>need not be
+	 * configured</i> at the point where it is returned.
+	 * 
+	 * @return The copy.
+	 */
+	ServiceDescriptionProvider newInstance();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionRegistry.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionRegistry.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionRegistry.java
new file mode 100644
index 0000000..e9b6c04
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionRegistry.java
@@ -0,0 +1,50 @@
+package net.sf.taverna.t2.servicedescriptions;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URL;
+import java.util.List;
+import java.util.Set;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionRegistryEvent;
+
+public interface ServiceDescriptionRegistry extends
+		Observable<ServiceDescriptionRegistryEvent> {
+	void addServiceDescriptionProvider(ServiceDescriptionProvider provider);
+
+	Set<ServiceDescriptionProvider> getDefaultServiceDescriptionProviders();
+
+	Set<ServiceDescriptionProvider> getServiceDescriptionProviders();
+
+	Set<ServiceDescriptionProvider> getServiceDescriptionProviders(
+			ServiceDescription sd);
+
+	Set<ServiceDescription> getServiceDescriptions();
+
+	ServiceDescription getServiceDescription(URI activityType);
+
+	List<ConfigurableServiceProvider> getUnconfiguredServiceProviders();
+
+	Set<ServiceDescriptionProvider> getUserAddedServiceProviders();
+
+	Set<ServiceDescriptionProvider> getUserRemovedServiceProviders();
+
+	void loadServiceProviders();
+
+	void loadServiceProviders(File serviceProvidersURL);
+
+	void loadServiceProviders(URL serviceProvidersURL);
+
+	void refresh();
+
+	void removeServiceDescriptionProvider(ServiceDescriptionProvider provider);
+
+	void saveServiceDescriptions();
+
+	void saveServiceDescriptions(File serviceDescriptionsFile);
+
+	void exportCurrentServiceDescriptions(File serviceDescriptionsFile);
+
+	boolean isDefaultSystemConfigurableProvidersLoaded();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionsConfiguration.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionsConfiguration.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionsConfiguration.java
new file mode 100644
index 0000000..7fbcbfc
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionsConfiguration.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.servicedescriptions;
+
+import uk.org.taverna.configuration.Configurable;
+
+/**
+ * @author David Withers
+ */
+public interface ServiceDescriptionsConfiguration extends Configurable {
+	public boolean isIncludeDefaults();
+
+	public void setIncludeDefaults(boolean includeDefaults);
+
+	public boolean isRemovePermanently();
+
+	public void setRemovePermanently(boolean removePermanently);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderEvent.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderEvent.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderEvent.java
new file mode 100644
index 0000000..1fd224e
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderEvent.java
@@ -0,0 +1,16 @@
+package net.sf.taverna.t2.servicedescriptions.events;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+
+public abstract class AbstractProviderEvent extends
+		ServiceDescriptionRegistryEvent {
+	private final ServiceDescriptionProvider provider;
+
+	public AbstractProviderEvent(ServiceDescriptionProvider provider) {
+		this.provider = provider;
+	}
+
+	public ServiceDescriptionProvider getProvider() {
+		return provider;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderNotification.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderNotification.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderNotification.java
new file mode 100644
index 0000000..2cabf90
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderNotification.java
@@ -0,0 +1,18 @@
+package net.sf.taverna.t2.servicedescriptions.events;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+
+public class AbstractProviderNotification extends AbstractProviderEvent {
+
+	private final String message;
+
+	public AbstractProviderNotification(ServiceDescriptionProvider provider, String message) {
+		super(provider);
+		this.message = message;
+	}
+
+	public String getMessage() {
+		return message;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AddedProviderEvent.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AddedProviderEvent.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AddedProviderEvent.java
new file mode 100644
index 0000000..6e003d7
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AddedProviderEvent.java
@@ -0,0 +1,10 @@
+package net.sf.taverna.t2.servicedescriptions.events;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+
+public class AddedProviderEvent extends AbstractProviderEvent {
+
+	public AddedProviderEvent(ServiceDescriptionProvider provider) {
+		super(provider);
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/PartialServiceDescriptionsNotification.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/PartialServiceDescriptionsNotification.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/PartialServiceDescriptionsNotification.java
new file mode 100644
index 0000000..3bd8c7f
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/PartialServiceDescriptionsNotification.java
@@ -0,0 +1,22 @@
+package net.sf.taverna.t2.servicedescriptions.events;
+
+import java.util.Collection;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+
+public class PartialServiceDescriptionsNotification extends
+		AbstractProviderNotification {
+	private final Collection<? extends ServiceDescription> serviceDescriptions;
+
+	public PartialServiceDescriptionsNotification(
+			ServiceDescriptionProvider provider,
+			Collection<? extends ServiceDescription> serviceDescriptions) {
+		super(provider, "Found " + serviceDescriptions.size() + " services");
+		this.serviceDescriptions = serviceDescriptions;
+	}
+
+	public Collection<? extends ServiceDescription> getServiceDescriptions() {
+		return serviceDescriptions;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderErrorNotification.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderErrorNotification.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderErrorNotification.java
new file mode 100644
index 0000000..a712124
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderErrorNotification.java
@@ -0,0 +1,19 @@
+package net.sf.taverna.t2.servicedescriptions.events;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+
+public class ProviderErrorNotification extends AbstractProviderNotification {
+
+	private final Throwable cause;
+
+	public ProviderErrorNotification(ServiceDescriptionProvider provider,
+			String message, Throwable cause) {
+		super(provider, message);
+		this.cause = cause;
+	}
+
+	public Throwable getCause() {
+		return cause;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderStatusNotification.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderStatusNotification.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderStatusNotification.java
new file mode 100644
index 0000000..f094e47
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderStatusNotification.java
@@ -0,0 +1,12 @@
+package net.sf.taverna.t2.servicedescriptions.events;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+
+public class ProviderStatusNotification extends AbstractProviderNotification {
+
+	public ProviderStatusNotification(ServiceDescriptionProvider provider,
+			String message) {
+		super(provider, message);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderUpdatingNotification.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderUpdatingNotification.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderUpdatingNotification.java
new file mode 100644
index 0000000..e2947e1
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderUpdatingNotification.java
@@ -0,0 +1,11 @@
+package net.sf.taverna.t2.servicedescriptions.events;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+
+public class ProviderUpdatingNotification extends AbstractProviderNotification {
+
+	public ProviderUpdatingNotification(ServiceDescriptionProvider provider) {
+		super(provider, "Updating");
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderWarningNotification.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderWarningNotification.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderWarningNotification.java
new file mode 100644
index 0000000..d7476aa
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderWarningNotification.java
@@ -0,0 +1,12 @@
+package net.sf.taverna.t2.servicedescriptions.events;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+
+public class ProviderWarningNotification extends AbstractProviderNotification {
+
+	public ProviderWarningNotification(ServiceDescriptionProvider provider,
+			String message) {
+		super(provider, message);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/RemovedProviderEvent.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/RemovedProviderEvent.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/RemovedProviderEvent.java
new file mode 100644
index 0000000..a382bdf
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/RemovedProviderEvent.java
@@ -0,0 +1,10 @@
+package net.sf.taverna.t2.servicedescriptions.events;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+
+public class RemovedProviderEvent extends AbstractProviderEvent {
+
+	public RemovedProviderEvent(ServiceDescriptionProvider provider) {
+		super(provider);
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionProvidedEvent.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionProvidedEvent.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionProvidedEvent.java
new file mode 100644
index 0000000..76ef22d
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionProvidedEvent.java
@@ -0,0 +1,20 @@
+package net.sf.taverna.t2.servicedescriptions.events;
+
+import java.util.Set;
+
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+
+public class ServiceDescriptionProvidedEvent extends AbstractProviderEvent {
+	private final Set<ServiceDescription> serviceDescriptions;
+
+	public ServiceDescriptionProvidedEvent(ServiceDescriptionProvider provider,
+			Set<ServiceDescription> serviceDescriptions) {
+		super(provider);
+		this.serviceDescriptions = serviceDescriptions;
+	}
+
+	public Set<ServiceDescription> getServiceDescriptions() {
+		return serviceDescriptions;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionRegistryEvent.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionRegistryEvent.java b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionRegistryEvent.java
new file mode 100644
index 0000000..0604510
--- /dev/null
+++ b/taverna-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionRegistryEvent.java
@@ -0,0 +1,4 @@
+package net.sf.taverna.t2.servicedescriptions.events;
+
+public abstract class ServiceDescriptionRegistryEvent {
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/pom.xml b/taverna-activity-palette-impl/pom.xml
new file mode 100644
index 0000000..ef85913
--- /dev/null
+++ b/taverna-activity-palette-impl/pom.xml
@@ -0,0 +1,98 @@
+<?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>taverna-activity-palette-impl</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Activity Palette Impl</name>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-activity-palette-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-workflowmodel-api</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-observer</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${taverna.osgi.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-app-configuration-api</artifactId>
+			<version>${taverna.osgi.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>${log4j.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-beanutils</groupId>
+			<artifactId>commons-beanutils</artifactId>
+			<version>${commons.beanutils.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.jdom</groupId>
+			<artifactId>com.springsource.org.jdom</artifactId>
+			<version>${jdom.version}</version>
+		</dependency>
+
+		<!-- TODO Remove non-test impl dependency -->
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-workflowmodel-impl</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>${junit.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-configuration-impl</artifactId>
+			<version>${taverna.osgi.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-app-configuration-impl</artifactId>
+			<version>${taverna.osgi.version}</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionConstants.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionConstants.java b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionConstants.java
new file mode 100644
index 0000000..c5221be
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionConstants.java
@@ -0,0 +1,10 @@
+package net.sf.taverna.t2.servicedescriptions.impl;
+
+public interface ServiceDescriptionConstants {
+	String SERVICE_PANEL_CONFIGURATION = "servicePanelConfiguration";
+	String PROVIDERS = "providers";
+	String IGNORED = "ignored";
+	String PROVIDER_ID = "providerID";
+	String CONFIGURATION = "configuration";
+	String TYPE = "type";
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionDeserializer.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionDeserializer.java b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionDeserializer.java
new file mode 100644
index 0000000..0a40bda
--- /dev/null
+++ b/taverna-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionDeserializer.java
@@ -0,0 +1,167 @@
+package net.sf.taverna.t2.servicedescriptions.impl;
+
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.CONFIGURATION;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.IGNORED;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDERS;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDER_ID;
+import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.TYPE;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+class ServiceDescriptionDeserializer {
+	private List<ServiceDescriptionProvider> serviceDescriptionProviders;
+
+	ServiceDescriptionDeserializer(
+			List<ServiceDescriptionProvider> serviceDescriptionProviders) {
+		this.serviceDescriptionProviders = serviceDescriptionProviders;
+	}
+
+	public void deserialize(ServiceDescriptionRegistry registry,
+			File serviceDescriptionsFile) throws DeserializationException {
+		try (FileInputStream serviceDescriptionFileStream = new FileInputStream(
+				serviceDescriptionsFile)) {
+			deserialize(registry, serviceDescriptionFileStream);
+		} catch (FileNotFoundException ex) {
+			throw new DeserializationException("Could not locate file "
+					+ serviceDescriptionsFile.getAbsolutePath()
+					+ " containing service descriptions.");
+		} catch (IOException ex) {
+			throw new DeserializationException(
+					"Could not read stream containing service descriptions from "
+							+ serviceDescriptionsFile.getAbsolutePath(), ex);
+		}
+	}
+
+	public void deserialize(ServiceDescriptionRegistry registry,
+			URL serviceDescriptionsURL) throws DeserializationException {
+		try (InputStream serviceDescriptionInputStream = serviceDescriptionsURL
+				.openStream()) {
+			deserialize(registry, serviceDescriptionInputStream);
+		} catch (FileNotFoundException ex) {
+			throw new DeserializationException("Could not open URL "
+					+ serviceDescriptionsURL
+					+ " containing service descriptions.");
+		} catch (IOException ex) {
+			throw new DeserializationException(
+					"Could not read stream containing service descriptions from "
+							+ serviceDescriptionsURL, ex);
+		}
+	}
+
+	private static final JsonFactory factory = new JsonFactory();
+
+	private void deserialize(ServiceDescriptionRegistry registry,
+			InputStream serviceDescriptionsInputStream) throws IOException,
+			DeserializationException {
+		ObjectNode node = (ObjectNode) new ObjectMapper(factory)
+				.readTree(serviceDescriptionsInputStream);
+		List<ServiceDescriptionProvider> providers = deserializeProviders(node,
+				true);
+		for (ServiceDescriptionProvider provider : providers)
+			registry.addServiceDescriptionProvider(provider);
+	}
+
+	public Collection<? extends ServiceDescriptionProvider> deserializeDefaults(
+			ServiceDescriptionRegistry registry,
+			File defaultConfigurableServiceProvidersFile)
+			throws DeserializationException {
+		ObjectNode node;
+		try (FileInputStream serviceDescriptionStream = new FileInputStream(
+				defaultConfigurableServiceProvidersFile)) {
+			node = (ObjectNode) new ObjectMapper(factory)
+					.readTree(serviceDescriptionStream);
+		} catch (IOException e) {
+			throw new DeserializationException("Can't read "
+					+ defaultConfigurableServiceProvidersFile);
+		}
+		return deserializeProviders(node, false);
+	}
+
+	private List<ServiceDescriptionProvider> deserializeProviders(
+			ObjectNode rootNode, boolean obeyIgnored)
+			throws DeserializationException {
+		List<ServiceDescriptionProvider> providers = new ArrayList<>();
+
+		ArrayNode providersNode = (ArrayNode) rootNode.get(PROVIDERS);
+		if (providersNode != null)
+			for (JsonNode provider : providersNode)
+				providers.add(deserializeProvider((ObjectNode) provider));
+
+		if (obeyIgnored) {
+			ArrayNode ignoredNode = (ArrayNode) rootNode.get(IGNORED);
+			if (ignoredNode != null)
+				for (JsonNode provider : ignoredNode)
+					providers
+							.remove(deserializeProvider((ObjectNode) provider));
+		}
+
+		return providers;
+	}
+
+	private ServiceDescriptionProvider deserializeProvider(
+			ObjectNode providerNode) throws DeserializationException {
+		String providerId = providerNode.get(PROVIDER_ID).asText().trim();
+		ServiceDescriptionProvider provider = null;
+		for (ServiceDescriptionProvider serviceProvider : serviceDescriptionProviders)
+			if (serviceProvider.getId().equals(providerId)) {
+				provider = serviceProvider;
+				break;
+			}
+		if (provider == null)
+			throw new DeserializationException(
+					"Could not find provider with id " + providerId);
+
+		/*
+		 * So we know the service provider now, but we need a separate instance
+		 * of that provider for each providerElem. E.g. we can have 2 or more
+		 * WSDL provider elements and need to return a separate provider
+		 * instance for each as they will have different configurations.
+		 */
+		ServiceDescriptionProvider instance = provider.newInstance();
+
+		if (instance instanceof ConfigurableServiceProvider)
+			try {
+				Configuration config = new Configuration();
+				config.setType(URI.create(providerNode.get(TYPE).textValue()));
+				config.setJson(providerNode.get(CONFIGURATION));
+				if (config != null)
+					((ConfigurableServiceProvider) instance).configure(config);
+			} catch (Exception e) {
+				throw new DeserializationException(
+						"Could not configure provider " + providerId
+								+ " using bean " + providerNode, e);
+			}
+		return instance;
+	}
+
+	@SuppressWarnings("serial")
+	static class DeserializationException extends Exception {
+		public DeserializationException(String string) {
+			super(string);
+		}
+
+		public DeserializationException(String string, Exception ex) {
+			super(string, ex);
+		}
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewBrowser.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewBrowser.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewBrowser.java
new file mode 100644
index 0000000..bdef0e7
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewBrowser.java
@@ -0,0 +1,711 @@
+package net.sf.taverna.t2.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.EventListener;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Base64;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs, Emmanuel Tagarira
+ */
+public class ResourcePreviewBrowser extends JFrame implements ActionListener, HyperlinkListener, ComponentListener {
+  // CONSTANTS
+  protected static final int PREFERRED_WIDTH = 750;
+  protected static final int PREFERRED_HEIGHT = 600;
+  protected static final int PREFERRED_SCROLL = 10;
+  protected static final int PREVIEW_HISTORY_LENGTH = 50;
+
+  // navigation data
+  private int iCurrentHistoryIdx; // index within the current history
+  private final ArrayList<String> alCurrentHistory; // current history - e.g. if one
+  // opens Page1, then Page2; goes back and opens Page3 - current preview would hold only [Page1, Page3]
+  private ArrayList<Resource> alFullHistory; // all resources that were
+  // previewed since application started (will be used by ResourcePreviewHistoryBrowser)
+
+  // components for accessing application's main elements
+  private final MainComponent pluginMainComponent;
+  private final MyExperimentClient myExperimentClient;
+  private final Logger logger;
+
+  // holder of the data about currently previewed item
+  private ResourcePreviewContent rpcContent;
+  private Resource resource;
+
+  // components of the preview window
+  private JPanel jpMain;
+  private JPanel jpStatusBar;
+  private JLabel lSpinnerIcon;
+  private JButton bBack;
+  private JButton bForward;
+  private JButton bRefresh;
+  private JButton bOpenInMyExp;
+  private JButton bDownload;
+  private JButton bOpenInTaverna;
+  private JButton bImportIntoTaverna;
+  private JButton bAddComment;
+  private JButton bAddRemoveFavourite;
+  private JButton bUpload;
+  private JButton bEditMetadata;
+  private JScrollPane spContentScroller;
+
+  // icons
+  private final ImageIcon iconOpenInMyExp = new ImageIcon(MyExperimentPerspective.getLocalResourceURL("open_in_my_experiment_icon"));
+  private final ImageIcon iconAddFavourite = new ImageIcon(MyExperimentPerspective.getLocalResourceURL("add_favourite_icon"));
+  private final ImageIcon iconDeleteFavourite = new ImageIcon(MyExperimentPerspective.getLocalResourceURL("delete_favourite_icon"));
+  private final ImageIcon iconAddComment = new ImageIcon(MyExperimentPerspective.getLocalResourceURL("add_comment_icon"));
+  private final ImageIcon iconSpinner = new ImageIcon(MyExperimentPerspective.getLocalResourceURL("spinner"));
+  private final ImageIcon iconSpinnerStopped = new ImageIcon(MyExperimentPerspective.getLocalResourceURL("spinner_stopped"));
+  private final FileManager fileManager;
+
+  public ResourcePreviewBrowser(MainComponent component, MyExperimentClient client, Logger logger, FileManager fileManager) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the
+	// parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+	this.fileManager = fileManager;
+
+	// initialise previewed items history
+	String strPreviewedItemsHistory = (String) myExperimentClient.getSettings().get(MyExperimentClient.INI_PREVIEWED_ITEMS_HISTORY);
+	if (strPreviewedItemsHistory != null) {
+	  Object oPreviewedItemsHistory = Base64.decodeToObject(strPreviewedItemsHistory);
+	  this.alFullHistory = (ArrayList<Resource>) oPreviewedItemsHistory;
+	} else {
+	  this.alFullHistory = new ArrayList<Resource>();
+	}
+
+	// no navigation history at loading
+	this.iCurrentHistoryIdx = -1;
+	this.alCurrentHistory = new ArrayList<String>();
+
+	// set options of the preview dialog box
+	this.setIconImage(new ImageIcon(MyExperimentPerspective.getLocalResourceURL("myexp_icon")).getImage());
+	this.addComponentListener(this);
+
+	this.initialiseUI();
+  }
+
+  /**
+   * Accessor method for getting a full history of previewed resources as a
+   * list.
+   */
+  public ArrayList<Resource> getPreviewHistory() {
+	return (this.alFullHistory);
+  }
+
+  /**
+   * As opposed to getPreviewHistory() which returns full history of previewed
+   * resources, this helper method only retrieves the current history stack.
+   *
+   * Example: if a user was to view the following items - A -> B -> C B <- C B
+   * -> D, the full history would be [A,C,B,D]; current history stack would be
+   * [A,B,D] - note how item C was "forgotten" (this works the same way as all
+   * web browsers do)
+   */
+  public List<String> getCurrentPreviewHistory() {
+	return (this.alCurrentHistory);
+  }
+
+  /**
+   * Deletes both 'current history' (the latest preview history stack) and the
+   * 'full preview history'. Also, resets the index in the current history, so
+   * that the preview browser would not allow using Back-Forward buttons until
+   * some new previews are opened.
+   */
+  public void clearPreviewHistory() {
+	this.iCurrentHistoryIdx = -1;
+	this.alCurrentHistory.clear();
+	this.alFullHistory.clear();
+  }
+
+  /**
+   * This method is a launcher for the real worker method ('createPreview()')
+   * that does all the job.
+   *
+   * The purpose of having this method is to manage history. This method is to
+   * be called every time when a "new" preview is requested. This will add a new
+   * link to the CurrentHistory stack.
+   *
+   * Clicks on "Back" and "Forward" buttons will only need to advance the
+   * counter of the current position in the CurrentHistory. Therefore, these
+   * will directly call 'createPreview()'.
+   */
+  public void preview(String action) {
+	// *** History Update ***
+	// if this is not the "newest" page in current history, remove all newer
+	// ones
+	// (that is if the user went "back" and opened some new link from on of the
+	// older pages)
+	while (alCurrentHistory.size() > iCurrentHistoryIdx + 1) {
+	  alCurrentHistory.remove(alCurrentHistory.size() - 1);
+	}
+
+	boolean bPreviewNotTheSameAsTheLastOne = true;
+	if (alCurrentHistory.size() > 0) {
+	  // will add new page to the history only if it's not the same as the last
+	  // one!
+	  if (action.equals(alCurrentHistory.get(alCurrentHistory.size() - 1))) {
+		bPreviewNotTheSameAsTheLastOne = false;
+	  }
+
+	  // this is not the first page in the history, enable "Back" button (if
+	  // only this isn't the same page as was the first one);
+	  // (this, however, is the last page in the history now - so disable
+	  // "Forward" button)
+	  bBack.setEnabled(bPreviewNotTheSameAsTheLastOne
+		  || alCurrentHistory.size() > 1);
+	  bForward.setEnabled(false);
+	} else if (alCurrentHistory.size() == 0) {
+	  // this is the first preview after application has loaded or since the
+	  // preview history was cleared - disable both Back and Forward buttons
+	  bBack.setEnabled(false);
+	  bForward.setEnabled(false);
+	}
+
+	// add current preview URI to the history
+	if (bPreviewNotTheSameAsTheLastOne) {
+	  iCurrentHistoryIdx++;
+	  alCurrentHistory.add(action);
+	}
+
+	// *** Launch Preview ***
+	createPreview(action);
+  }
+
+  private void createPreview(String action) {
+	// JUST FOR TESTING THE CURRENT_HISTORY OPERATION
+	// javax.swing.JOptionPane.showMessageDialog(null, "History idx: " +
+	// this.iCurrentHistoryIdx + "\n" + alCurrentHistory.toString());
+
+	// show that loading is in progress
+	this.setTitle("Loading preview...");
+	this.lSpinnerIcon.setIcon(this.iconSpinner);
+
+	// disable all action buttons while loading is in progress
+	bOpenInMyExp.setEnabled(false);
+	bDownload.setEnabled(false);
+	bOpenInTaverna.setEnabled(false);
+	bImportIntoTaverna.setEnabled(false);
+	bAddRemoveFavourite.setEnabled(false);
+	bAddComment.setEnabled(false);
+	bUpload.setEnabled(false);
+	bEditMetadata.setEnabled(false);
+
+	// Make call to myExperiment API in a different thread
+	// (then use SwingUtilities.invokeLater to update the UI when ready).
+	final String strAction = action;
+	final EventListener self = this;
+
+	new Thread("Load myExperiment resource preview content") {
+	  @Override
+	  public void run() {
+		logger.debug("Starting to fetch the preview content data");
+
+		try {
+		  // *** Fetch Data and Create Preview Content ***
+		  rpcContent = pluginMainComponent.getPreviewFactory().createPreview(strAction, self);
+
+		  // as all the details about the previewed resource are now known, can
+		  // store this into full preview history
+		  // (before that make sure that if the this item was viewed before,
+		  // it's removed and re-added at the "top" of the list)
+		  // (also make sure that the history size doesn't exceed the pre-set
+		  // value)
+		  alFullHistory.remove(rpcContent.getResource());
+		  alFullHistory.add(rpcContent.getResource());
+		  if (alFullHistory.size() > PREVIEW_HISTORY_LENGTH)
+			alFullHistory.remove(0);
+		  pluginMainComponent.getHistoryBrowser().refreshHistoryBox(HistoryBrowserTabContentPanel.PREVIEWED_ITEMS_HISTORY);
+
+		  // *** Update the Preview Dialog Box when everything is ready ***
+		  SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+			  // 'stop' loading action in the status bar and window title
+			  setTitle(Resource.getResourceTypeName(rpcContent.getResourceType())
+				  + ": " + rpcContent.getResourceTitle());
+			  lSpinnerIcon.setIcon(iconSpinnerStopped);
+
+			  // update the state of action buttons in the button bar
+			  updateButtonBarState(rpcContent);
+
+			  // wrap received content into a ScrollPane
+			  spContentScroller = new JScrollPane(rpcContent.getContent());
+			  spContentScroller.setBorder(BorderFactory.createEmptyBorder());
+			  spContentScroller.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+			  // remove everything from the preview and re-add all components
+			  // (NB! Removing only CENTER component didn't work properly)
+			  jpMain.removeAll();
+			  jpMain.add(redrawStatusBar(), BorderLayout.NORTH);
+			  jpMain.add(spContentScroller, BorderLayout.CENTER);
+			  validate();
+			  repaint();
+			}
+		  });
+		} catch (Exception ex) {
+		  logger.error("Exception on attempt to login to myExperiment:\n", ex);
+		}
+	  }
+	}.start();
+
+	// show the dialog box
+	this.setVisible(true);
+  }
+
+  private void initialiseUI() {
+	// create the STATUS BAR of the preview window
+	createButtonsForStatusBar();
+
+	// put everything together
+	jpMain = new JPanel();
+	jpMain.setOpaque(true);
+	jpMain.setLayout(new BorderLayout());
+	jpMain.add(redrawStatusBar(), BorderLayout.NORTH);
+
+	// add all content into the main dialog
+	this.getContentPane().add(jpMain);
+
+  }
+
+  private void createButtonsForStatusBar() {
+	// navigation buttons => far left of status bar
+	bBack = new JButton(new ImageIcon(MyExperimentPerspective.getLocalResourceURL("back_icon")));
+	bBack.setToolTipText("Back");
+	bBack.addActionListener(this);
+	bBack.setEnabled(false);
+
+	bForward = new JButton(new ImageIcon(MyExperimentPerspective.getLocalResourceURL("forward_icon")));
+	bForward.setToolTipText("Forward");
+	bForward.addActionListener(this);
+	bForward.setEnabled(false);
+
+	// refresh buttons => far right of status bar
+	bRefresh = new JButton(new ImageIcon(MyExperimentPerspective.getLocalResourceURL("refresh_icon")));
+	bRefresh.setToolTipText("Refresh");
+	bRefresh.addActionListener(this);
+
+	lSpinnerIcon = new JLabel(this.iconSpinner);
+
+	// ACTION BUTTONS
+	// 'open in myExperiment' button is the only one that is always available,
+	// still will be set available during loading of the preview for consistency of the UI
+
+	// myExperiment "webby" functions
+	bOpenInMyExp = new JButton(iconOpenInMyExp);
+	bOpenInMyExp.setEnabled(false);
+	bOpenInMyExp.addActionListener(this);
+
+	bAddRemoveFavourite = new JButton(iconAddFavourite);
+	bAddRemoveFavourite.setEnabled(false);
+	bAddRemoveFavourite.addActionListener(this);
+
+	bAddComment = new JButton(iconAddComment);
+	bAddComment.setEnabled(false);
+	bAddComment.addActionListener(this);
+
+	bEditMetadata = new JButton("Update", WorkbenchIcons.editIcon);
+	bEditMetadata.setEnabled(false);
+	bEditMetadata.addActionListener(this);
+
+	bUpload = new JButton("Upload", WorkbenchIcons.upArrowIcon);
+	bUpload.setEnabled(false);
+	bUpload.addActionListener(this);
+
+	// functions more specific to taverna
+	bOpenInTaverna = new JButton(WorkbenchIcons.openIcon);
+	bOpenInTaverna.setEnabled(false);
+	bOpenInTaverna.addActionListener(this);
+
+	bImportIntoTaverna = new JButton();
+	bImportIntoTaverna.setEnabled(false);
+	bImportIntoTaverna.addActionListener(this);
+
+	bDownload = new JButton(WorkbenchIcons.saveIcon);
+	bDownload.setEnabled(false);
+	bDownload.addActionListener(this);
+  }
+
+  private JPanel redrawStatusBar() {
+	// far left of button bar
+	JPanel jpNavigationButtons = new JPanel();
+	jpNavigationButtons.add(bBack);
+	jpNavigationButtons.add(bForward);
+
+	// far right of button bar
+	JPanel jpRefreshButtons = new JPanel();
+	jpRefreshButtons.add(bRefresh);
+	jpRefreshButtons.add(lSpinnerIcon);
+
+	// myExperiment buttons: second left of the button bar
+	JPanel jpMyExperimentButtons = new JPanel();
+	jpMyExperimentButtons.add(bOpenInMyExp);
+	jpMyExperimentButtons.add(bAddRemoveFavourite);
+	jpMyExperimentButtons.add(bAddComment);
+	jpMyExperimentButtons.add(bEditMetadata);
+	jpMyExperimentButtons.add(bUpload);
+
+	// taverna buttons: second right of the button bar
+	JPanel jpTavernaButtons = new JPanel();
+	jpTavernaButtons.add(bOpenInTaverna);
+	jpTavernaButtons.add(bImportIntoTaverna);
+	jpTavernaButtons.add(bDownload);
+
+	// put all action buttons into a button bar
+	JPanel jpStatusBar = new JPanel();
+	jpStatusBar.setLayout(new GridBagLayout());
+	int spaceBetweenSections = 40;
+
+	GridBagConstraints c = new GridBagConstraints();
+	c.insets = new Insets(0, spaceBetweenSections, 0, spaceBetweenSections / 2);
+	c.anchor = GridBagConstraints.WEST;
+	c.fill = GridBagConstraints.HORIZONTAL;
+	c.gridx = 0;
+	c.gridy = 0;
+	jpStatusBar.add(jpNavigationButtons, c);
+
+	c.gridx++;
+	c.insets = new Insets(0, spaceBetweenSections / 2, 0, spaceBetweenSections / 2);
+	jpStatusBar.add(jpMyExperimentButtons, c);
+
+	c.gridx++;
+	jpStatusBar.add(jpTavernaButtons, c);
+
+	c.gridx++;
+	c.insets = new Insets(0, spaceBetweenSections / 2, 0, spaceBetweenSections);
+	jpStatusBar.add(jpRefreshButtons, c);
+
+	return jpStatusBar;
+
+	//	// put all action buttons into a button bar
+	//	JPanel jpActionButtons = new JPanel();
+	//	jpActionButtons.setLayout(new GridBagLayout());
+	//	GridBagConstraints c = new GridBagConstraints();
+	//	double spacing = ResourcePreviewBrowser.PREFERRED_WIDTH * 0.1;
+	//	c.insets = new Insets(0, (int) spacing, 0, (int) spacing / 2);
+	//	c.gridx = 0;
+	//	c.gridy = 0;
+	//	jpActionButtons.add(jpMyExperimentButtons, c);
+	//
+	//	c.gridx++;
+	//	c.insets = new Insets(0, (int) spacing / 2, 0, (int) spacing);
+	//	jpActionButtons.add(jpTavernaButtons, c);
+	//
+	//	jpStatusBar = new JPanel();
+	//	jpStatusBar.setLayout(new BorderLayout());
+	//	jpStatusBar.add(jpNavigationButtons, BorderLayout.WEST);
+	//	jpStatusBar.add(jpActionButtons, BorderLayout.CENTER);
+	//	jpStatusBar.add(jpRefreshButtons, BorderLayout.EAST);
+  }
+
+  private void updateButtonBarState(ResourcePreviewContent content) {
+	// get the visible type name of the resource
+	Resource r = this.rpcContent.getResource();
+	String strResourceType = Resource.getResourceTypeName(r.getItemType()).toLowerCase();
+
+	// "Open in myExperiment" is always available for every item type
+	this.bOpenInMyExp.setEnabled(true);
+	this.bOpenInMyExp.setToolTipText("Open this " + strResourceType
+		+ " in myExperiment");
+
+	// "edit metadata" to myExperiment is only available for logged in
+	// users who are the owners of the workflow
+	String strTooltip = "It is currently not possible to edit the metadata for this workflow";
+	boolean bUpdateMetaAvailable = false;
+
+	if (myExperimentClient.isLoggedIn()
+		&& (myExperimentClient.getCurrentUser().equals(r.getUploader()))
+		&& (r.getItemTypeName().equals("Workflow"))) {
+	  strTooltip = "Update the metadata of this workflow.";
+	  bUpdateMetaAvailable = true;
+	} else {
+	  strTooltip = "Only the owners of workflows can change the metadata of workflows.";
+	}
+	this.bEditMetadata.setToolTipText(strTooltip);
+	this.bEditMetadata.setEnabled(bUpdateMetaAvailable);
+
+	// "upload new version" to myExperiment is only available for logged in
+	// users who are the owners of the workflow
+	strTooltip = "It is currently not possible to upload a new version of this workflow.";
+	boolean bUploadAvailable = false;
+
+	if (myExperimentClient.isLoggedIn()
+		&& (myExperimentClient.getCurrentUser().equals(r.getUploader()))
+		&& (r.getItemTypeName().equals("Workflow"))) {
+	  strTooltip = "Upload a new version of this workflow.";
+	  bUploadAvailable = true;
+	} else {
+	  strTooltip = "Only the owners of workflows can upload new versions of workflows.";
+	}
+	this.bUpload.setToolTipText(strTooltip);
+	this.bUpload.setEnabled(bUploadAvailable);
+
+	// "Download" - only for selected types and based on current user's
+	// permissions (these conditions are checked within the action)
+	this.bDownload.setAction(pluginMainComponent.new DownloadResourceAction(r, false));
+
+	// "Open in Taverna" - only for Taverna workflows and when download is
+	// allowed for current user (these checks are carried out inside the action)
+	this.bOpenInTaverna.setAction(pluginMainComponent.new LoadResourceInTavernaAction(r, true));
+
+	// "Import into Taverna" - only for Taverna workflows and when download is
+	// allowed for current user (these checks are carried out inside the action)
+	// the import button
+	this.bImportIntoTaverna.setAction(pluginMainComponent.new ImportIntoTavernaAction(r));
+
+	// "Add to Favourites" - for all types, but only for logged in users
+	strTooltip = "It is currently not possible to add " + strResourceType
+		+ "s to favourites";
+	boolean bFavouritingAvailable = false;
+	if (r.isFavouritable()) {
+	  if (myExperimentClient.isLoggedIn()) {
+		if (r.isFavouritedBy(myExperimentClient.getCurrentUser())) {
+		  strTooltip = "Remove this " + strResourceType
+			  + " from your favourites";
+		  this.bAddRemoveFavourite.setIcon(iconDeleteFavourite);
+		} else {
+		  strTooltip = "Add this " + strResourceType + " to your favourites";
+		  this.bAddRemoveFavourite.setIcon(iconAddFavourite);
+		}
+		bFavouritingAvailable = true;
+	  } else {
+		// TODO should be changed to display login box first, then favouriting option
+		strTooltip = "Only logged in users can add items to favourites";
+	  }
+	}
+	this.bAddRemoveFavourite.setToolTipText(strTooltip);
+	this.bAddRemoveFavourite.setEnabled(bFavouritingAvailable);
+
+	// "Add Comment" - for all types besides users and only for logged in users
+	strTooltip = "It is currently not possible to comment on "
+		+ strResourceType + "s";
+	boolean bCommentingAvailable = false;
+	if (r.isCommentableOn()) {
+	  if (myExperimentClient.isLoggedIn()) {
+		strTooltip = "Add a comment on this " + strResourceType;
+		bCommentingAvailable = true;
+	  } else {
+		// TODO should be changed to display login box first, then commenting option
+		strTooltip = "Only logged in users can make comments";
+	  }
+	}
+	this.bAddComment.setToolTipText(strTooltip);
+	this.bAddComment.setEnabled(bCommentingAvailable);
+  }
+
+  public void hyperlinkUpdate(HyperlinkEvent e) {
+	if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+	  String strAction = e.getDescription().toString();
+
+	  if (strAction.startsWith("preview:")) {
+		this.preview(strAction);
+	  } else {
+		try {
+		    Desktop.getDesktop().browse(new URI(strAction));
+		} catch (Exception ex) {
+		  logger.error("Failed while trying to open the URL in a standard browser; URL was: "
+			  + strAction + "\nException was: " + ex);
+		}
+	  }
+	}
+  }
+
+  public void actionPerformed(ActionEvent e) {
+	if (e.getSource().equals(this.bBack)) {
+	  // "Back" button clicked
+
+	  // update position in the history
+	  iCurrentHistoryIdx--;
+
+	  // enable or disable "back"/"forward" buttons as appropriate
+	  bBack.setEnabled(iCurrentHistoryIdx > 0);
+	  bForward.setEnabled(iCurrentHistoryIdx < alCurrentHistory.size() - 1);
+
+	  // open requested preview from the history
+	  this.createPreview(alCurrentHistory.get(iCurrentHistoryIdx));
+	} else if (e.getSource().equals(this.bForward)) {
+	  // "Forward" button clicked
+
+	  // update position in the history
+	  iCurrentHistoryIdx++;
+
+	  // enable or disable "back"/"forward" buttons as appropriate
+	  bBack.setEnabled(iCurrentHistoryIdx > 0);
+	  bForward.setEnabled(iCurrentHistoryIdx < alCurrentHistory.size() - 1);
+
+	  // open requested preview from the history
+	  this.createPreview(alCurrentHistory.get(iCurrentHistoryIdx));
+	} else if (e.getSource().equals(this.bRefresh)) {
+	  // "Refresh" button clicked
+
+	  // simply reload the same preview
+	  this.createPreview(alCurrentHistory.get(iCurrentHistoryIdx));
+	} else if (e.getSource().equals(this.bOpenInMyExp)) {
+	  // "Open in myExperiment" button clicked
+	  try {
+	      Desktop.getDesktop().browse(new URI(this.rpcContent.getResourceURL()));
+	  } catch (Exception ex) {
+		logger.error("Failed while trying to open the URL in a standard browser; URL was: "
+			+ this.rpcContent.getResourceURL() + "\nException was: " + ex);
+	  }
+	} else if (e.getSource().equals(this.bUpload)) {
+	  /* ************************************************************************* */
+	  Resource resource = this.rpcContent.getResource();
+	  if (resource.getItemTypeName().equals("Workflow")) {
+		UploadWorkflowDialog uploadWorkflowDialog = new UploadWorkflowDialog(this, true, resource, fileManager);
+
+		if (uploadWorkflowDialog.launchUploadDialogAndPostIfRequired()) {
+		  // "true" has been returned so update the resource
+		  this.actionPerformed(new ActionEvent(this.bRefresh, 0, ""));
+		}
+	  }
+	} else if (e.getSource().equals(this.bEditMetadata)) {
+	  Resource resource = this.rpcContent.getResource();
+	  if (resource.getItemTypeName().equals("Workflow")) {
+		UploadWorkflowDialog uploadWorkflowDialog = new UploadWorkflowDialog(this, false, resource, fileManager);
+
+		if (uploadWorkflowDialog.launchUploadDialogAndPostIfRequired()) {
+		  // "true" has been returned so update the resource
+		  this.actionPerformed(new ActionEvent(this.bRefresh, 0, ""));
+		}
+	  }
+	  /* ************************************************************************* */
+	} else if (e.getSource().equals(this.bAddComment)) {
+	  // "Add Comment" button was clicked
+	  String strComment = null;
+	  AddCommentDialog commentDialog = new AddCommentDialog(this, this.rpcContent.getResource(), pluginMainComponent, myExperimentClient, logger);
+	  if ((strComment = commentDialog.launchAddCommentDialogAndPostCommentIfRequired()) != null) {
+		// comment was added because return value is not null;
+		// a good option now would be to reload only the comments tab, but
+		// for now we refresh the whole of the preview
+		this.actionPerformed(new ActionEvent(this.bRefresh, 0, ""));
+
+		// update history of the items that were commented on, making sure that:
+		// - there's only one occurrence of this item in the history;
+		// - if this item was in the history before, it is moved to the 'top' now;
+		// - predefined history size is not exceeded
+		this.pluginMainComponent.getHistoryBrowser().getCommentedOnItemsHistoryList().remove(this.rpcContent.getResource());
+		this.pluginMainComponent.getHistoryBrowser().getCommentedOnItemsHistoryList().add(this.rpcContent.getResource());
+		if (this.pluginMainComponent.getHistoryBrowser().getCommentedOnItemsHistoryList().size() > HistoryBrowserTabContentPanel.COMMENTED_ON_ITEMS_HISTORY) {
+		  this.pluginMainComponent.getHistoryBrowser().getCommentedOnItemsHistoryList().remove(0);
+		}
+
+		// now update the history of the items that were commented on in 'History' tab
+		if (this.pluginMainComponent.getHistoryBrowser() != null) {
+		  this.pluginMainComponent.getHistoryBrowser().refreshHistoryBox(HistoryBrowserTabContentPanel.COMMENTED_ON_ITEMS_HISTORY);
+		}
+	  }
+	} else if (e.getSource().equals(this.bAddRemoveFavourite)) {
+	  boolean bItemIsFavourited = this.rpcContent.getResource().isFavouritedBy(this.myExperimentClient.getCurrentUser());
+
+	  AddRemoveFavouriteDialog favouriteDialog = new AddRemoveFavouriteDialog(this, !bItemIsFavourited, this.rpcContent.getResource(), pluginMainComponent, myExperimentClient, logger);
+	  int iFavouritingStatus = favouriteDialog.launchAddRemoveFavouriteDialogAndPerformNecessaryActionIfRequired();
+
+	  // if the operation wasn't cancelled, update status of the
+	  // "add/remove favourite" button and the list of favourites in the user profile
+	  if (iFavouritingStatus != AddRemoveFavouriteDialog.OPERATION_CANCELLED) {
+		this.updateButtonBarState(this.rpcContent);
+		this.pluginMainComponent.getMyStuffTab().getSidebar().repopulateFavouritesBox();
+		this.pluginMainComponent.getMyStuffTab().getSidebar().revalidate();
+	  }
+	} else if (e.getSource() instanceof JClickableLabel) {
+	  // clicked somewhere on a JClickableLabel; if that's a 'preview' request - launch preview
+	  if (e.getActionCommand().startsWith("preview:")) {
+		this.preview(e.getActionCommand());
+	  } else if (e.getActionCommand().startsWith("tag:")) {
+		// pass this event onto the Tag Browser tab
+		this.pluginMainComponent.getTagBrowserTab().actionPerformed(e);
+		this.pluginMainComponent.getMainTabs().setSelectedComponent(this.pluginMainComponent.getTagBrowserTab());
+	  } else {
+		// show the link otherwise
+		try {
+		    Desktop.getDesktop().browse(new URI(e.getActionCommand()));
+		} catch (Exception ex) {
+		  logger.error("Failed while trying to open the URL in a standard browser; URL was: "
+			  + e.getActionCommand() + "\nException was: " + ex);
+		}
+	  }
+	} else if (e.getSource() instanceof TagCloudPanel
+		&& e.getActionCommand().startsWith("tag:")) {
+	  // close the window and pass this event onto the Tag Browser tab
+	  this.setVisible(false);
+	  this.pluginMainComponent.getTagBrowserTab().actionPerformed(e);
+	  this.pluginMainComponent.getMainTabs().setSelectedComponent(this.pluginMainComponent.getTagBrowserTab());
+	}
+  }
+
+  // *** Callbacks for ComponentListener interface ***
+
+  public void componentShown(ComponentEvent e) {
+	// every time the preview browser window is shown, it will start loading a preview
+	// - this state is set in the preview() method; (so this won't have to be done here)
+
+	// remove everything from the preview and re-add only the status bar
+	// (this is done so that newly opened preview window won't show the old
+	// preview)
+	jpMain.removeAll();
+	jpMain.add(redrawStatusBar(), BorderLayout.NORTH);
+	repaint();
+
+	// set the size of the dialog box (NB! Size needs to be set before the position!)
+	this.setSize(ResourcePreviewBrowser.PREFERRED_WIDTH, ResourcePreviewBrowser.PREFERRED_HEIGHT);
+	this.setMinimumSize(new Dimension(ResourcePreviewBrowser.PREFERRED_WIDTH, ResourcePreviewBrowser.PREFERRED_HEIGHT));
+
+	// make sure that the dialog box appears centered horizontally relatively to
+	// the main component; also, pad by 30px vertically from the top of the main component
+	int iMainComponentCenterX = (int) Math.round(this.pluginMainComponent.getLocationOnScreen().getX()
+		+ (this.pluginMainComponent.getWidth() / 2));
+	int iPosX = iMainComponentCenterX - (this.getWidth() / 2);
+	int iPosY = ((int) this.pluginMainComponent.getLocationOnScreen().getY()) + 30;
+	this.setLocation(iPosX, iPosY);
+
+	myExperimentClient.storeHistoryAndSettings();
+  }
+
+  public void componentHidden(ComponentEvent e) {
+	myExperimentClient.storeHistoryAndSettings();
+  }
+
+  public void componentResized(ComponentEvent e) {
+	// do nothing
+  }
+
+  public void componentMoved(ComponentEvent e) {
+	// do nothing
+  }
+
+  public Resource getResource() {
+	return resource;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewContent.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewContent.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewContent.java
new file mode 100644
index 0000000..85b47c0
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewContent.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import javax.swing.JComponent;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+
+/**
+ * Helper class to hold all data about the generated preview.
+ * 
+ * @author Sergejs Aleksejevs
+ * 
+ */
+public class ResourcePreviewContent
+{
+  private Resource resource;
+  private JComponent jcContent;
+  
+  public ResourcePreviewContent()
+  {
+    // empty constructor
+  }
+  
+  public ResourcePreviewContent(Resource resource, JComponent content)
+  {
+    this.resource = resource;
+    this.jcContent = content;
+  }
+  
+  public Resource getResource()
+  {
+    return(this.resource);
+  }
+  
+  public int getResourceType()
+  {
+    return(this.resource.getItemType());
+  }
+  
+  public String getResourceTitle()
+  {
+    return(this.resource.getTitle());
+  }
+  
+  public String getResourceURL()
+  {
+    return(this.resource.getResource());
+  }
+  
+  public String getResourceURI()
+  {
+    return(this.resource.getURI());
+  }
+  
+  public JComponent getContent()
+  {
+    return(this.jcContent);
+  }
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualView.java b/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualView.java
new file mode 100644
index 0000000..22d2da1
--- /dev/null
+++ b/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualView.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * 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.parallelize;
+
+import java.awt.BorderLayout;
+import java.awt.Frame;
+import java.util.List;
+
+import javax.swing.Action;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+
+import net.sf.taverna.t2.lang.ui.ReadOnlyTextArea;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+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.JsonNode;
+
+/**
+ * View of a processor, including it's iteration stack, activities, etc.
+ *
+ * @author Alan R Williams
+ *
+ */
+@SuppressWarnings("serial")
+public class ParallelizeContextualView extends ContextualView {
+
+	private final Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	private Processor processor;
+
+	private JPanel panel;
+
+	private final EditManager editManager;
+
+	private final SelectionManager selectionManager;
+
+	public ParallelizeContextualView(Processor processor, EditManager editManager, SelectionManager selectionManager) {
+		super();
+		this.processor = processor;
+		this.editManager = editManager;
+		this.selectionManager = selectionManager;
+		initialise();
+		initView();
+	}
+
+	@Override
+	public void refreshView() {
+		initialise();
+	}
+
+	private void initialise() {
+		if (panel == null) {
+			panel = createPanel();
+		} else {
+			panel.removeAll();
+		}
+
+		JTextArea textArea = new ReadOnlyTextArea();
+		textArea.setEditable(false);
+		String maxJobs = "1";
+		for (Configuration configuration : scufl2Tools.configurationsFor(processor, selectionManager.getSelectedProfile())) {
+			JsonNode processorConfig = configuration.getJson();
+			if (processorConfig.has("parallelize")) {
+				JsonNode parallelizeConfig = processorConfig.get("parallelize");
+				if (parallelizeConfig.has("maximumJobs")) {
+					maxJobs = parallelizeConfig.get("maximumJobs").asText();
+				}
+			}
+		}
+		textArea.setText("The maximum number of jobs is " + maxJobs);
+		textArea.setBackground(panel.getBackground());
+		panel.add(textArea, BorderLayout.CENTER);
+		revalidate();
+	}
+
+
+	@Override
+	public JComponent getMainFrame() {
+		return panel;
+	}
+
+	@Override
+	public String getViewTitle() {
+	    return "Parallel jobs";
+	}
+
+	protected JPanel createPanel() {
+		JPanel result = new JPanel();
+		result.setLayout(new BorderLayout());
+
+
+		return result;
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 400;
+	}
+
+	@Override
+	public Action getConfigureAction(Frame owner) {
+		return new ParallelizeConfigureAction(owner, this, processor, editManager, selectionManager);
+	}
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualViewFactory.java b/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualViewFactory.java
new file mode 100644
index 0000000..f9ff7e7
--- /dev/null
+++ b/taverna-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualViewFactory.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * 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.parallelize;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import uk.org.taverna.scufl2.api.core.Processor;
+
+public class ParallelizeContextualViewFactory implements ContextualViewFactory<Processor> {
+
+	public static URI TYPE = URI.create("http://ns.taverna.org.uk/2010/scufl2/taverna/dispatchlayer/Parallelize");
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public boolean canHandle(Object selection) {
+		return selection instanceof Processor;
+	}
+
+	public List<ContextualView> getViews(Processor selection) {
+		return Arrays.asList(new ContextualView[] {new ParallelizeContextualView(selection, editManager, selectionManager)});
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..edd7b2d
--- /dev/null
+++ b/taverna-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.parallelize.ParallelizeConfigureMenuAction

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
----------------------------------------------------------------------
diff --git a/taverna-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
new file mode 100644
index 0000000..8b94f86
--- /dev/null
+++ b/taverna-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.parallelize.ParallelizeContextualViewFactory

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context-osgi.xml b/taverna-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context-osgi.xml
new file mode 100644
index 0000000..1859672
--- /dev/null
+++ b/taverna-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context-osgi.xml
@@ -0,0 +1,16 @@
+<?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="ParallelizeConfigureMenuAction" auto-export="interfaces" />
+
+	<service ref="ParallelizeContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" />
+
+	<reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" />
+	<reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context.xml
----------------------------------------------------------------------
diff --git a/taverna-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context.xml b/taverna-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context.xml
new file mode 100644
index 0000000..1d1fdd4
--- /dev/null
+++ b/taverna-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context.xml
@@ -0,0 +1,16 @@
+<?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="ParallelizeConfigureMenuAction" class="net.sf.taverna.t2.workbench.parallelize.ParallelizeConfigureMenuAction">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+
+	<bean id="ParallelizeContextualViewFactory" class="net.sf.taverna.t2.workbench.parallelize.ParallelizeContextualViewFactory">
+			<property name="editManager" ref="editManager" />
+			<property name="selectionManager" ref="selectionManager" />
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/LocalTestLauncher.bat
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/LocalTestLauncher.bat b/taverna-perspective-biocatalogue/LocalTestLauncher.bat
new file mode 100644
index 0000000..1c7f299
--- /dev/null
+++ b/taverna-perspective-biocatalogue/LocalTestLauncher.bat
@@ -0,0 +1,14 @@
+@echo off
+echo.
+echo.
+del biocatalogue-perspective-local-launch.jar
+echo Deleted old JAR file if it was there.
+cd target\classes
+jar cfM ..\..\biocatalogue-perspective-local-launch.jar *.*
+cd ..\..
+echo JAR assembly done, launching app...
+
+java -cp .;.\biocatalogue-perspective-local-launch.jar;c:/Users/Sergey/.m2/repository/net/sf/taverna/t2/workbench/ui-api/0.2/ui-api-0.2.jar;c:\Users\Sergey\.m2\repository\net\sf\taverna\t2\lang\ui\1.0\ui-1.0.jar;c:\Users\Sergey\.m2\repository\net\sf\taverna\t2\ui-components\workflow-view\1.0\workflow-view-1.0.jar;c:\Users\Sergey\.m2\repository\net\sf\taverna\t2\ui-activities\wsdl-activity-ui\0.7\wsdl-activity-ui-0.7.jar;C:\Users\Sergey\.m2\repository\log4j\log4j\1.2.13\log4j-1.2.13.jar;C:\Users\Sergey\.m2\repository\net\sf\taverna\t2\workbench\commons-icons\0.2\commons-icons-0.2.jar;c:\Users\Sergey\.m2\repository\BrowserLauncher2\BrowserLauncher2\1.3\BrowserLauncher2-1.3.jar;C:\Users\Sergey\.m2\repository\jdom\jdom\1.0\jdom-1.0.jar;"c:\Program Files\Java\xmlbeans-2.4.0\lib\xbean.jar";"c:\Program Files\Java\xmlbeans-2.4.0\lib\jsr173_1.0_api.jar";.\lib\lablib-checkboxtree-3.1.jar;.\lib\core-renderer.jar;.\lib\commons-lang-2.4.jar net.sf.taverna.t2.ui.perspectives.biocatalogue.TestJFra
 meForLocalLaunch
+
+del biocatalogue-perspective-local-launch.jar
+echo Cleanup done - deleted old the JAR file that was used for the current launch.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/lib/core-renderer.jar
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/lib/core-renderer.jar b/taverna-perspective-biocatalogue/lib/core-renderer.jar
new file mode 100644
index 0000000..871fabf
Binary files /dev/null and b/taverna-perspective-biocatalogue/lib/core-renderer.jar differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/log4j.properties
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/log4j.properties b/taverna-perspective-biocatalogue/log4j.properties
new file mode 100644
index 0000000..fcd1d0c
--- /dev/null
+++ b/taverna-perspective-biocatalogue/log4j.properties
@@ -0,0 +1,4 @@
+log4j.rootLogger=DEBUG, A1
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/pom.xml b/taverna-perspective-biocatalogue/pom.xml
new file mode 100644
index 0000000..4188449
--- /dev/null
+++ b/taverna-perspective-biocatalogue/pom.xml
@@ -0,0 +1,178 @@
+<?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>perspective-biocatalogue</artifactId>
+	<name>BioCatalogue Perspective</name>
+	<repositories>
+		<repository>
+			<releases />
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+			<id>fuse</id>
+			<name>fuseRepository</name>
+			<url>http://repo.fusesource.com/maven2-all/</url>
+		</repository>
+	</repositories>
+
+	<dependencies>
+		<!-- <dependency> <groupId>${project.parent.groupId}</groupId> <artifactId>perspective-core</artifactId>
+			<version>${project.parent.version}</version> </dependency> -->
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>workflowmodel-impl</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>menu-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>net.sf.taverna.t2.lang</groupId>
+			<artifactId>ui</artifactId>
+			<version>${t2.lang.version}</version>
+		</dependency>
+		<!-- required for providing contextual views in the bottom-left area of
+			Design perspective -->
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>contextual-views-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<!-- required for inserting a SOAP processor into the current workflow -->
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-activities</groupId>
+			<artifactId>wsdl-activity-ui</artifactId>
+			<version>${t2.ui.activities.version}</version>
+		</dependency>
+		<!-- required for importing REST processors into the current workflow -->
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-activities</groupId>
+			<artifactId>rest-activity-ui</artifactId>
+			<version>${t2.ui.activities.version}</version>
+		</dependency>
+		<!-- required for inserting a processor into the current workflow -->
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>workflow-view</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<!-- required registering with and opening help window -->
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>helper</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>uk.org.taverna.configuration</groupId>
+			<artifactId>taverna-app-configuration-api</artifactId>
+			<version>${taverna.configuration.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.jdom</groupId>
+			<artifactId>com.springsource.org.jdom</artifactId>
+			<version>${jdom.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.log4j</groupId>
+			<artifactId>com.springsource.org.apache.log4j</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.xmlbeans</groupId>
+			<artifactId>xmlbeans</artifactId>
+			<version>2.5.0</version>
+		</dependency>
+
+		<!-- FlyingSaucer XHTML Renderer -->
+		<!-- (it is critical to use version R8, not any earlier ones - e.g. R8pre2,
+			etc.) -->
+		<dependency>
+			<groupId>org.xhtmlrenderer</groupId>
+			<artifactId>core-renderer</artifactId>
+			<version>${org.xhtmlrenderer.core-renderer.version}</version>
+			<exclusions>
+				<exclusion>
+					<groupId>bouncycastle</groupId>
+					<artifactId>bcprov-jdk14</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>bouncycastle</groupId>
+					<artifactId>bcmail-jdk14</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+
+		<!-- At least StringEscapeUtils class is used from this library -->
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>com.springsource.org.apache.commons.lang</artifactId>
+			<version>${commons.lang.version}</version>
+		</dependency>
+
+		<!-- Gson: Java to Json conversion -->
+		<dependency>
+			<groupId>com.google.code.gson</groupId>
+			<artifactId>gson</artifactId>
+			<version>${gson.version}</version>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<!-- Adds "xmlbeans:xmlbeans" Maven2 goal to compile the API binding classes
+			from XSD schema. -->
+		<plugins>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>xmlbeans-maven-plugin</artifactId>
+				<version>2.3.3</version>
+				<executions>
+					<execution>
+						<goals>
+							<goal>xmlbeans</goal>
+						</goals>
+					</execution>
+				</executions>
+				<inherited>true</inherited>
+				<configuration>
+					<!-- "javaSource=1.5" is required to make use of generics and have getXXXList()
+						methods available, not just getXXXArrray() -->
+					<javaSource>1.5</javaSource>
+					<download>true</download>
+					<schemaDirectory>src/main/xsd</schemaDirectory>
+					<!-- Default is target/generated-sources/xmlbeans - which the Maven
+						plugin should be able to add to the Project classpath -->
+					<!-- <sourceGenerationDirectory>src/main/java</sourceGenerationDirectory> -->
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/schema_compilation/move_scomp_results_into_project.bat
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/schema_compilation/move_scomp_results_into_project.bat b/taverna-perspective-biocatalogue/schema_compilation/move_scomp_results_into_project.bat
new file mode 100644
index 0000000..3786d7c
--- /dev/null
+++ b/taverna-perspective-biocatalogue/schema_compilation/move_scomp_results_into_project.bat
@@ -0,0 +1,29 @@
+@echo off
+
+echo This will replace the JAR file with compiled API classes
+pause
+del ..\lib\biocatalogue_api_classes.jar
+move biocatalogue_api_classes.jar ..\lib\
+echo JAR file replaced
+echo.
+
+REM replace the sources of API classes
+echo This will delete *ALL* files in \src\main\java\org\biocatalogue
+echo                                 \src\main\java\org\purl
+echo                                 \src\main\java\org\w3
+pause
+
+rd /S /Q ..\src\main\java\org\biocatalogue
+rd /S /Q ..\src\main\java\org\purl
+rd /S /Q ..\src\main\java\org\w3
+
+move /Y org\biocatalogue ..\src\main\java\org
+move /Y org\purl ..\src\main\java\org
+move /Y org\w3 ..\src\main\java\org
+rd org
+
+echo Sources of API classes replaced
+echo.
+
+echo Done!
+pause
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/schema_compilation/scomp_compile_from_web.bat
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/schema_compilation/scomp_compile_from_web.bat b/taverna-perspective-biocatalogue/schema_compilation/scomp_compile_from_web.bat
new file mode 100644
index 0000000..ee10a50
--- /dev/null
+++ b/taverna-perspective-biocatalogue/schema_compilation/scomp_compile_from_web.bat
@@ -0,0 +1,9 @@
+@echo off
+
+REM -src . -- put source files here
+REM -srconly -- only sources, no compiling of java classes, no jar bundling
+REM -compiler -- where to find javac
+REM -javasource -- which JAVA version to aim for (1.5 uses generics)
+REM -dl -- allows download of referenced schemas
+
+scomp -src . -srconly -compiler "%JAVA_HOME%\bin\javac.exe" -javasource 1.5 -dl http://www.biocatalogue.org/2009/xml/rest/schema-v1.xsd
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/schema_compilation/scomp_compile_from_web_to_jar.bat
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/schema_compilation/scomp_compile_from_web_to_jar.bat b/taverna-perspective-biocatalogue/schema_compilation/scomp_compile_from_web_to_jar.bat
new file mode 100644
index 0000000..dd66206
--- /dev/null
+++ b/taverna-perspective-biocatalogue/schema_compilation/scomp_compile_from_web_to_jar.bat
@@ -0,0 +1,9 @@
+@echo off
+
+REM -src . -- put source files here
+REM -compiler -- where to find javac
+REM -javasource -- which JAVA version to aim for (1.5 uses generics)
+REM -out -- specifies the name of the target JAR file
+REM -dl -- allows download of referenced schemas
+
+scomp -src . -compiler "%JAVA_HOME%\bin\javac.exe" -javasource 1.5 -out biocatalogue_api_classes.jar -dl http://www.biocatalogue.org/2009/xml/rest/schema-v1.xsd
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/doc/BioCatalogue Plugin Documentation.odt
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/doc/BioCatalogue Plugin Documentation.odt b/taverna-perspective-biocatalogue/src/main/doc/BioCatalogue Plugin Documentation.odt
new file mode 100644
index 0000000..85056d7
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/doc/BioCatalogue Plugin Documentation.odt differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/help/Index-TOC-Map-Additions.txt
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/help/Index-TOC-Map-Additions.txt b/taverna-perspective-biocatalogue/src/main/help/Index-TOC-Map-Additions.txt
new file mode 100644
index 0000000..8d6c953
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/help/Index-TOC-Map-Additions.txt
@@ -0,0 +1,20 @@
+/* http://www.mygrid.org.uk/taverna2_1/helpset/toc.xml */
+
+<tocitem text="BioCatalogue Plugin" target="biocatalogue-plugin" expand="false">
+  <tocitem text="Feature Overview" target="biocatalogue-plugin-features"/>
+  <tocitem text="Feedback" target="biocatalogue-plugin-feedback"/>
+</tocitem>
+
+
+
+/* http://www.mygrid.org.uk/taverna2_1/helpset/index.xml */
+
+<indexitem text="BioCatalogue Plugin" target="biocatalogue-plugin"/>
+
+
+
+/* http://www.mygrid.org.uk/taverna2_1/helpset/map.xml */
+
+<mapID target="biocatalogue-plugin" url="html/biocatalogue-plugin.html"/>
+<mapID target="biocatalogue-plugin-features" url="html/biocatalogue-plugin-features.html"/>
+<mapID target="biocatalogue-plugin-feedback" url="html/biocatalogue-plugin-feedback.html"/>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin-features.html
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin-features.html b/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin-features.html
new file mode 100644
index 0000000..ece2949
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin-features.html
@@ -0,0 +1,113 @@
+<html>
+  <head>
+    <meta content="text/html; charset=MacRoman" http-equiv="Content-Type">
+    <link rel="stylesheet" href="./basic.css" type="text/css">
+    <meta name="generator" content="Helen">
+    <title>BioCatalogue Plugin - Features</title>
+  </head>
+  <body>
+    <h2>
+      BioCatalogue Plugin - Features
+    </h2>
+    
+    <h3>
+      BioCatalogue Perspective
+    </h3>
+    <p>
+      This perspective is designed for searching and browsing the BioCatalogue
+      data. It aids with discovery of Processors that may be used in a workflow.
+    </p>
+    
+    <p>
+      In the BioCatalogue perspective you may:
+      <ul>
+        <li>Search for Web Services by free text queries of by tags;</li>
+        <li>Filter search results by the same set of criteria as available on the BioCatalogue website;</li>
+        <li>Save favourite filters and search queries for re-use at a later point;</li>
+        <li>View search history;</li>
+        <li>Immediately see the types and monitoring statuses of all found Web
+            Services directly in the results listings;</li>
+        <li>Preview Web Services.</li>
+      </ul>
+    </p>
+    
+    <p>
+      Web Service previews display a similar set of information to
+      that shown on the BioCatalogue website: service description,
+      type, location, provider and other details are shown. Listing
+      of all operations and their descriptions within that Web Service
+      is shown. Any operation may be added directly into the current
+      workflow or into the Service Panel in the Design Perspective
+      for later use.
+    </p>
+    
+    <h3>
+      Integration into Design Perspective
+    </h3>
+    
+    <p>
+      <ul>
+        <li>Any Web Service operations added to the Service Panel from the BioCatalogue
+            perspective can be dragged into the Workflow Diagram like any other Processors.
+            They are saved by the plugin, so that when Taverna is restarted, those services
+            can still be found in the Service Panel.
+        </li>
+        <li>Right mouse click on a Processor in the Workflow Explorer or Workflow Diagram
+            will display options provided by the plugin - for all WSDL Processors it is
+            possible to check their monitoring status or launch the Processor Preview.
+        </li>
+        <li>Right mouse click on an empty space in the Workflow Diagram will let to launch
+            the workflow "health check" - currently this feature will identify a list of all
+            WSDL activities in a workflow and will fetch the latest monitoring data about
+            each of the from BioCatalogue.
+        </li>
+        <li>"Details" tab in the contextual view area (bottom-left corner of the Design
+            Perspective) will display information about the WSDL Processors and their
+            input or output ports (if they are registered in BioCatalogue). These contextual
+            views are only shown if BioCatalogue knows how to handle the selected type of
+            workflow element.
+        </li>
+      </ul>
+    </p>
+    
+    
+    <h3>
+      Choosing the BioCatalogue Instance to Work With
+    </h3>
+    
+    <p>
+      The BioCatalogue is an open-source project and anyone can setup their
+      own instance of the BioCatalogue software. By default, the plugin is
+      configured to use the main BioCatalogue website
+      (at <font color="blue">http://www.biocatalogue.org</font>) as a source
+      of data.
+    </p>
+    <p>
+      Should this be necessary, the plugin can be configured to use another
+      instance of BioCatalogue. This can be done through the Preferences dialog
+      of Taverna by going to: <pre>File -> Preferences -> BioCatalogue</pre>
+    </p>
+    
+    
+    <h3>
+      Known Issues and Missing Functionality
+    </h3>
+    
+    <p>
+      Below are the most important known issues. These will be fixed in the later releases.
+    </p>
+    
+    <p>
+      <ul>
+        <li>Previews are only available for SOAP services, but not REST services
+            or users, registries, service providers.
+        </li>
+        <li>Search history, favourite search queries and filters are not persisted.
+            This means that this data will only be available for the current working
+            session and will be lost after Taverna is switched off.
+        </li>
+        <li>Only read access to the BioCatalogue data is currently provided.</li>
+      </ul>
+    </p>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin-feedback.html
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin-feedback.html b/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin-feedback.html
new file mode 100644
index 0000000..b2c48b4
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin-feedback.html
@@ -0,0 +1,28 @@
+<html>
+  <head>
+    <meta content="text/html; charset=MacRoman" http-equiv="Content-Type">
+    <link rel="stylesheet" href="./basic.css" type="text/css">
+    <meta name="generator" content="Helen">
+    <title>BioCatalogue Plugin - Feedback</title>
+  </head>
+  <body>
+    <h2>
+      BioCatalogue Plugin - Feedback
+    </h2>
+    <p>
+      Please provide us with your feedback to help improve the BioCatalogue plugin.
+      In order to do so, please go to <em>About</em> tab in the <em>BioCatalogue perspective</em>
+      and click the "Leave feedback" button - you will be taken to a web page with a form
+      to fill in and submit your comments.
+    </p>
+    <p>
+      Developers are very interested to hear:
+      <ul>
+        <li>suggestions regarding the existing functionality;</li>
+        <li>new feature requests;</li>
+        <li>ideas for improving the user interfaces;</li>
+        <li>any other feedback you may have.</li>
+      </ul>
+    </p>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin.html
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin.html b/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin.html
new file mode 100644
index 0000000..152b935
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/help/biocatalogue-plugin.html
@@ -0,0 +1,54 @@
+<html>
+  <head>
+    <meta content="text/html; charset=MacRoman" http-equiv="Content-Type">
+    <link rel="stylesheet" href="./basic.css" type="text/css">
+    <meta name="generator" content="Helen">
+    <title>BioCatalogue Plugin</title>
+  </head>
+  <body>
+    <h2>
+      BioCatalogue Plugin
+    </h2>
+    <h3><small>Version: 0.1.1 (alpha)</small></h3>
+    <p>
+      The <em>BioCatalogue plugin</em> is intended to provide access to the data held 
+      in the <em>BioCatalogue Web Services Registry</em> directly from <em>Taverna Workbench</em>.
+    </p>
+    <p>
+      In its current state the plugin is designed to:
+      <ul>
+        <li>display the integration capabilities with both BioCatalogue and Taverna;</li>
+        <li>provide a general idea of the kinds of data that can be fetched
+            from the Biocatalogue through its REST API;
+        </li>
+        <li>attempt to make the workflow composition process easier and provide useful
+            contextual data to help with understanding of existing workflows.
+        </li>
+      </ul>
+    </p>
+    <p>
+      This release has made the plugin compatible with the latest version of 
+      Taverna - 2.2. Several important bugs were also fixed, however more new
+      functionality will be added soon.
+    </p>
+    <p>
+      To learn more about the available functionality, please see the <a href=
+      "./biocatalogue-plugin-features.html">feature list</a>.
+    </p>
+    <p><b>
+      Please note that this is an incomplete version of the BioCatalogue plugin.
+      You may see notifications that certain pieces of functionality have not been
+      implemented yet; some features are not yet fully stable, which means that
+      occasionally you may see unexpected error messages.
+    </b></p>
+    <p>
+       Any <a href="./biocatalogue-plugin-feedback.html">feedback</a> will be greatly apppreciated - it
+       will help to understand the true needs of the user community and develop
+       a complete version of this plugin later in the year.
+    </p>
+    <p>
+      This version of the plugin was developed by Sergejs Aleksejevs as part of his
+      final year project on the undergraduate Computer Science course at the University of Manchester.
+    </p>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/BioCataloguePluginConstants.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/BioCataloguePluginConstants.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/BioCataloguePluginConstants.java
new file mode 100644
index 0000000..791b544
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/BioCataloguePluginConstants.java
@@ -0,0 +1,77 @@
+package net.sf.taverna.biocatalogue.model;
+
+import java.io.File;
+
+
+/**
+ * This class contains the collection of important constants,
+ * which are used throughout the BioCatalogue plugin.
+ *
+ * @author Sergejs Aleksejevs
+ */
+public class BioCataloguePluginConstants
+{
+  public static final String APP_VISIBLE_NAME = "Service Catalogue Plugin";
+  public static final String APP_PREFIX = "T2ServiceCataloguePlugin:";
+
+
+  public static final boolean PERFORM_API_RESPONSE_TIME_LOGGING = true;
+  public static final boolean PERFORM_API_XML_DATA_BINDING_TIME_LOGGING = true;
+  public static final String API_OPERATION_LOG_FILENAME = "service_catalogue_api.log";
+
+
+  public static final int DEFAULT_SCROLL = 15;               // default vertical scroll increment to be used in all JScrollPane instances within the plugin
+  public static final int DEFAULT_TOOLTIP_DURATION = 10000;  // default duration of visibility of tooltips (in "ms")
+  public static final int DEFAULT_THREAD_STARTUP_TIME = 10;  // this is the time (in "ms") that we think the system takes at most to start a new thread
+
+  public static final int API_DEFAULT_REQUESTED_TAG_COUNT_PER_PAGE = 50;
+  public static final int API_DEFAULT_REQUESTED_WEB_SERVICE_COUNT_PER_PAGE = 20;
+  public static final int API_DEFAULT_REQUESTED_SOAP_OPERATION_COUNT_PER_PAGE = 20;
+  public static final int API_DEFAULT_REQUESTED_REST_METHOD_COUNT_PER_PAGE = 20;
+  public static final int API_DEFAULT_REQUESTED_USER_COUNT_PER_PAGE = 20;
+  public static final int API_DEFAULT_REQUESTED_SERVICE_PROVIDER_COUNT_PER_PAGE = 20;
+
+
+  public static final int SEARCH_HISTORY_LENGTH = 50;        // maximum number of search history items to store (if exceeded, oldest will be removed)
+  public static final int FAVOURITE_SEARCHES_LENGTH = 30;    // maximum number of favourite search settings to store (if exceeded, oldest will be removed)
+  public static final int FAVOURITE_FILTERS_LENGTH = 30;     // maximum number of favourite service filters to store (if exceeded, oldest will be removed)
+  public static final int RESOURCE_PREVIEW_HISTORY_LENGTH = 50;
+
+  public static final int RESOURCE_PREVIEW_BROWSER_PREFERRED_WIDTH = 750;
+  public static final int RESOURCE_PREVIEW_BROWSER_PREFERRED_HEIGHT = 600;
+
+  public static final String ACTION_FILTER_FOUND_SERVICES = APP_PREFIX + "filterFoundServices:";
+  public static final String ACTION_FILTER_BY_CATEGORY = APP_PREFIX + "filterByCategory:";
+  public static final String ACTION_SHOW_IN_WEB_BROWSER = APP_PREFIX + "showInWebBrowser:";
+  public static final String ACTION_SHOW_TAG_SELECTION_DIALOG = APP_PREFIX + "showTagSelectionDialgog";
+  public static final String ACTION_PREVIEW_CURRENT_FILTER = APP_PREFIX + "previewCurrentFilter";
+  public static final String ACTION_PREVIEW_RESOURCE = APP_PREFIX + "preview:";
+  public static final String ACTION_PREVIEW_SOAP_OPERATION_AFTER_LOOKUP = APP_PREFIX + "previewSoapOperationAfterLookup:";
+  public static final String ACTION_PREVIEWED_SERVICE_HEALTH_CHECK = APP_PREFIX + "previewedServiceHealthCheck";
+  public static final String ACTION_TAG_SEARCH_PREFIX = APP_PREFIX + "tag:";
+
+
+
+  public static final String CONFIG_FILE_FOLDER_WHEN_RUNNING_STANDALONE = ".Taverna2-ServiceCatalogue Plugin";
+
+
+
+  // ---------------------------- CONTEXTUAL VIEWS --------------------------------
+
+  // this value currently makes contextual views generated by this
+  // plugin the to be the last in the list
+  public static final int CONTEXTUAL_VIEW_PREFERRED_POSITION = 600;
+
+
+
+  // ------------------------------------------------------------------------------
+
+  /*
+   * Some of the settings are determined during the runtime - hence are non-final.
+   *
+   * These are set in MainComponent.initialiseEnvironment()
+   */
+
+  public static File CONFIG_FILE_FOLDER = new File(ApplicationRuntime.getInstance().getApplicationHomeDir(), "conf");
+  public static File LOG_FILE_FOLDER = new File(ApplicationRuntime.getInstance().getApplicationHomeDir(), "logs");
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/HTTPMethodInterpreter.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/HTTPMethodInterpreter.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/HTTPMethodInterpreter.java
new file mode 100644
index 0000000..ecd4cf1
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/HTTPMethodInterpreter.java
@@ -0,0 +1,46 @@
+package net.sf.taverna.biocatalogue.model;
+
+import org.apache.log4j.Logger;
+import org.biocatalogue.x2009.xml.rest.HttpVerb;
+
+import net.sf.taverna.t2.activities.rest.RESTActivity.HTTP_METHOD;
+
+/**
+ * Very simple class for translating HTTP method values returned
+ * by the BioCatalogue API into the set of values that are used
+ * by the REST activity.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class HTTPMethodInterpreter
+{
+  // deny instantiation of this class
+  private HTTPMethodInterpreter() { }
+  
+  public static HTTP_METHOD getHTTPMethodForRESTActivity(HttpVerb.Enum httpVerb)
+  {
+    switch (httpVerb.intValue()) {
+      case HttpVerb.INT_GET: return HTTP_METHOD.GET;
+      case HttpVerb.INT_POST: return HTTP_METHOD.POST;
+      case HttpVerb.INT_PUT: return HTTP_METHOD.PUT;
+      case HttpVerb.INT_DELETE: return HTTP_METHOD.DELETE;
+      default:
+        String errorMsg = "Unable to translate " + httpVerb.toString() + " to correct representation for REST activity;\n" +
+        		              "this HTTP method wasn't supported at the time of implementation.";
+        Logger.getLogger(HTTPMethodInterpreter.class).error(errorMsg);
+        throw new UnsupportedHTTPMethodException(errorMsg);
+    }
+  }
+  
+  
+  public static class UnsupportedHTTPMethodException extends IllegalArgumentException
+  {
+    public UnsupportedHTTPMethodException() {
+      /* empty constructor */
+    }
+    
+    public UnsupportedHTTPMethodException(String message) {
+      super(message);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/LoadingExpandedResource.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/LoadingExpandedResource.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/LoadingExpandedResource.java
new file mode 100644
index 0000000..cbd3f2e
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/LoadingExpandedResource.java
@@ -0,0 +1,41 @@
+package net.sf.taverna.biocatalogue.model;
+
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+import org.biocatalogue.x2009.xml.rest.impl.ResourceLinkImpl;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class LoadingExpandedResource extends ResourceLinkImpl
+{
+  private boolean nowLoading;
+  private ResourceLink associatedObj;
+  
+  public LoadingExpandedResource(ResourceLink associatedObj)
+  {
+    super(ResourceLink.type);
+    
+    this.associatedObj = associatedObj;
+    this.nowLoading = true;
+  }
+  
+  public ResourceLink getAssociatedObj() {
+    return associatedObj;
+  }
+  
+  public boolean isLoading() {
+    return (nowLoading);
+  }
+  public void setLoading(boolean isLoading) {
+    this.nowLoading = isLoading;
+  }
+  
+  public String getHref() {
+    return (associatedObj.getHref());
+  }
+  
+  public String getResourceName() {
+    return (associatedObj.getResourceName());
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/LoadingResource.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/LoadingResource.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/LoadingResource.java
new file mode 100644
index 0000000..a003075
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/LoadingResource.java
@@ -0,0 +1,39 @@
+package net.sf.taverna.biocatalogue.model;
+
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+import org.biocatalogue.x2009.xml.rest.impl.ResourceLinkImpl;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class LoadingResource extends ResourceLinkImpl
+{
+  private boolean nowLoading;
+  private ResourceLink associatedObj;
+  
+  public LoadingResource(String resourceURL, String resourceName) {
+    super(ResourceLink.type);
+    
+    associatedObj = ResourceLink.Factory.newInstance();
+    associatedObj.setHref(resourceURL);
+    associatedObj.setResourceName(resourceName);
+    
+    this.nowLoading = false;
+  }
+  
+  public String getHref() {
+    return (associatedObj.getHref());
+  }
+  
+  public String getResourceName() {
+    return (associatedObj.getResourceName());
+  }
+  
+  public boolean isLoading() {
+    return (nowLoading);
+  }
+  public void setLoading(boolean isLoading) {
+    this.nowLoading = isLoading;
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Pair.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Pair.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Pair.java
new file mode 100644
index 0000000..b694db8
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Pair.java
@@ -0,0 +1,30 @@
+package net.sf.taverna.biocatalogue.model;
+
+/**
+ * Trivial class to represent a generic pair of objects.
+ * Any types of objects can be used.
+ * 
+ * @author Sergejs Aleksejevs
+ *
+ * @param <T1> Type of the first object.
+ * @param <T2> Type of the second object.
+ */
+public class Pair<T1,T2>
+{
+  private final T1 firstObject;
+  private final T2 secondObject;
+
+  public Pair(T1 firstObject, T2 secondObject) {
+    this.firstObject = firstObject;
+    this.secondObject = secondObject;
+  }
+  
+  public T1 getFirstObject() {
+    return firstObject;
+  }
+  
+  public T2 getSecondObject() {
+    return secondObject;
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Resource.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Resource.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Resource.java
new file mode 100644
index 0000000..3095863
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Resource.java
@@ -0,0 +1,506 @@
+package net.sf.taverna.biocatalogue.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.Icon;
+import javax.swing.JOptionPane;
+import javax.swing.ListCellRenderer;
+
+import net.sf.taverna.biocatalogue.model.connectivity.BeansForJSONLiteAPI;
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+import net.sf.taverna.biocatalogue.ui.search_results.RESTMethodListCellRenderer;
+import net.sf.taverna.biocatalogue.ui.search_results.SOAPOperationListCellRenderer;
+import net.sf.taverna.biocatalogue.ui.search_results.ServiceListCellRenderer;
+import net.sf.taverna.t2.workbench.MainWindow;
+
+import org.apache.log4j.Logger;
+import org.biocatalogue.x2009.xml.rest.Registry;
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+import org.biocatalogue.x2009.xml.rest.RestMethod;
+import org.biocatalogue.x2009.xml.rest.RestMethods;
+import org.biocatalogue.x2009.xml.rest.Service;
+import org.biocatalogue.x2009.xml.rest.ServiceProvider;
+import org.biocatalogue.x2009.xml.rest.Services;
+import org.biocatalogue.x2009.xml.rest.SoapOperation;
+import org.biocatalogue.x2009.xml.rest.SoapOperations;
+import org.biocatalogue.x2009.xml.rest.User;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class Resource
+{
+  /**
+   * A single point of definition of the types of resources that the BioCatalogue plugin
+   * "knows" about. This enum provides various details about resource types -
+   * display names for single items of that type, names of collections of items of that
+   * type, icons to represent the items of a particular type, etc.
+   * 
+   * @author Sergejs Aleksejevs
+   */
+  public static enum TYPE
+  {
+    // the order is important - all these types will appear in the user interface
+    // in the same order as listed here
+    @SuppressWarnings("serial")
+	SOAPOperation (SoapOperation.class, SoapOperations.class, BeansForJSONLiteAPI.SOAPOperationsIndex.class, "WSDL service", "WSDL services",
+                   "WSDL services can be directly imported into the current workflow or Service Panel",
+                   ResourceManager.getIconFromTaverna(ResourceManager.SOAP_OPERATION_ICON), true, true, true, false, true, true, true, true,
+                   SOAPOperationListCellRenderer.class, BioCatalogueClient.API_SOAP_OPERATIONS_URL,
+                   new HashMap<String,String>() {{
+                   }},
+                   new HashMap<String,String>(BioCatalogueClient.API_INCLUDE_ANCESTORS) {{
+                     put(BioCatalogueClient.API_PER_PAGE_PARAMETER, ""+BioCataloguePluginConstants.API_DEFAULT_REQUESTED_SOAP_OPERATION_COUNT_PER_PAGE);
+                   }},
+                   BioCataloguePluginConstants.API_DEFAULT_REQUESTED_SOAP_OPERATION_COUNT_PER_PAGE,
+                   BioCatalogueClient.API_SOAP_OPERATION_FILTERS_URL),
+                   
+    @SuppressWarnings("serial")
+	RESTMethod    (RestMethod.class, RestMethods.class, BeansForJSONLiteAPI.RESTMethodsIndex.class, "REST service", "REST services",
+                   "REST services can be directly imported into the current workflow or Service Panel",
+                   ResourceManager.getIconFromTaverna(ResourceManager.REST_METHOD_ICON), true, true, true, false, true, false, true, true,
+                   RESTMethodListCellRenderer.class, BioCatalogueClient.API_REST_METHODS_URL,
+                   new HashMap<String,String>() {{
+                   }},
+                   new HashMap<String,String>(BioCatalogueClient.API_INCLUDE_ANCESTORS) {{
+                     put(BioCatalogueClient.API_PER_PAGE_PARAMETER, ""+BioCataloguePluginConstants.API_DEFAULT_REQUESTED_REST_METHOD_COUNT_PER_PAGE);
+                   }},
+                   BioCataloguePluginConstants.API_DEFAULT_REQUESTED_REST_METHOD_COUNT_PER_PAGE,
+                   BioCatalogueClient.API_REST_METHOD_FILTERS_URL); //,
+                   
+    // TODO - the following resource types have been disabled, as no actions for them can be done yet
+    //        -- they are still to be implemented; if the following types are uncommented, they will be
+    //        automatically searchable and visible in BioCatalogue Exploration tab; ListCellRenderers, however,
+    //        would need to be added first.
+//    @SuppressWarnings("serial")
+//	Service       (Service.class, Services.class, BeansForJSONLiteAPI.ServicesIndex.class, "Web service", "Web services",
+//                   "<html>Web services represent collections of WSDL services or REST services.<br>" +
+//                         "They cannot be directly imported into the current workflow or Service Panel,<br>" +
+//                         "but they may contain much more information about individual WSDL or REST<br>" +
+//                         "services and also provide some context for their usage.</html>",
+//                   ResourceManager.getImageIcon(ResourceManager.SERVICE_ICON), true, true, true, false, false, false, true,
+//                   ServiceListCellRenderer.class, BioCatalogueClient.API_SERVICES_URL, 
+//                   new HashMap<String,String>(BioCatalogueClient.API_INCLUDE_SUMMARY) {{
+//                   }},
+//                   new HashMap<String,String>() {{
+//                     put(BioCatalogueClient.API_PER_PAGE_PARAMETER, ""+BioCataloguePluginConstants.API_DEFAULT_REQUESTED_WEB_SERVICE_COUNT_PER_PAGE);
+//                   }},
+//                   BioCataloguePluginConstants.API_DEFAULT_REQUESTED_WEB_SERVICE_COUNT_PER_PAGE,
+//                   BioCatalogueClient.API_SERVICE_FILTERS_URL),
+//                   
+//    ServiceProvider (ServiceProvider.class, ServiceProviders.class, BeansForJSONLiteAPI.ServiceProvidersIndex.class, "Service Provider", "Service Providers", "",
+//                     ResourceManager.getImageIcon(ResourceManager.SERVICE_PROVIDER_ICON), false, false, false, false, false, false, false,
+//                     ServiceProviderListCellRenderer.class, BioCatalogueClient.API_SERVICE_PROVIDERS_URL,
+//                     new HashMap<String,String>() {{
+//                     }},
+//                     new HashMap<String,String>() {{
+//                       put(BioCatalogueClient.API_PER_PAGE_PARAMETER, ""+BioCataloguePluginConstants.API_DEFAULT_REQUESTED_SERVICE_PROVIDER_COUNT_PER_PAGE);
+//                     }},
+//                     BioCataloguePluginConstants.API_DEFAULT_REQUESTED_SERVICE_PROVIDER_COUNT_PER_PAGE,
+//                     null),
+//                     
+//    User          (User.class, Users.class, BeansForJSONLiteAPI.UsersIndex.class, "User", "Users", "",
+//                   ResourceManager.getImageIcon(ResourceManager.USER_ICON), false, false, true, false, false, false, false,
+//                   UserListCellRenderer.class, BioCatalogueClient.API_USERS_URL,
+//                   new HashMap<String,String>() {{
+//                   }},
+//                   new HashMap<String,String>() {{
+//                     put(BioCatalogueClient.API_PER_PAGE_PARAMETER, ""+BioCataloguePluginConstants.API_DEFAULT_REQUESTED_USER_COUNT_PER_PAGE);
+//                   }},
+//                   BioCataloguePluginConstants.API_DEFAULT_REQUESTED_USER_COUNT_PER_PAGE,
+//                   BioCatalogueClient.API_USER_FILTERS_URL);
+    
+    
+    @SuppressWarnings("unchecked")
+	private Class xmlbeansGeneratedClass;
+    @SuppressWarnings("unchecked")
+	private Class xmlbeansGeneratedCollectionClass;
+    private Class<?> jsonLiteAPIBindingBeanClass;
+    private String resourceTypeName;
+    private String resourceCollectionName;
+    private String resourceTabTooltip;
+    private Icon icon;
+    private boolean defaultType;
+    private boolean suitableForTagSearch;
+    private boolean suitableForFiltering;
+    private boolean suitableForOpeningInPreviewBrowser;
+    private boolean suitableForAddingToServicePanel;
+    private boolean suitableForAddingToWorkflowDiagram;
+    private boolean suitableForHealthCheck;
+    private Class<? extends ListCellRenderer> resultListingCellRendererClass;
+    private String apiResourceCollectionIndex;
+    private Map<String,String> apiResourceCollectionIndexSingleExpandedResourceAdditionalParameters;
+    private Map<String,String> apiResourceCollectionIndexAdditionalParameters;
+    private int apiResourceCountPerIndexPage;
+    private String apiResourceCollectionFilters;
+	private final boolean suitableForAddingAllToServicePanel;
+    
+    @SuppressWarnings("unchecked")
+	TYPE(Class xmlbeansGeneratedClass, Class xmlbeansGeneratedCollectionClass, Class<?> jsonLiteAPIBindingBeanClass,
+        String resourceTypeName, String resourceCollectionName, String resourceTabTooltip, Icon icon,
+        boolean defaultType, boolean suitableForTagSearch, boolean suitableForFiltering, boolean suitableForOpeningInPreviewBrowser,
+        boolean suitableForAddingToServicePanel, boolean suitableForAddingAllToServicePanel, boolean suitableForAddingToWorkflowDiagram,
+        boolean suitableForHealthCheck, Class<? extends ListCellRenderer> resultListingCellRendererClass,
+        String apiResourceCollectionIndex, Map<String,String> apiResourceCollectionIndexSingleExpandedResourceAdditionalParameters,
+        Map<String,String> apiResourceCollectionIndexAdditionalParameters, int apiResourceCountPerIndexListingPage,
+        String apiResourceCollectionFilters)
+    {
+      this.xmlbeansGeneratedClass = xmlbeansGeneratedClass;
+      this.xmlbeansGeneratedCollectionClass = xmlbeansGeneratedCollectionClass;
+      this.jsonLiteAPIBindingBeanClass = jsonLiteAPIBindingBeanClass;
+      this.resourceTypeName = resourceTypeName;
+      this.resourceCollectionName = resourceCollectionName;
+      this.resourceTabTooltip = resourceTabTooltip;
+      this.icon = icon;
+      this.defaultType = defaultType;
+      this.suitableForTagSearch = suitableForTagSearch;
+      this.suitableForFiltering = suitableForFiltering;
+      this.suitableForOpeningInPreviewBrowser = suitableForOpeningInPreviewBrowser;
+      this.suitableForAddingToServicePanel = suitableForAddingToServicePanel;
+	this.suitableForAddingAllToServicePanel = suitableForAddingAllToServicePanel;
+      this.suitableForAddingToWorkflowDiagram = suitableForAddingToWorkflowDiagram;
+      this.suitableForHealthCheck = suitableForHealthCheck;
+      this.resultListingCellRendererClass = resultListingCellRendererClass;
+      this.apiResourceCollectionIndex = apiResourceCollectionIndex;
+      this.apiResourceCollectionIndexSingleExpandedResourceAdditionalParameters = apiResourceCollectionIndexSingleExpandedResourceAdditionalParameters;
+      this.apiResourceCollectionIndexAdditionalParameters = apiResourceCollectionIndexAdditionalParameters;
+      this.apiResourceCountPerIndexPage = apiResourceCountPerIndexListingPage;
+      this.apiResourceCollectionFilters = apiResourceCollectionFilters;
+    }
+    
+    
+    
+    @SuppressWarnings("unchecked")
+	public Class getXmlBeansGeneratedClass() {
+      return this.xmlbeansGeneratedClass;
+    }
+    
+    /**
+     * @return Class that represents collection of resources of this type,
+     *         as represented by XmlBeans.
+     */
+    @SuppressWarnings("unchecked")
+	public Class getXmlBeansGeneratedCollectionClass() {
+      return this.xmlbeansGeneratedCollectionClass;
+    }
+    
+    
+    /**
+     * @return Class of the bean to be used when de-serialising JSON
+     *         data received from the 'Lite' BioCatalogue JSON API's index
+     *         of resources of this type.  
+     */
+    public Class<?> getJsonLiteAPIBindingBeanClass() {
+      return this.jsonLiteAPIBindingBeanClass;
+    }
+    
+    
+    /**
+     * @return Display name of a type of a single item belonging to that type.
+     *         (E.g. 'User' or 'Service') 
+     */
+    public String getTypeName() {
+      return this.resourceTypeName;
+    }
+    
+    /**
+     * @return Display name of a collection of items of this type.
+     *         (E.g. 'Users' or 'Services').
+     */
+    public String getCollectionName() {
+      return this.resourceCollectionName;
+    }
+    
+    /**
+     * @return HTML-formatted string that can be used as a tooltip
+     *         for tabs in BioCatalogue Exploration tab of BioCatalogue
+     *         perspective.
+     */
+    public String getCollectionTabTooltip() {
+      return this.resourceTabTooltip;
+    }
+    
+    /**
+     * @return Small icon that represents this resource type.
+     */
+    public Icon getIcon() {
+      return this.icon;
+    }
+    
+    /**
+     * @return <code>true</code> - if used for search by default;<br/>
+     *         <code>false</code> - otherwise.
+     */
+    public boolean isDefaultSearchType() {
+      return this.defaultType;
+    }
+    
+    /**
+     * Resources not of all resource types can be searched for by tags (although every resource type
+     * can be searched for by a free-text query).
+     * 
+     * @return <code>true</code> if resources of this type can be searched for by tags,<br/>
+     *         <code>false</code> otherwise.
+     */
+    public boolean isSuitableForTagSearch() {
+      return this.suitableForTagSearch;
+    }
+    
+    /**
+     * Not all resource types are suitable for filtering - for example, there are no
+     * filters available for service providers in BioCatalogue.
+     * 
+     * @return <code>true</code> indicates that tab dedicated to displaying search
+     *         results of this resource type can have a filter tree.
+     */
+    public boolean isSuitableForFiltering() {
+      return this.suitableForFiltering;
+    }
+    
+    /**
+     * @return <code>true</code> indicates that "Preview" option can be made
+     *         available for items of this type, as preview factory would be implemented
+     *         for such resources.
+     */
+    public boolean isSuitableForOpeningInPreviewBrowser() {
+      return this.suitableForOpeningInPreviewBrowser;
+    }
+    
+    public boolean isSuitableForAddingToServicePanel() {
+      return this.suitableForAddingToServicePanel;
+    }
+    
+    public boolean isSuitableForAddingToWorkflowDiagram() {
+      return this.suitableForAddingToWorkflowDiagram;
+    }
+    
+    /**
+     * @return <code>true</code> indicates that monitoring data can be obtained
+     *         from BioCatalougue for this type of resource.
+     */
+    public boolean isSuitableForHealthCheck() {
+      return this.suitableForHealthCheck;
+    }
+    
+    
+    /**
+     * This method helps to defer instantiation of ListCellRenderers
+     * until they are first accessed - it is because construction of
+     * the renderers requires knowledge of all available resource types,
+     * therefore they cannot be instantiated until after Resource class
+     * has been fully loaded.
+     * 
+     * @return {@link ListCellRenderer} for this type of resources or
+     *         <code>null</code> if an error has occurred during
+     *         instantiation of required renderer.
+     */
+    public ListCellRenderer getResultListingCellRenderer() {
+      try {
+        return this.resultListingCellRendererClass.newInstance();
+      }
+      catch (Exception e) {
+        Logger.getLogger(Resource.class).error("Unable to instantiate search results ListCellRenderer for " +
+                                               this.getCollectionName(), e);
+        JOptionPane.showMessageDialog(MainWindow.getMainWindow(), 
+            "Taverna was unable to instantiate ListCellRenderer for " + this.getCollectionName() + ".\n\n" +
+            "This may make Taverna unstable.", "Service Catalogue Plugin", JOptionPane.ERROR_MESSAGE);
+        return null;
+      }
+    }
+    
+    /**
+     * @return URL in the BioCatalogue API that provides an index of the collection of
+     *         all resources of this type.
+     */
+    public String getAPIResourceCollectionIndex() {
+      return apiResourceCollectionIndex;
+    }
+    
+    /**
+     * @return Keys and values for any additional URL parameters that need to be included into the
+     *         BioCatalogue API requests that are made in order to fetch all necessary additional
+     *         details for a *single* expanded entry in the search results listing. 
+     */
+    public Map<String,String> getResourceCollectionIndexSingleExpandedResourceAdditionalParameters() {
+      return apiResourceCollectionIndexSingleExpandedResourceAdditionalParameters;
+    }
+    
+    /**
+     * @return Keys and values for any additional URL parameters that need to be included into the
+     *         requests sent to filtered indexes of collections of this type in the BioCatalogue API.
+     */
+    public Map<String,String> getAPIResourceCollectionIndexAdditionalParameters() {
+      return apiResourceCollectionIndexAdditionalParameters;
+    }
+    
+    /**
+     * @return Number of resources of this type that one page of search results from
+     *         the API will contain.
+     */
+    public int getApiResourceCountPerIndexPage() {
+      return apiResourceCountPerIndexPage;
+    }
+    
+    /**
+     * @return BioCatalogue API URL that provides a collection of filters for the
+     *         resource of this type. 
+     */
+    public String getAPIResourceCollectionFiltersURL() {
+      return apiResourceCollectionFilters;
+    }
+    
+    
+    /**
+     * This method is useful for adding / removing tabs into the results view - provides
+     * and index for the tabbed view to place a tab, relevant to a particular resource type.
+     * This helps to preserve the order of tabs after adding / removing them.
+     * 
+     * @return Zero-based index of this resource type in the <code>RESOURCE_TYPE</code> enum or 
+     *         <code>-1</code> if not found (which is impossible under normal conditions).
+     */
+    public int index()
+    {
+      TYPE[] values = TYPE.values();
+      for (int i = 0; i < values.length; i++) {
+        if (this == values[i]) {
+          return (i);
+        }
+      }
+      return (-1);
+    }
+
+
+
+	/**
+	 * @return the suitableForAddingAllToServicePanel
+	 */
+	public boolean isSuitableForAddingAllToServicePanel() {
+		return suitableForAddingAllToServicePanel;
+	}
+    
+  };
+  
+  
+  
+  // ----------------------------- RESOURCE CLASS -------------------------------
+  
+  
+  // current resource data
+  private final TYPE resourceType;
+  private final String resourceURL;
+  private final String resourceTitle;
+  
+  
+  public Resource(String resourceURL, String resourceTitle)
+  {
+    this.resourceURL = extractPureResourceURLFromPreviewActionCommand(resourceURL);
+    this.resourceTitle = resourceTitle;
+    this.resourceType = getResourceTypeFromResourceURL(resourceURL);
+  }
+  
+  public TYPE getType() {
+    return resourceType;
+  }
+  
+  public String getURL() {
+    return resourceURL;
+  }
+
+  public String getTitle() {
+    return resourceTitle;
+  }
+  
+  
+  
+  public boolean equals(Object other)
+  {
+    if (other instanceof Resource)
+    {
+      // compare by all components
+      Resource otherRes = (Resource)other;
+      return (this.resourceType == otherRes.resourceType &&
+              this.resourceTitle.equals(otherRes.resourceTitle) &&
+              this.resourceURL.equals(otherRes.resourceURL));
+    }
+    else {
+      // other object is of different type
+      return (false);
+    }
+  }
+  
+  
+  /**
+   * @param url Either URL of the resource in BioCatalogue or preview action command
+   *            ({@link BioCataloguePluginConstants#ACTION_PREVIEW_RESOURCE}).
+   * @return Type of this resource according to the BioCatalogue URL that points to this
+   *         resource or <code>null</code> if the type of the resource couldn't be determined.
+   */
+  public static TYPE getResourceTypeFromResourceURL(String url)
+  {
+    String pureURL = extractPureResourceURLFromPreviewActionCommand(url);
+    
+//    if (pureURL.startsWith(BioCatalogueClient.API_SERVICES_URL))               return(TYPE.Service);
+//    else
+    if (pureURL.startsWith(BioCatalogueClient.API_SOAP_OPERATIONS_URL)) {
+    	return(TYPE.SOAPOperation);
+    }
+    if (pureURL.startsWith(BioCatalogueClient.API_REST_METHODS_URL)) {
+    	return(TYPE.RESTMethod);
+    }
+//    else if (pureURL.startsWith(BioCatalogueClient.API_SERVICE_PROVIDERS_URL)) return(TYPE.ServiceProvider);   // TODO - re-enable these lines as soon as ServiceProvider and User type are started to be used
+//    else if (pureURL.startsWith(BioCatalogueClient.API_USERS_URL))             return(TYPE.User);
+      return (null);
+  }
+  
+  
+  /**
+   * @param previewActionCommand Either resource preview action command or a 'pure' resource URL already.
+   * @return A "pure" resource URL in BioCatalogue with the action prefix
+   *         ({@link BioCataloguePluginConstants#ACTION_PREVIEW_RESOURCE}) removed. 
+   */
+  public static String extractPureResourceURLFromPreviewActionCommand(String previewActionCommand)
+  {
+    return (previewActionCommand.startsWith(BioCataloguePluginConstants.ACTION_PREVIEW_RESOURCE) ?
+            previewActionCommand.substring(BioCataloguePluginConstants.ACTION_PREVIEW_RESOURCE.length()) :
+            previewActionCommand);
+  }
+  
+  
+  /**
+   * @param resource
+   * @return Display name for listings of items.
+   */
+  public static String getDisplayNameForResource(ResourceLink resource)
+  {
+    if (resource instanceof SoapOperation) {
+      return ((SoapOperation)resource).getName();
+    }
+    else if (resource instanceof RestMethod)
+    {
+      RestMethod restMethod = (RestMethod)resource;
+      return (restMethod.getName() == null || restMethod.getName().length() == 0 ?
+              restMethod.getEndpointLabel() :
+              restMethod.getName());
+    }
+    else if (resource instanceof Service) {
+      return ((Service)resource).getName();
+    }
+    else if (resource instanceof ServiceProvider) {
+      return ((ServiceProvider)resource).getName();
+    }
+    else if (resource instanceof User) {
+      return ((User)resource).getName();
+    }
+    else if (resource instanceof Registry) {
+      return ((Registry)resource).getName();
+    }
+    else if (resource instanceof LoadingResource) {
+      return (resource.getResourceName());
+    }
+    else {
+      return ("ERROR: ITEM NOT RECOGNISED - Item is of known generic type from the Service Catalogue Plugin, but not specifically recognised" + resource.toString());
+    }
+  }
+  
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ViewShowMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ViewShowMenuSection.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ViewShowMenuSection.java
new file mode 100644
index 0000000..2df05e5
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ViewShowMenuSection.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.ui.impl.menu;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author Alex Nenadic
+ * @author Alan R Williams
+ */
+public class ViewShowMenuSection extends AbstractMenuSection {
+	public static final URI DIAGRAM_MENU = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#diagram");
+	public static final URI VIEW_SHOW_MENU_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#viewShowMenuSection");
+
+	public ViewShowMenuSection() {
+		super(DIAGRAM_MENU, 10, VIEW_SHOW_MENU_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuManager
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuManager b/taverna-menu-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuManager
new file mode 100644
index 0000000..3b06fd9
--- /dev/null
+++ b/taverna-menu-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuManager
@@ -0,0 +1 @@
+net.sf.taverna.t2.ui.menu.impl.MenuManagerImpl
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/resources/META-INF/spring/menu-impl-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/resources/META-INF/spring/menu-impl-context-osgi.xml b/taverna-menu-impl/src/main/resources/META-INF/spring/menu-impl-context-osgi.xml
new file mode 100644
index 0000000..3a1eadf
--- /dev/null
+++ b/taverna-menu-impl/src/main/resources/META-INF/spring/menu-impl-context-osgi.xml
@@ -0,0 +1,26 @@
+<?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="MenuManagerImpl" interface="net.sf.taverna.t2.ui.menu.MenuManager" />
+
+	<service ref="FileMenu" auto-export="interfaces" />
+	<service ref="EditMenu" auto-export="interfaces" />
+	<service ref="AdvancedMenu" auto-export="interfaces" />
+	<service ref="HelpMenu" auto-export="interfaces" />
+	<service ref="OnlineHelpMenuAction" auto-export="interfaces" />
+	<service ref="FeedbackMenuAction" auto-export="interfaces" />
+	<service ref="ShowLogsAndDataMenuAction" auto-export="interfaces" />
+
+	<reference id="applicationConfiguration" interface="uk.org.taverna.configuration.app.ApplicationConfiguration" />
+	<reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" />
+
+	<list id="menuComponents" interface="net.sf.taverna.t2.ui.menu.MenuComponent" cardinality="0..N" greedy-proxying="true">
+		<listener ref="MenuManagerImpl" bind-method="update" unbind-method="update" />
+	</list>
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/resources/META-INF/spring/menu-impl-context.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/resources/META-INF/spring/menu-impl-context.xml b/taverna-menu-impl/src/main/resources/META-INF/spring/menu-impl-context.xml
new file mode 100644
index 0000000..62fd24e
--- /dev/null
+++ b/taverna-menu-impl/src/main/resources/META-INF/spring/menu-impl-context.xml
@@ -0,0 +1,25 @@
+<?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="MenuManagerImpl" class="net.sf.taverna.t2.ui.menu.impl.MenuManagerImpl">
+		<property name="menuComponents" ref="menuComponents" />
+		<property name="selectionManager" ref="selectionManager" />
+	</bean>
+
+	<bean id="FileMenu" class="net.sf.taverna.t2.workbench.ui.impl.menu.FileMenu" />
+	<bean id="EditMenu" class="net.sf.taverna.t2.workbench.ui.impl.menu.EditMenu" />
+	<bean id="AdvancedMenu" class="net.sf.taverna.t2.workbench.ui.impl.menu.AdvancedMenu" />
+	<bean id="HelpMenu" class="net.sf.taverna.t2.workbench.ui.impl.menu.HelpMenu" />
+	<bean id="OnlineHelpMenuAction"
+		class="net.sf.taverna.t2.workbench.ui.impl.menu.OnlineHelpMenuAction" />
+	<bean id="FeedbackMenuAction"
+		class="net.sf.taverna.t2.workbench.ui.impl.menu.FeedbackMenuAction" />
+	<bean id="ShowLogsAndDataMenuAction"
+		class="net.sf.taverna.t2.workbench.ui.impl.menu.ShowLogsAndDataMenuAction">
+		<property name="applicationConfiguration" ref="applicationConfiguration" />
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-items/pom.xml b/taverna-menu-items/pom.xml
new file mode 100644
index 0000000..07566e2
--- /dev/null
+++ b/taverna-menu-items/pom.xml
@@ -0,0 +1,88 @@
+<?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>menu-items</artifactId>
+	<packaging>bundle</packaging>
+	<name>Additional menu items</name>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>menu-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>report-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>selection-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>design-ui</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>graph-view</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>workflow-view</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.taverna.t2.lang</groupId>
+			<artifactId>ui</artifactId>
+			<version>${t2.lang.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>uk.org.taverna.commons</groupId>
+			<artifactId>taverna-services-api</artifactId>
+			<version>0.1.0-SNAPSHOT</version>
+		</dependency>
+
+		<dependency>
+			<groupId>net.sf.taverna.t2.ui-activities</groupId>
+			<artifactId>stringconstant-activity-ui</artifactId>
+			<version>${t2.ui.activities.version}</version>
+		</dependency>
+		<!-- TODO remove dependencies on implementations -->
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>contextual-views-impl</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AbstractConnectPortMenuActions.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AbstractConnectPortMenuActions.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AbstractConnectPortMenuActions.java
new file mode 100644
index 0000000..2bbed5b
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AbstractConnectPortMenuActions.java
@@ -0,0 +1,268 @@
+package net.sf.taverna.t2.ui.menu.items.activityport;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.NamedSet;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputPort;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+import uk.org.taverna.scufl2.api.port.Port;
+import uk.org.taverna.scufl2.api.port.ProcessorPort;
+import uk.org.taverna.scufl2.api.port.ReceiverPort;
+import uk.org.taverna.scufl2.api.port.SenderPort;
+import uk.org.taverna.scufl2.api.port.WorkflowPort;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.menu.AbstractMenuCustom;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.ui.menu.MenuManager.ComponentFactory;
+import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager;
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+public abstract class AbstractConnectPortMenuActions extends AbstractMenuCustom
+		implements ContextualMenuComponent {
+
+	protected ActivityIconManager activityIconManager;
+	protected ContextualSelection contextualSelection;
+	protected MenuManager menuManager;
+	protected WorkbenchConfiguration workbenchConfiguration;
+	protected ColourManager colourManager;
+	private EditManager editManager;
+
+	public static final String CONNECT_AS_INPUT_TO = "Connect as input to...";
+	public static final String CONNECT_WITH_OUTPUT_FROM = "Connect with output from...";
+
+	public static final String SERVICE_INPUT_PORTS = "Service input ports";
+	public static final String SERVICE_OUTPUT_PORTS = "Service output ports";
+
+	public static final String NEW_WORKFLOW_INPUT_PORT = "New workflow input port...";
+	public static final String NEW_WORKFLOW_OUTPUT_PORT = "New workflow output port...";
+
+	public static final String WORKFLOW_INPUT_PORTS = "Workflow input ports";
+	public static final String WORKFLOW_OUTPUT_PORTS = "Workflow output ports";
+
+	public static final String SERVICES = "Services";
+
+	private Scufl2Tools scufl2Tools  = new Scufl2Tools();
+
+	public AbstractConnectPortMenuActions(URI parentId, int positionHint) {
+		super(parentId, positionHint);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+		this.customComponent = null;
+	}
+
+	@Override
+	protected Component createCustomComponent() {
+		Workflow workflow = (Workflow) getContextualSelection().getParent();
+		Profile profile = workflow.getParent().getMainProfile();
+		Port port = getSelectedPort();
+		// Component component =
+		// getContextualSelection().getRelativeToComponent();
+
+		String label;
+		if (port instanceof ReceiverPort) {
+			label = CONNECT_WITH_OUTPUT_FROM;
+		} else {
+			label = CONNECT_AS_INPUT_TO;
+		}
+		JMenu connectMenu = new JMenu(new DummyAction(label,
+				WorkbenchIcons.datalinkIcon));
+		addPortMenuItems(workflow, port, connectMenu);
+		addProcessorMenuItems(workflow, profile, port, connectMenu);
+		return connectMenu;
+	}
+
+	private Port getSelectedPort() {
+		Port port = (Port) getContextualSelection().getSelection();
+		return port;
+	}
+
+	protected void addPortMenuItems(Workflow workflow, Port port, JMenu connectMenu) {
+		Color workflowPortColour = colourManager.getPreferredColour(WorkflowPort.class.getCanonicalName());
+
+		boolean addedPorts = false;
+		if (port instanceof SenderPort) {
+			connectMenu.add(new ShadedLabel(WORKFLOW_OUTPUT_PORTS, workflowPortColour));
+			for (OutputWorkflowPort outputWorkflowPort : workflow.getOutputPorts()) {
+				ConnectPortsAction connectPortsAction =
+						new ConnectPortsAction(workflow, (SenderPort) port, outputWorkflowPort, editManager);
+				connectPortsAction.putValue(Action.SMALL_ICON, WorkbenchIcons.outputIcon);
+				connectPortsAction.putValue(Action.NAME, outputWorkflowPort.getName());
+				connectMenu.add(new JMenuItem(connectPortsAction));
+				addedPorts = true;
+			}
+		} else if (port instanceof ReceiverPort) {
+			connectMenu.add(new ShadedLabel(WORKFLOW_INPUT_PORTS, workflowPortColour));
+			for (InputWorkflowPort inputWorkflowPort : workflow.getInputPorts()) {
+				ConnectPortsAction connectPortsAction =
+						new ConnectPortsAction(workflow, inputWorkflowPort, (ReceiverPort) port, editManager);
+				connectPortsAction.putValue(Action.SMALL_ICON, WorkbenchIcons.inputIcon);
+				connectPortsAction.putValue(Action.NAME, inputWorkflowPort.getName());
+				connectMenu.add(new JMenuItem(connectPortsAction));
+				addedPorts = true;
+			}
+		}
+		if (addedPorts) {
+			connectMenu.addSeparator();
+		}
+		CreateAndConnectDataflowPortAction newDataflowPortAction = new CreateAndConnectDataflowPortAction(
+				workflow, port, getSuggestedName(port), contextualSelection.getRelativeToComponent(), editManager);
+
+		if (port instanceof ReceiverPort) {
+			newDataflowPortAction.putValue(Action.NAME, NEW_WORKFLOW_INPUT_PORT);
+		} else if (port instanceof SenderPort) {
+			newDataflowPortAction.putValue(Action.NAME, NEW_WORKFLOW_OUTPUT_PORT);
+		}
+		newDataflowPortAction.putValue(Action.SMALL_ICON, WorkbenchIcons.newIcon);
+		connectMenu.add(new JMenuItem(newDataflowPortAction));
+	}
+
+	/**
+	 * @param port
+	 * @return
+	 */
+	private String getSuggestedName(Port port) {
+		String suggestedName;
+		if (port instanceof ProcessorPort) {
+			suggestedName = ((ProcessorPort) port).getParent().getName() + "_" + port.getName();
+		} else {
+			suggestedName = port.getName();
+		}
+		return suggestedName;
+	}
+
+	protected void addProcessorMenuItems(Workflow dataflow, Profile profile,
+			final Port targetPort, JMenu connectMenu) {
+		final Set<Processor> processors = findProcessors(dataflow, targetPort);
+		if (processors.isEmpty()) {
+			return;
+		}
+		connectMenu.add(new ShadedLabel(SERVICES, colourManager.getPreferredColour(Processor.class.getCanonicalName())));
+
+		List<JMenuItem> menuItems = new ArrayList<JMenuItem>();
+		for (Processor processor : processors) {
+			Activity activity = scufl2Tools.processorBindingForProcessor(processor, profile).getBoundActivity();
+			Icon icon = activityIconManager.iconForActivity(activity);
+			final Color processorPortColour = colourManager.getPreferredColour(ProcessorPort.class.getCanonicalName());
+
+			JMenu processorMenu = new JMenu(new DummyAction(processor.getName(), icon));
+			List<JMenuItem> processorMenuItems = new ArrayList<JMenuItem>();
+			if (targetPort instanceof ReceiverPort) {
+				processorMenu.add(new ShadedLabel(SERVICE_OUTPUT_PORTS,
+						processorPortColour));
+				menuItems.add(processorMenu);
+				for (OutputProcessorPort outputProcessorPort : processor.getOutputPorts()) {
+					ConnectPortsAction connectPortsAction = new ConnectPortsAction(dataflow,
+							outputProcessorPort, (ReceiverPort) targetPort, editManager);
+					connectPortsAction.putValue(Action.SMALL_ICON,
+							WorkbenchIcons.outputPortIcon);
+					connectPortsAction.putValue(Action.NAME, outputProcessorPort.getName());
+					processorMenuItems.add(new JMenuItem(connectPortsAction));
+				}
+			} else if (targetPort instanceof SenderPort) {
+				processorMenu.add(new ShadedLabel(SERVICE_INPUT_PORTS,
+						processorPortColour));
+				menuItems.add(processorMenu);
+				for (InputProcessorPort inputProcessorPort : processor.getInputPorts()) {
+					ConnectPortsAction connectPortsAction = new ConnectPortsAction(dataflow,
+							(SenderPort) targetPort, inputProcessorPort, editManager);
+					connectPortsAction.putValue(Action.SMALL_ICON,
+							WorkbenchIcons.inputPortIcon);
+					connectPortsAction.putValue(Action.NAME, inputProcessorPort.getName());
+					processorMenuItems.add(new JMenuItem(connectPortsAction));
+				}
+			}
+
+			menuManager.addMenuItemsWithExpansion(processorMenuItems,
+					processorMenu, workbenchConfiguration.getMaxMenuItems(),
+					new ComponentFactory() {
+						public Component makeComponent() {
+							if (targetPort instanceof InputPort) {
+								return new ShadedLabel(SERVICE_OUTPUT_PORTS, processorPortColour);
+							} else {
+								return new ShadedLabel(SERVICE_INPUT_PORTS, processorPortColour);
+							}
+						}
+					});
+		}
+		menuManager.addMenuItemsWithExpansion(menuItems, connectMenu,
+				workbenchConfiguration.getMaxMenuItems(),
+				new ComponentFactory() {
+					public Component makeComponent() {
+						return new ShadedLabel(SERVICES, colourManager
+								.getPreferredColour(Processor.class
+										.getCanonicalName()));
+					}
+				});
+	}
+
+	protected Set<Processor> findProcessors(Workflow dataflow, Port targetPort) {
+		Set<Processor> possibleProcessors = new HashSet<Processor>();
+		if (targetPort instanceof InputProcessorPort) {
+			InputProcessorPort inputProcessorPort = (InputProcessorPort) targetPort;
+			possibleProcessors = scufl2Tools.possibleUpStreamProcessors(dataflow, inputProcessorPort.getParent());
+		} else if (targetPort instanceof OutputProcessorPort) {
+			OutputProcessorPort outputProcessorPort = (OutputProcessorPort) targetPort;
+			possibleProcessors = scufl2Tools.possibleDownStreamProcessors(dataflow, outputProcessorPort.getParent());
+		} else {
+			// Probably a dataflow port, everything is allowed
+			possibleProcessors = dataflow.getProcessors();
+		}
+		return possibleProcessors;
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setMenuManager(MenuManager menuManager) {
+		this.menuManager = menuManager;
+	}
+
+	public void setActivityIconManager(ActivityIconManager activityIconManager) {
+		this.activityIconManager = activityIconManager;
+	}
+
+	public void setWorkbenchConfiguration(WorkbenchConfiguration workbenchConfiguration) {
+		this.workbenchConfiguration = workbenchConfiguration;
+	}
+
+	public void setColourManager(ColourManager colourManager) {
+		this.colourManager = colourManager;
+	}
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityInputPortSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityInputPortSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityInputPortSection.java
new file mode 100644
index 0000000..89414d6
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityInputPortSection.java
@@ -0,0 +1,67 @@
+/**********************************************************************
+ * 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.ui.menu.items.activityport;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DefaultContextualMenu;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+
+public class ActivityInputPortSection extends AbstractMenuSection implements
+		ContextualMenuComponent {
+
+	private static final String ACTIVITY_INPUT_PORT = "Service input port: ";
+	public static final URI activityInputPortSection = URI
+			.create("http://taverna.sf.net/2009/contextMenu/activityInputPort");
+	private ContextualSelection contextualSelection;
+
+	public ActivityInputPortSection() {
+		super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10,
+				activityInputPortSection);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return getContextualSelection().getSelection() instanceof InputProcessorPort;
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+		this.action = null;
+	}
+
+	@Override
+	protected Action createAction() {
+		InputProcessorPort port = (InputProcessorPort) getContextualSelection().getSelection();
+		String name = ACTIVITY_INPUT_PORT + port.getName();
+		return new DummyAction(name);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityOutputPortSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityOutputPortSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityOutputPortSection.java
new file mode 100644
index 0000000..b26cff9
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityOutputPortSection.java
@@ -0,0 +1,67 @@
+/**********************************************************************
+ * 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.ui.menu.items.activityport;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DefaultContextualMenu;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+
+public class ActivityOutputPortSection extends AbstractMenuSection implements
+		ContextualMenuComponent {
+
+	private static final String ACTIVITY_OUTPUT_PORT = "Service output port: ";
+	public static final URI activityOutputPortSection = URI
+			.create("http://taverna.sf.net/2009/contextMenu/activityOutputPort");
+	private ContextualSelection contextualSelection;
+
+	public ActivityOutputPortSection() {
+		super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10,
+				activityOutputPortSection);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return getContextualSelection().getSelection() instanceof OutputProcessorPort;
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+		this.action = null;
+	}
+
+	@Override
+	protected Action createAction() {
+		OutputProcessorPort port = (OutputProcessorPort) getContextualSelection().getSelection();
+		String name = ACTIVITY_OUTPUT_PORT + port.getName();
+		return new DummyAction(name);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AddInputPortDefaultValueAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AddInputPortDefaultValueAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AddInputPortDefaultValueAction.java
new file mode 100644
index 0000000..414ffac
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AddInputPortDefaultValueAction.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * 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.ui.menu.items.activityport;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+import net.sf.taverna.t2.activities.stringconstant.views.StringConstantConfigView;
+import net.sf.taverna.t2.workbench.design.actions.DataflowEditAction;
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workflow.edits.AddChildEdit;
+import net.sf.taverna.t2.workflow.edits.AddDataLinkEdit;
+import net.sf.taverna.t2.workflow.edits.AddProcessorEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.commons.services.ServiceRegistry;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.iterationstrategy.CrossProduct;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.port.OutputActivityPort;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+/**
+ * Action for adding a default value to an input port of a processor.
+ *
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class AddInputPortDefaultValueAction extends DataflowEditAction {
+
+	private static Logger logger = Logger.getLogger(AddInputPortDefaultValueAction.class);
+
+	private static final URI STRING_CONSTANT = URI
+			.create("http://ns.taverna.org.uk/2010/activity/constant");
+
+	private InputProcessorPort inputPort;
+
+	private final ServiceRegistry serviceRegistry;
+
+	public AddInputPortDefaultValueAction(Workflow workflow, InputProcessorPort inputPort,
+			Component component, EditManager editManager, SelectionManager selectionManager,
+			ServiceRegistry serviceRegistry) {
+		super(workflow, component, editManager, selectionManager);
+		this.inputPort = inputPort;
+		this.serviceRegistry = serviceRegistry;
+		putValue(SMALL_ICON, WorkbenchIcons.inputValueIcon);
+		putValue(NAME, "Set constant value");
+	}
+
+	public void actionPerformed(ActionEvent e) {
+		try {
+			Activity activity = new Activity();
+			activity.setType(STRING_CONSTANT);
+			Configuration configuration = new Configuration();
+			configuration.setType(STRING_CONSTANT.resolve("#Config"));
+			configuration.getJsonAsObjectNode().put("string", "");
+			configuration.setConfigures(activity);
+
+			StringConstantConfigView configView = new StringConstantConfigView(activity,
+					configuration, serviceRegistry);
+
+			int answer = JOptionPane.showConfirmDialog(component, configView,
+					"Text constant value", JOptionPane.OK_CANCEL_OPTION);
+			if (answer != JOptionPane.CANCEL_OPTION) {
+
+				configView.noteConfiguration();
+				configuration.setJson(configView.getJson());
+
+				Profile profile = selectionManager.getSelectedProfile();
+
+				Processor processor = new Processor();
+				processor.setName(inputPort.getName() + "_value");
+
+				CrossProduct crossProduct = new CrossProduct();
+				crossProduct.setParent(processor.getIterationStrategyStack());
+
+				ProcessorBinding processorBinding = new ProcessorBinding();
+				processorBinding.setBoundProcessor(processor);
+				processorBinding.setBoundActivity(activity);
+
+				// create activity port
+				OutputActivityPort activityPort = new OutputActivityPort(activity, "value");
+				activityPort.setDepth(0);
+				activityPort.setGranularDepth(0);
+				// create processor port
+				OutputProcessorPort processorPort = new OutputProcessorPort(processor,
+						activityPort.getName());
+				processorPort.setDepth(0);
+				processorPort.setGranularDepth(0);
+				// add a new port binding
+				new ProcessorOutputPortBinding(processorBinding, activityPort, processorPort);
+
+				// Add a data link between the string constant processor's output port
+				// and the processor containing the passed inputPort.
+				DataLink datalink = new DataLink();
+				datalink.setReceivesFrom(processorPort);
+				datalink.setSendsTo(inputPort);
+
+				List<Edit<?>> editList = new ArrayList<Edit<?>>();
+				editList.add(new AddChildEdit<Profile>(profile, activity));
+				editList.add(new AddChildEdit<Profile>(profile, configuration));
+				editList.add(new AddChildEdit<Profile>(profile, processorBinding));
+				editList.add(new AddProcessorEdit(dataflow, processor));
+				editList.add(new AddDataLinkEdit(dataflow, datalink));
+
+				editManager.doDataflowEdit(dataflow.getParent(), new CompoundEdit(editList));
+
+			}
+		} catch (EditException ex) {
+			logger.error("Adding default value for input port failed", ex);
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectInputPortMenuActions.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectInputPortMenuActions.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectInputPortMenuActions.java
new file mode 100644
index 0000000..f22ebbc
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectInputPortMenuActions.java
@@ -0,0 +1,41 @@
+/**********************************************************************
+ * 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.ui.menu.items.activityport;
+
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+
+public class ConnectInputPortMenuActions extends AbstractConnectPortMenuActions
+		implements ContextualMenuComponent {
+
+	public ConnectInputPortMenuActions() {
+		super(ActivityInputPortSection.activityInputPortSection, 20);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof InputProcessorPort
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectOutputPortMenuActions.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectOutputPortMenuActions.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectOutputPortMenuActions.java
new file mode 100644
index 0000000..2bb3ec0
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectOutputPortMenuActions.java
@@ -0,0 +1,41 @@
+/**********************************************************************
+ * 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.ui.menu.items.activityport;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+
+
+public class ConnectOutputPortMenuActions extends AbstractConnectPortMenuActions  {
+
+	public ConnectOutputPortMenuActions() {
+		super(ActivityOutputPortSection.activityOutputPortSection, 20);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof OutputProcessorPort
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectPortsAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectPortsAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectPortsAction.java
new file mode 100644
index 0000000..f10fedf
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectPortsAction.java
@@ -0,0 +1,68 @@
+/**********************************************************************
+ * 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.ui.menu.items.activityport;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workflow.edits.AddDataLinkEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.ReceiverPort;
+import uk.org.taverna.scufl2.api.port.SenderPort;
+
+@SuppressWarnings("serial")
+public class ConnectPortsAction extends AbstractAction {
+	private static Logger logger = Logger.getLogger(ConnectPortsAction.class);
+	private final Workflow workflow;
+	private final ReceiverPort receiverPort;
+	private final SenderPort senderPort;
+	private final EditManager editManager;
+
+	public ConnectPortsAction(Workflow workflow,
+			SenderPort senderPort, ReceiverPort receiverPort, EditManager editManager) {
+		super("Connect " + senderPort.getName() + " to " + receiverPort.getName());
+		this.workflow = workflow;
+		this.receiverPort = receiverPort;
+		this.senderPort = senderPort;
+		this.editManager = editManager;
+	}
+
+	public void actionPerformed(ActionEvent e) {
+		DataLink dataLink = new DataLink();
+		dataLink.setReceivesFrom(senderPort);
+		dataLink.setSendsTo(receiverPort);
+		Edit<Workflow> edit = new AddDataLinkEdit(workflow, dataLink);
+		try {
+			editManager.doDataflowEdit(workflow.getParent(), edit);
+		} catch (EditException ex) {
+			logger.warn("Can't create connection between " + senderPort
+					+ " and " + receiverPort, ex);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/CreateAndConnectDataflowPortAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/CreateAndConnectDataflowPortAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/CreateAndConnectDataflowPortAction.java
new file mode 100644
index 0000000..534f4ca
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/CreateAndConnectDataflowPortAction.java
@@ -0,0 +1,226 @@
+/**********************************************************************
+ * 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.ui.menu.items.activityport;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog;
+import net.sf.taverna.t2.workbench.design.ui.DataflowInputPortPanel;
+import net.sf.taverna.t2.workbench.design.ui.DataflowOutputPortPanel;
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workflow.edits.AddChildEdit;
+import net.sf.taverna.t2.workflow.edits.AddDataLinkEdit;
+import net.sf.taverna.t2.workflow.edits.AddWorkflowInputPortEdit;
+import net.sf.taverna.t2.workflow.edits.AddWorkflowOutputPortEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.DepthPort;
+import uk.org.taverna.scufl2.api.port.InputPort;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import uk.org.taverna.scufl2.api.port.OutputPort;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+import uk.org.taverna.scufl2.api.port.Port;
+import uk.org.taverna.scufl2.api.port.ReceiverPort;
+import uk.org.taverna.scufl2.api.port.SenderPort;
+
+/**
+ * Action to create a dataflow input/output port and connect it to the specified
+ * processor/activity output/input port.
+ * <p>
+ * The created dataflow port name will be taken from the name of the provided
+ * port.
+ *
+ * @author Stian Soiland-Reyes
+ *
+ */
+@SuppressWarnings("serial")
+public class CreateAndConnectDataflowPortAction extends AbstractAction {
+
+	private static final String VALID_PORT_NAME_REGEX = "[\\p{L}\\p{Digit}_.]+";
+	private static final Dimension INPUT_PORT_DIALOGUE_SIZE = new Dimension(400, 250);
+	private static final Dimension OUTPUT_PORT_DIALOGUE_SIZE = new Dimension(400, 200);
+
+	private static final String INVALID_WORKFLOW_OUTPUT_PORT_NAME = "Invalid workflow output port name.";
+	private static final String DUPLICATE_WORKFLOW_OUTPUT_PORT_NAME = "Duplicate workflow output port name.";
+	private static final String SET_THE_WORKFLOW_OUTPUT_PORT_NAME = "Set the workflow output port name.";
+	private static final String ADD_WORKFLOW_OUTPUT_PORT = "Add workflow output port";
+	private static final String SET_THE_INPUT_PORT_LIST_DEPTH = "Set the input port list depth.";
+	private static final String SET_THE_INPUT_PORT_TYPE = "Set the input port type.";
+	private static final String INVALID_WORKFLOW_INPUT_PORT_NAME = "Invalid workflow input port name.";
+	private static final String DUPLICATE_WORKFLOW_INPUT_PORT_NAME = "Duplicate workflow input port name.";
+	private static final String SET_THE_WORKFLOW_INPUT_PORT_NAME = "Set the workflow input port name.";
+	private static final String ADD_WORKFLOW_INPUT_PORT = "Add workflow input port";
+	private static Logger logger = Logger.getLogger(CreateAndConnectDataflowPortAction.class);
+	private final Workflow workflow;
+
+	private final Port port;
+	private final String suggestedName;
+	private final Component parentComponent;
+	private final EditManager editManager;
+
+	/**
+	 * Action for creating a Workflow input/output port and linking it to the
+	 * specified port.
+	 * <p>
+	 * If the provided port is an InputPort then a
+	 * Workflow OutputPort will be created and linked. Vice versa, if the
+	 * provided port is an OutputPort, a Workflow InputPort will be created.
+	 *
+	 * @param workflow
+	 *            Workflow where to create the Workflow input/output port
+	 * @param port
+	 *            Existing Processor port to connect to
+	 * @param suggestedName
+	 *            suggested port name
+	 * @param parentComponent
+	 *            Component to be parent of any pop-ups
+	 */
+	public CreateAndConnectDataflowPortAction(Workflow workflow, Port port,
+			String suggestedName, Component parentComponent, EditManager editManager) {
+		super("Connect to new workflow port");
+		this.workflow = workflow;
+		this.port = port;
+		this.suggestedName = suggestedName;
+		this.parentComponent = parentComponent;
+		this.editManager = editManager;
+		if (!(port instanceof InputPort || port instanceof OutputPort)) {
+			throw new IllegalArgumentException("Port " + port
+					+ " must be either an InputPort or OutputPort");
+		}
+	}
+
+	public void actionPerformed(ActionEvent e) {
+		if (port instanceof ReceiverPort) {
+			InputWorkflowPort inputWorkflowPort = new InputWorkflowPort();
+			inputWorkflowPort.setName(suggestedName);
+			workflow.getInputPorts().addWithUniqueName(inputWorkflowPort);
+			workflow.getInputPorts().remove(inputWorkflowPort);
+			if (port instanceof DepthPort) {
+				inputWorkflowPort.setDepth(((DepthPort) port).getDepth());
+			} else {
+				inputWorkflowPort.setDepth(0);
+			}
+			showDialogue(inputWorkflowPort);
+
+		} else if (port instanceof SenderPort) {
+			OutputWorkflowPort outputWorkflowPort = new OutputWorkflowPort();
+			outputWorkflowPort.setName(suggestedName);
+			workflow.getOutputPorts().addWithUniqueName(outputWorkflowPort);
+			workflow.getOutputPorts().remove(outputWorkflowPort);
+			showDialogue(outputWorkflowPort);
+		} else {
+			throw new IllegalStateException("Port " + port
+					+ " must be either an InputPort or OutputPort");
+		}
+
+	}
+
+	protected void showDialogue(InputWorkflowPort portTemplate) {
+		Set<String> usedInputPorts = new HashSet<String>();
+		for (InputWorkflowPort usedInputPort : workflow.getInputPorts()) {
+			usedInputPorts.add(usedInputPort.getName());
+		}
+		DataflowInputPortPanel inputPanel = new DataflowInputPortPanel();
+
+		ValidatingUserInputDialog vuid = new ValidatingUserInputDialog(
+				ADD_WORKFLOW_INPUT_PORT, inputPanel);
+		vuid.addTextComponentValidation(inputPanel.getPortNameField(),
+				SET_THE_WORKFLOW_INPUT_PORT_NAME, usedInputPorts,
+				DUPLICATE_WORKFLOW_INPUT_PORT_NAME, VALID_PORT_NAME_REGEX,
+				INVALID_WORKFLOW_INPUT_PORT_NAME);
+		vuid.addMessageComponent(inputPanel.getSingleValueButton(),
+				SET_THE_INPUT_PORT_TYPE);
+		vuid.addMessageComponent(inputPanel.getListValueButton(),
+				SET_THE_INPUT_PORT_LIST_DEPTH);
+		vuid.setSize(INPUT_PORT_DIALOGUE_SIZE);
+
+		inputPanel.setPortName(portTemplate.getName());
+		inputPanel.setPortDepth(portTemplate.getDepth());
+
+		if (vuid.show(parentComponent)) {
+			InputWorkflowPort inputWorkflowPort = new InputWorkflowPort();
+			inputWorkflowPort.setName(inputPanel.getPortName());
+			inputWorkflowPort.setDepth(inputPanel.getPortDepth());
+			List<Edit<?>> editList = new ArrayList<Edit<?>>();
+			editList.add(new AddWorkflowInputPortEdit(workflow, inputWorkflowPort));
+			DataLink dataLink = new DataLink();
+			dataLink.setReceivesFrom(inputWorkflowPort);
+			dataLink.setSendsTo((ReceiverPort) port);
+			editList.add(new AddDataLinkEdit(workflow, dataLink));
+			try {
+				CompoundEdit compoundEdit = new CompoundEdit(editList);
+				editManager.doDataflowEdit(workflow.getParent(), compoundEdit);
+			} catch (EditException ex) {
+				logger.warn("Can't create or connect new input port", ex);
+			}
+
+		}
+	}
+
+	protected void showDialogue(OutputWorkflowPort portTemplate) {
+		Set<String> usedOutputPorts = new HashSet<String>();
+		for (OutputWorkflowPort usedInputPort : workflow.getOutputPorts()) {
+			usedOutputPorts.add(usedInputPort.getName());
+		}
+		DataflowOutputPortPanel outputPanel = new DataflowOutputPortPanel();
+
+		ValidatingUserInputDialog vuid = new ValidatingUserInputDialog(
+				ADD_WORKFLOW_OUTPUT_PORT, outputPanel);
+		vuid.addTextComponentValidation(outputPanel.getPortNameField(),
+				SET_THE_WORKFLOW_OUTPUT_PORT_NAME, usedOutputPorts,
+				DUPLICATE_WORKFLOW_OUTPUT_PORT_NAME,
+				VALID_PORT_NAME_REGEX, INVALID_WORKFLOW_OUTPUT_PORT_NAME);
+		vuid.setSize(OUTPUT_PORT_DIALOGUE_SIZE);
+		outputPanel.setPortName(portTemplate.getName());
+
+		if (vuid.show(parentComponent)) {
+			OutputWorkflowPort outputWorkflowPort = new OutputWorkflowPort();
+			outputWorkflowPort.setName(outputPanel.getPortName());
+			List<Edit<?>> editList = new ArrayList<Edit<?>>();
+			editList.add(new AddWorkflowOutputPortEdit(workflow, outputWorkflowPort));
+			DataLink dataLink = new DataLink();
+			dataLink.setReceivesFrom((SenderPort) port);
+			dataLink.setSendsTo(outputWorkflowPort);
+			editList.add(new AddDataLinkEdit(workflow, dataLink));
+			try {
+				CompoundEdit compoundEdit = new CompoundEdit(editList);
+				editManager.doDataflowEdit(workflow.getParent(), compoundEdit);
+			} catch (EditException ex) {
+				logger.warn("Can't create or connect new workflow output port", ex);
+			}
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetConstantInputPortValueMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetConstantInputPortValueMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetConstantInputPortValueMenuAction.java
new file mode 100644
index 0000000..30a91c6
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetConstantInputPortValueMenuAction.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.ui.menu.items.activityport;
+
+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.selection.SelectionManager;
+import uk.org.taverna.commons.services.ServiceRegistry;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+
+public class SetConstantInputPortValueMenuAction extends AbstractContextualMenuAction {
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+	private ServiceRegistry serviceRegistry;
+
+	public SetConstantInputPortValueMenuAction() {
+		super(ActivityInputPortSection.activityInputPortSection, 10);
+	}
+
+	@Override
+	public synchronized Action getAction() {
+		SetDefaultInputPortValueAction action = (SetDefaultInputPortValueAction) super.getAction();
+		action.updateStatus();
+		return action;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof InputProcessorPort
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new SetDefaultInputPortValueAction(editManager, selectionManager, serviceRegistry);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setServiceRegistry(ServiceRegistry serviceRegistry) {
+		this.serviceRegistry = serviceRegistry;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetDefaultInputPortValueAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetDefaultInputPortValueAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetDefaultInputPortValueAction.java
new file mode 100644
index 0000000..302aaf6
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetDefaultInputPortValueAction.java
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * 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.ui.menu.items.activityport;
+
+import java.awt.event.ActionEvent;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+import javax.swing.JOptionPane;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage;
+import net.sf.taverna.t2.workbench.selection.events.PerspectiveSelectionEvent;
+import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent;
+import uk.org.taverna.commons.services.ServiceRegistry;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+
+/**
+ * An action that sets a default value to a processor's input port, in case
+ * the input port is selected on the Graph View.
+ *
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class SetDefaultInputPortValueAction extends AbstractAction {
+
+	/* Current workflow's selection model event observer. */
+	private Observer<DataflowSelectionMessage> workflowSelectionObserver = new DataflowSelectionObserver();
+
+	private final EditManager editManager;
+	private final SelectionManager selectionManager;
+	private final ServiceRegistry serviceRegistry;
+
+	private Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	public SetDefaultInputPortValueAction(EditManager editManager,
+			SelectionManager selectionManager, ServiceRegistry serviceRegistry) {
+		super();
+		this.editManager = editManager;
+		this.selectionManager = selectionManager;
+		this.serviceRegistry = serviceRegistry;
+		putValue(SMALL_ICON, WorkbenchIcons.inputValueIcon);
+		putValue(NAME, "Constant value");
+		putValue(SHORT_DESCRIPTION, "Add a constant value for an input port");
+		setEnabled(false);
+
+		selectionManager.addObserver(new SelectionManagerObserver());
+
+	}
+
+	public void actionPerformed(ActionEvent e) {
+		WorkflowBundle workflowBundle = selectionManager.getSelectedWorkflowBundle();
+		DataflowSelectionModel dataFlowSelectionModel = selectionManager
+				.getDataflowSelectionModel(workflowBundle);
+		// Get selected port
+		Set<Object> selectedWFComponents = dataFlowSelectionModel.getSelection();
+		if (selectedWFComponents.size() > 1) {
+			JOptionPane.showMessageDialog(null,
+					"Only one workflow component should be selected for this action.", "Warning",
+					JOptionPane.WARNING_MESSAGE);
+		} else {
+			Object selectedWFComponent = selectedWFComponents.toArray()[0];
+			if (selectedWFComponent instanceof InputProcessorPort) {
+				new AddInputPortDefaultValueAction(workflowBundle.getMainWorkflow(),
+						(InputProcessorPort) selectedWFComponent, null, editManager,
+						selectionManager, serviceRegistry).actionPerformed(e);
+			}
+		}
+	}
+
+	/**
+	 * Check if action should be enabled or disabled and update its status.
+	 */
+	public void updateStatus() {
+		WorkflowBundle workflowBundle = selectionManager.getSelectedWorkflowBundle();
+		DataflowSelectionModel selectionModel = selectionManager
+				.getDataflowSelectionModel(workflowBundle);
+
+		// List of all selected objects in the graph view
+		Set<Object> selection = selectionModel.getSelection();
+
+		if (selection.isEmpty()) {
+			setEnabled(false);
+		} else {
+			// Take the first selected item - we only support single selections anyway
+			Object selected = selection.toArray()[0];
+			if (selected instanceof InputProcessorPort) {
+				// If this input port is not already connected to something - enable the button
+				setEnabled(scufl2Tools.datalinksTo((InputProcessorPort) selected).isEmpty());
+			}
+		}
+	}
+
+	/**
+	 * Observes events on workflow Selection Manager, i.e. when a workflow
+	 * node is selected in the graph view, and enables/disables this action accordingly.
+	 */
+	private final class DataflowSelectionObserver implements Observer<DataflowSelectionMessage> {
+
+		public void notify(Observable<DataflowSelectionMessage> sender,
+				DataflowSelectionMessage message) throws Exception {
+			updateStatus();
+		}
+	}
+
+	private final class SelectionManagerObserver extends SwingAwareObserver<SelectionManagerEvent> {
+
+		private static final String DESIGN_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.design.DesignPerspective";
+
+		@Override
+		public void notifySwing(Observable<SelectionManagerEvent> sender,
+				SelectionManagerEvent message) {
+			if (message instanceof WorkflowBundleSelectionEvent) {
+				WorkflowBundleSelectionEvent workflowBundleSelectionEvent = (WorkflowBundleSelectionEvent) message;
+				WorkflowBundle oldFlow = workflowBundleSelectionEvent
+						.getPreviouslySelectedWorkflowBundle();
+				WorkflowBundle newFlow = workflowBundleSelectionEvent.getSelectedWorkflowBundle();
+				// Update the buttons status as current dataflow has changed
+				updateStatus();
+
+				// Remove the workflow selection model listener from the previous (if any)
+				// and add to the new workflow (if any)
+				if (oldFlow != null) {
+					selectionManager.getDataflowSelectionModel(oldFlow).removeObserver(
+							workflowSelectionObserver);
+				}
+
+				if (newFlow != null) {
+					selectionManager.getDataflowSelectionModel(newFlow).addObserver(
+							workflowSelectionObserver);
+				}
+			} else if (message instanceof PerspectiveSelectionEvent) {
+				PerspectiveSelectionEvent perspectiveSelectionEvent = (PerspectiveSelectionEvent) message;
+				if (DESIGN_PERSPECTIVE_ID.equals(perspectiveSelectionEvent.getSelectedPerspective().getID())) {
+					updateStatus();
+				} else {
+					setEnabled(false);
+				}
+			}
+		}
+
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/annotated/AnnotatedConfigureMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/annotated/AnnotatedConfigureMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/annotated/AnnotatedConfigureMenuAction.java
new file mode 100644
index 0000000..7933940
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/annotated/AnnotatedConfigureMenuAction.java
@@ -0,0 +1,77 @@
+/**********************************************************************
+ * 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.ui.menu.items.annotated;
+
+import java.awt.event.ActionEvent;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JOptionPane;
+
+import net.sf.taverna.t2.annotation.Annotated;
+import net.sf.taverna.t2.annotation.AnnotationBeanSPI;
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureSection;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.annotated.AnnotatedContextualView;
+
+public class AnnotatedConfigureMenuAction extends AbstractContextualMenuAction {
+	private static final String ANNOTATE = "Annotate...";
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+	private List<AnnotationBeanSPI> annotationBeans;
+
+	public AnnotatedConfigureMenuAction() {
+		super(ConfigureSection.configureSection, 40);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled() && (getContextualSelection().getSelection() instanceof Annotated);
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		return new AbstractAction(ANNOTATE) {
+			public void actionPerformed(ActionEvent e) {
+				AnnotatedContextualView view = new AnnotatedContextualView((Annotated) getContextualSelection().getSelection(),
+						editManager, selectionManager, annotationBeans);
+				JOptionPane.showMessageDialog(null, view.getMainFrame(), "Annotation", JOptionPane.PLAIN_MESSAGE);
+			}
+		};
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setAnnotationBeans(List<AnnotationBeanSPI> annotationBeans) {
+		this.annotationBeans = annotationBeans;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureRunningContextualMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureRunningContextualMenuSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureRunningContextualMenuSection.java
new file mode 100644
index 0000000..9c459e2
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureRunningContextualMenuSection.java
@@ -0,0 +1,50 @@
+/**********************************************************************
+ * 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.ui.menu.items.contextualviews;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenu;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+
+public class ConfigureRunningContextualMenuSection extends AbstractMenu
+implements ContextualMenuComponent {
+	public static final String CONFIGURE_RUNNING = "Configure running";
+	public static final URI configureRunningSection = URI
+	.create("http://taverna.sf.net/2009/contextMenu/configureRunning");
+	private ContextualSelection contextualSelection;
+
+	public ConfigureRunningContextualMenuSection() {
+		super(ConfigureSection.configureSection, 45, configureRunningSection, CONFIGURE_RUNNING);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return true;
+//		return super.isEnabled() && contextualSelection instanceof Processor;
+	}
+	
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureSection.java
new file mode 100644
index 0000000..0f1a17f
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureSection.java
@@ -0,0 +1,61 @@
+/**********************************************************************
+ * 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.ui.menu.items.contextualviews;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+
+/**
+ * Menu section containing the actions to add service templates, i.e. activities
+ * than are not readily runnable but need to be configured first. The actual actions that
+ * go into this menu can be found in the ui modules for the activities.
+ *
+ * @author Alex Nenadic
+ *
+ */
+public class ConfigureSection extends AbstractMenuSection
+		implements ContextualMenuComponent {
+
+	public static final URI configureSection = URI
+			.create("http://taverna.sf.net/2009/contextMenu/configure");
+	private ContextualSelection contextualSelection;
+
+	public ConfigureSection() {
+		super(EditSection.editSection, 100, configureSection);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		Object selection = getContextualSelection().getSelection();
+		return super.isEnabled();
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/EditSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/EditSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/EditSection.java
new file mode 100644
index 0000000..d23ec6e
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/EditSection.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.ui.menu.items.contextualviews;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DefaultContextualMenu;
+
+/**
+ * Menu section containing the actions to add service templates, i.e. activities
+ * than are not readily runnable but need to be configured first. The actual actions that
+ * go into this menu can be found in the ui modules for the activities.
+ *
+ * @author Alex Nenadic
+ *
+ */
+public class EditSection extends AbstractMenuSection
+		implements ContextualMenuComponent {
+
+	private static final String EDIT = "Edit";
+	public static final URI editSection = URI
+			.create("http://taverna.sf.net/2009/contextMenu/edit");
+	private ContextualSelection contextualSelection;
+
+	public EditSection() {
+		super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, editSection);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled();
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+	}
+
+	@Override
+	protected Action createAction() {
+		DummyAction action = new DummyAction(EDIT);
+		// Set the colour for the section
+		action.putValue(AbstractMenuSection.SECTION_COLOR, ShadedLabel.ORANGE);
+		return action;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/InsertSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/InsertSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/InsertSection.java
new file mode 100644
index 0000000..c1e21f6
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/InsertSection.java
@@ -0,0 +1,63 @@
+/**********************************************************************
+ * 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.ui.menu.items.contextualviews;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DefaultContextualMenu;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public class InsertSection extends AbstractMenuSection implements
+		ContextualMenuComponent {
+
+	private static final String INSERT = "Insert";
+	public static final URI insertSection = URI
+			.create("http://taverna.sf.net/2009/contextMenu/insert");
+	private ContextualSelection contextualSelection;
+
+	public InsertSection() {
+		super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 20, insertSection);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof Workflow;
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new DummyAction(INSERT);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/PasteMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/PasteMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/PasteMenuAction.java
new file mode 100644
index 0000000..8f9b1f9
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/PasteMenuAction.java
@@ -0,0 +1,74 @@
+/**********************************************************************
+ * 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.ui.menu.items.contextualviews;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.actions.PasteGraphComponentAction;
+import uk.org.taverna.commons.services.ServiceRegistry;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public class PasteMenuAction extends AbstractContextualMenuAction {
+
+	private static final URI PASTE_SERVICE_URI = URI
+	.create("http://taverna.sf.net/2008/t2workbench/paste#pasteServiceComponent");
+
+	private EditManager editManager;
+	private MenuManager menuManager;
+	private SelectionManager selectionManager;
+	private ServiceRegistry serviceRegistry;
+
+	public PasteMenuAction() {
+		super(EditSection.editSection, 20, PASTE_SERVICE_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return PasteGraphComponentAction.getInstance(editManager, menuManager, selectionManager, serviceRegistry);
+	}
+
+	public boolean isEnabled() {
+		return super.isEnabled() && (getContextualSelection().getSelection() instanceof Workflow);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setMenuManager(MenuManager menuManager) {
+		this.menuManager = menuManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setServiceRegistry(ServiceRegistry serviceRegistry) {
+		this.serviceRegistry = serviceRegistry;
+	}
+
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenRecentMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenRecentMenuAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenRecentMenuAction.java
new file mode 100644
index 0000000..76ef759
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenRecentMenuAction.java
@@ -0,0 +1,418 @@
+package net.sf.taverna.t2.workbench.file.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_0;
+import static java.awt.event.KeyEvent.VK_R;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static javax.swing.Action.NAME;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.SwingUtilities.invokeLater;
+import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_OPEN_SECTION_URI;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.ui.menu.AbstractMenuCustom;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.events.AbstractDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.file.events.OpenedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.SavedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+
+import org.apache.log4j.Logger;
+import org.jdom.Document;
+import org.jdom.JDOMException;
+import org.jdom.input.SAXBuilder;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+public class FileOpenRecentMenuAction extends AbstractMenuCustom implements
+		Observer<FileManagerEvent> {
+	public static final URI RECENT_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileOpenRecent");
+	private static final String CONF = "conf";
+	private static Logger logger = Logger
+			.getLogger(FileOpenRecentMenuAction.class);
+	private static final String RECENT_WORKFLOWS_XML = "recentWorkflows.xml";
+	private static final int MAX_ITEMS = 10;
+
+	private FileManager fileManager;
+	private ApplicationConfiguration applicationConfiguration;
+	private JMenu menu;
+	private List<Recent> recents = new ArrayList<>();
+	private Thread loadRecentThread;
+
+	public FileOpenRecentMenuAction(FileManager fileManager) {
+		super(FILE_OPEN_SECTION_URI, 30, RECENT_URI);
+		this.fileManager = fileManager;
+		fileManager.addObserver(this);
+	}
+
+	@Override
+	public void notify(Observable<FileManagerEvent> sender,
+			FileManagerEvent message) throws Exception {
+		FileManager fileManager = (FileManager) sender;
+		if (message instanceof OpenedDataflowEvent
+				|| message instanceof SavedDataflowEvent) {
+			AbstractDataflowEvent dataflowEvent = (AbstractDataflowEvent) message;
+			WorkflowBundle dataflow = dataflowEvent.getDataflow();
+			Object dataflowSource = fileManager.getDataflowSource(dataflow);
+			FileType dataflowType = fileManager.getDataflowType(dataflow);
+			addRecent(dataflowSource, dataflowType);
+		}
+		if (message instanceof ClosedDataflowEvent)
+			// Make sure enabled/disabled status is correct
+			updateRecentMenu();
+	}
+
+	public void updateRecentMenu() {
+		invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				updateRecentMenuGUI();
+			}
+		});
+		saveRecent();
+	}
+
+	protected void addRecent(Object dataflowSource, FileType dataflowType) {
+		if (dataflowSource == null)
+			return;
+		if (!(dataflowSource instanceof Serializable)) {
+			logger.warn("Can't serialize workflow source for 'Recent workflows': "
+					+ dataflowSource);
+			return;
+		}
+		synchronized (recents) {
+			Recent recent = new Recent((Serializable) dataflowSource, dataflowType);
+			if (recents.contains(recent))
+				recents.remove(recent);
+			recents.add(0, recent); // Add to front
+		}
+		updateRecentMenu();
+	}
+
+	@Override
+	protected Component createCustomComponent() {
+		action = new DummyAction("Recent workflows");
+		action.putValue(MNEMONIC_KEY, VK_R);
+		menu = new JMenu(action);
+		// Disabled until we have loaded the recent workflows
+		menu.setEnabled(false);
+		loadRecentThread = new Thread("Loading recent workflow menu") {
+			// Avoid hanging GUI initialization while deserialising
+			@Override
+			public void run() {
+				loadRecent();
+				updateRecentMenu();
+			}
+		};
+		loadRecentThread.start();
+		return menu;
+	}
+
+	protected synchronized void loadRecent() {
+		File confDir = new File(applicationConfiguration.getApplicationHomeDir(), CONF);
+		confDir.mkdir();
+		File recentFile = new File(confDir, RECENT_WORKFLOWS_XML);
+		if (!recentFile.isFile())
+			return;
+		try {
+			loadRecent(recentFile);
+		} catch (JDOMException|IOException e) {
+			logger.warn("Could not read recent workflows from file "
+					+ recentFile, e);
+		}
+	}
+
+	private void loadRecent(File recentFile) throws FileNotFoundException,
+			IOException, JDOMException {
+		SAXBuilder builder = new SAXBuilder();
+		@SuppressWarnings("unused")
+		Document document;
+		try (InputStream fileInputStream = new BufferedInputStream(
+				new FileInputStream(recentFile))) {
+			document = builder.build(fileInputStream);
+		}
+		synchronized (recents) {
+			recents.clear();
+			//RecentDeserializer deserialiser = new RecentDeserializer();
+			try {
+				// recents.addAll(deserialiser.deserializeRecent(document
+				// .getRootElement()));
+			} catch (Exception e) {
+				logger.warn("Could not read recent workflows from file "
+						+ recentFile, e);
+			}
+		}
+	}
+
+	protected synchronized void saveRecent() {
+		File confDir = new File(applicationConfiguration.getApplicationHomeDir(), CONF);
+		confDir.mkdir();
+		File recentFile = new File(confDir, RECENT_WORKFLOWS_XML);
+
+		try {
+			saveRecent(recentFile);
+//		} catch (JDOMException e) {
+//			logger.warn("Could not generate XML for recent workflows to file "
+//					+ recentFile, e);
+		} catch (IOException e) {
+			logger.warn("Could not write recent workflows to file "
+					+ recentFile, e);
+		}
+	}
+
+	private void saveRecent(File recentFile) throws FileNotFoundException,
+			IOException {
+		// RecentSerializer serializer = new RecentSerializer();
+		// XMLOutputter outputter = new XMLOutputter();
+
+		// Element serializedRecent;
+		synchronized (recents) {
+			if (recents.size() > MAX_ITEMS)
+				// Remove excess entries
+				recents.subList(MAX_ITEMS, recents.size()).clear();
+			// serializedRecent = serializer.serializeRecent(recents);
+		}
+		try (OutputStream outputStream = new BufferedOutputStream(
+				new FileOutputStream(recentFile))) {
+			// outputter.output(serializedRecent, outputStream);
+		}
+	}
+
+	protected void updateRecentMenuGUI() {
+		int items = 0;
+		menu.removeAll();
+		synchronized (recents) {
+			for (Recent recent : recents) {
+				if (++items >= MAX_ITEMS)
+					break;
+				OpenRecentAction openRecentAction = new OpenRecentAction(
+						recent, fileManager);
+				if (fileManager.getDataflowBySource(recent.getDataflowSource()) != null)
+					openRecentAction.setEnabled(false);
+				// else setEnabled(true)
+				JMenuItem menuItem = new JMenuItem(openRecentAction);
+				if (items < 10) {
+					openRecentAction.putValue(NAME, items + " "
+							+ openRecentAction.getValue(NAME));
+					menuItem.setMnemonic(VK_0 + items);
+				}
+				menu.add(menuItem);
+			}
+		}
+		menu.setEnabled(items > 0);
+		menu.revalidate();
+	}
+
+	@SuppressWarnings("serial")
+	public static class OpenRecentAction extends AbstractAction implements
+			Runnable {
+		private final Recent recent;
+		private Component component = null;
+		private final FileManager fileManager;
+
+		public OpenRecentAction(Recent recent, FileManager fileManager) {
+			this.recent = recent;
+			this.fileManager = fileManager;
+			Serializable source = recent.getDataflowSource();
+			String name;
+			if (source instanceof File)
+				name = ((File) source).getAbsolutePath();
+			else
+				name = source.toString();
+			this.putValue(NAME, name);
+			this.putValue(SHORT_DESCRIPTION, "Open the workflow " + name);
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			component = null;
+			if (e.getSource() instanceof Component)
+				component = (Component) e.getSource();
+			setEnabled(false);
+			new Thread(this, "Opening workflow from "
+					+ recent.getDataflowSource()).start();
+		}
+
+		/**
+		 * Opening workflow in separate thread
+		 */
+		@Override
+		public void run() {
+			final Serializable source = recent.getDataflowSource();
+			try {
+				fileManager.openDataflow(recent.makefileType(), source);
+			} catch (OpenException ex) {
+				logger.warn("Failed to open the workflow from  " + source
+						+ " \n", ex);
+				showMessageDialog(component,
+						"Failed to open the workflow from url " + source
+								+ " \n" + ex.getMessage(), "Error!",
+						ERROR_MESSAGE);
+			} finally {
+				setEnabled(true);
+			}
+		}
+	}
+
+	@SuppressWarnings("serial")
+	public static class Recent implements Serializable {
+		private final class RecentFileType extends FileType {
+			@Override
+			public String getMimeType() {
+				return mimeType;
+			}
+
+			@Override
+			public String getExtension() {
+				return extension;
+			}
+
+			@Override
+			public String getDescription() {
+				return "File type " + extension + " " + mimeType;
+			}
+		}
+
+		private Serializable dataflowSource;
+		private String mimeType;
+		private String extension;
+
+		public String getMimeType() {
+			return mimeType;
+		}
+
+		public void setMimeType(String mimeType) {
+			this.mimeType = mimeType;
+		}
+
+		public String getExtension() {
+			return extension;
+		}
+
+		public void setExtension(String extension) {
+			this.extension = extension;
+		}
+
+		public Recent() {
+		}
+
+		public FileType makefileType() {
+			if (mimeType == null && extension == null)
+				return null;
+			return new RecentFileType();
+		}
+
+		public Recent(Serializable dataflowSource, FileType dataflowType) {
+			setDataflowSource(dataflowSource);
+			if (dataflowType != null) {
+				setMimeType(dataflowType.getMimeType());
+				setExtension(dataflowType.getExtension());
+			}
+		}
+
+		@Override
+		public int hashCode() {
+			final int prime = 31;
+			int result = 1;
+			result = prime
+					* result
+					+ ((dataflowSource == null) ? 0 : dataflowSource.hashCode());
+			result = prime * result
+					+ ((extension == null) ? 0 : extension.hashCode());
+			result = prime * result
+					+ ((mimeType == null) ? 0 : mimeType.hashCode());
+			return result;
+		}
+
+		@Override
+		public boolean equals(Object obj) {
+			if (this == obj)
+				return true;
+			if (obj == null)
+				return false;
+			if (!(obj instanceof Recent))
+				return false;
+			Recent other = (Recent) obj;
+
+			if (dataflowSource == null) {
+				if (other.dataflowSource != null)
+					return false;
+			} else if (!dataflowSource.equals(other.dataflowSource))
+				return false;
+
+			if (extension == null) {
+				if (other.extension != null)
+					return false;
+			} else if (!extension.equals(other.extension))
+				return false;
+
+			if (mimeType == null) {
+				if (other.mimeType != null)
+					return false;
+			} else if (!mimeType.equals(other.mimeType))
+				return false;
+
+			return true;
+		}
+
+		public Serializable getDataflowSource() {
+			return dataflowSource;
+		}
+
+		public void setDataflowSource(Serializable dataflowSource) {
+			this.dataflowSource = dataflowSource;
+		}
+
+		@Override
+		public String toString() {
+			return getDataflowSource() + "";
+		}
+	}
+
+	// TODO find new serialization
+//	protected static class RecentDeserializer extends AbstractXMLDeserializer {
+//		public Collection<Recent> deserializeRecent(Element el) {
+//			return (Collection<Recent>) super.createBean(el, getClass()
+//					.getClassLoader());
+//		}
+//	}
+//
+//	protected static class RecentSerializer extends AbstractXMLSerializer {
+//		public Element serializeRecent(List<Recent> x) throws JDOMException,
+//				IOException {
+//			Element beanAsElement = super.beanAsElement(x);
+//			return beanAsElement;
+//		}
+//	}
+
+	public void setApplicationConfiguration(
+			ApplicationConfiguration applicationConfiguration) {
+		this.applicationConfiguration = applicationConfiguration;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAllMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAllMenuAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAllMenuAction.java
new file mode 100644
index 0000000..86edacb
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAllMenuAction.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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.file.impl.menu;
+
+import static net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuSection.FILE_SAVE_SECTION_URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.SaveAllWorkflowsAction;
+
+public class FileSaveAllMenuAction extends AbstractMenuAction {
+	private final EditManager editManager;
+	private final FileManager fileManager;
+
+	public FileSaveAllMenuAction(EditManager editManager,
+			FileManager fileManager) {
+		super(FILE_SAVE_SECTION_URI, 30);
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new SaveAllWorkflowsAction(editManager, fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAsMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAsMenuAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAsMenuAction.java
new file mode 100644
index 0000000..77917c9
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAsMenuAction.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * 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.file.impl.menu;
+
+import static net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuSection.FILE_SAVE_SECTION_URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.SaveWorkflowAsAction;
+
+public class FileSaveAsMenuAction extends AbstractMenuAction {
+	private final FileManager fileManager;
+
+	public FileSaveAsMenuAction(FileManager fileManager) {
+		super(FILE_SAVE_SECTION_URI, 20);
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new SaveWorkflowAsAction(fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuAction.java
new file mode 100644
index 0000000..eeaecb3
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuAction.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.file.impl.menu;
+
+import static net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuSection.FILE_SAVE_SECTION_URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.SaveWorkflowAction;
+
+public class FileSaveMenuAction extends AbstractMenuAction {
+	private final EditManager editManager;
+	private final FileManager fileManager;
+
+	public FileSaveMenuAction(EditManager editManager, FileManager fileManager) {
+		super(FILE_SAVE_SECTION_URI, 10);
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new SaveWorkflowAction(editManager, fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuSection.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuSection.java
new file mode 100644
index 0000000..a75a855
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuSection.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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.file.impl.menu;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+public class FileSaveMenuSection extends AbstractMenuSection {
+	public static final URI FILE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#file");
+	public static final URI FILE_SAVE_SECTION_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileSaveSection");
+
+	public FileSaveMenuSection() {
+		super(FILE_URI, 40, FILE_SAVE_SECTION_URI);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/WorkflowsMenu.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/WorkflowsMenu.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/WorkflowsMenu.java
new file mode 100644
index 0000000..e056572
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/WorkflowsMenu.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * 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.file.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_0;
+import static java.awt.event.KeyEvent.VK_W;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static javax.swing.SwingUtilities.invokeLater;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.ButtonGroup;
+import javax.swing.JMenu;
+import javax.swing.JRadioButtonMenuItem;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.ui.menu.AbstractMenuCustom;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.events.AbstractDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+public class WorkflowsMenu extends AbstractMenuCustom {
+	private EditManagerObserver editManagerObserver = new EditManagerObserver();
+	private FileManager fileManager;
+	private FileManagerObserver fileManagerObserver = new FileManagerObserver();
+
+	private JMenu workflowsMenu;
+
+	public WorkflowsMenu(EditManager editManager, FileManager fileManager) {
+		super(DEFAULT_MENU_BAR, 900);
+		this.fileManager = fileManager;
+		fileManager.addObserver(fileManagerObserver);
+		editManager.addObserver(editManagerObserver);
+	}
+
+	@Override
+	protected Component createCustomComponent() {
+		DummyAction action = new DummyAction("Workflows");
+		action.putValue(MNEMONIC_KEY, VK_W);
+
+		workflowsMenu = new JMenu(action);
+
+		updateWorkflowsMenu();
+		return workflowsMenu;
+	}
+
+	public void updateWorkflowsMenu() {
+		invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				updateWorkflowsMenuUI();
+			}
+		});
+	}
+
+	protected void updateWorkflowsMenuUI() {
+		workflowsMenu.setEnabled(false);
+		workflowsMenu.removeAll();
+		ButtonGroup workflowsGroup = new ButtonGroup();
+
+		int i = 0;
+		WorkflowBundle currentDataflow = fileManager.getCurrentDataflow();
+		for (WorkflowBundle workflowBundle : fileManager.getOpenDataflows()) {
+			String name = fileManager.getDataflowName(workflowBundle);
+			if (fileManager.isDataflowChanged(workflowBundle))
+				name = "*" + name;
+			// A counter
+			name = ++i + " " + name;
+
+			SwitchWorkflowAction switchWorkflowAction = new SwitchWorkflowAction(
+					name, workflowBundle);
+			if (i < 10)
+				switchWorkflowAction.putValue(MNEMONIC_KEY, new Integer(VK_0
+						+ i));
+
+			JRadioButtonMenuItem switchWorkflowMenuItem = new JRadioButtonMenuItem(
+					switchWorkflowAction);
+			workflowsGroup.add(switchWorkflowMenuItem);
+			if (workflowBundle.equals(currentDataflow))
+				switchWorkflowMenuItem.setSelected(true);
+			workflowsMenu.add(switchWorkflowMenuItem);
+		}
+		if (i == 0)
+			workflowsMenu.add(new NoWorkflowsOpen());
+		workflowsMenu.setEnabled(true);
+		workflowsMenu.revalidate();
+	}
+
+	private final class EditManagerObserver implements
+			Observer<EditManagerEvent> {
+		@Override
+		public void notify(Observable<EditManagerEvent> sender,
+				EditManagerEvent message) throws Exception {
+			if (message instanceof AbstractDataflowEditEvent)
+				updateWorkflowsMenu();
+		}
+	}
+
+	private final class FileManagerObserver implements
+			Observer<FileManagerEvent> {
+		@Override
+		public void notify(Observable<FileManagerEvent> sender,
+				FileManagerEvent message) throws Exception {
+			if (message instanceof AbstractDataflowEvent)
+				updateWorkflowsMenu();
+			// TODO: Don't rebuild whole menu
+		}
+	}
+
+	@SuppressWarnings("serial")
+	private final class NoWorkflowsOpen extends AbstractAction {
+		private NoWorkflowsOpen() {
+			super("No workflows open");
+			setEnabled(false);
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			// No-op
+		}
+	}
+
+	@SuppressWarnings("serial")
+	private final class SwitchWorkflowAction extends AbstractAction {
+		private final WorkflowBundle workflowBundle;
+
+		private SwitchWorkflowAction(String name, WorkflowBundle workflowBundle) {
+			super(name);
+			this.workflowBundle = workflowBundle;
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			fileManager.setCurrentDataflow(workflowBundle);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/CloseToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/CloseToolbarAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/CloseToolbarAction.java
new file mode 100644
index 0000000..68ef3f9
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/CloseToolbarAction.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * 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.file.impl.toolbar;
+
+import static net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection.FILE_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.CloseWorkflowAction;
+
+/**
+ * Action to close the current workflow.
+ * 
+ * @author Alex Nenadic
+ */
+public class CloseToolbarAction extends AbstractMenuAction {
+	private static final URI FILE_CLOSE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarClose");
+	private final EditManager editManager;
+	private final FileManager fileManager;
+
+	public CloseToolbarAction(EditManager editManager, FileManager fileManager) {
+		super(FILE_TOOLBAR_SECTION, 30, FILE_CLOSE_URI);
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new CloseWorkflowAction(editManager, fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/FileToolbarMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/FileToolbarMenuSection.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/FileToolbarMenuSection.java
new file mode 100644
index 0000000..257d590
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/FileToolbarMenuSection.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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.file.impl.toolbar;
+
+import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+public class FileToolbarMenuSection extends AbstractMenuSection {
+	public static final URI FILE_TOOLBAR_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarSection");
+
+	public FileToolbarMenuSection() {
+		super(DEFAULT_TOOL_BAR, 10, FILE_TOOLBAR_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/NewToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/NewToolbarAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/NewToolbarAction.java
new file mode 100644
index 0000000..2c8e922
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/NewToolbarAction.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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.file.impl.toolbar;
+
+import static net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection.FILE_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.NewWorkflowAction;
+
+public class NewToolbarAction extends AbstractMenuAction {
+	private static final URI FILE_NEW_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarNew");
+	private final FileManager fileManager;
+
+	public NewToolbarAction(FileManager fileManager) {
+		super(FILE_TOOLBAR_SECTION, 10, FILE_NEW_URI);
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new NewWorkflowAction(fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenToolbarAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenToolbarAction.java
new file mode 100644
index 0000000..ae99509
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenToolbarAction.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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.file.impl.toolbar;
+
+import static net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection.FILE_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.OpenWorkflowAction;
+
+public class OpenToolbarAction extends AbstractMenuAction {
+	private static final URI FILE_OPEN_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarOpen");
+	private final FileManager fileManager;
+
+	public OpenToolbarAction(FileManager fileManager) {
+		super(FILE_TOOLBAR_SECTION, 20, FILE_OPEN_URI);
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new OpenWorkflowAction(fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenWorkflowFromURLToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenWorkflowFromURLToolbarAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenWorkflowFromURLToolbarAction.java
new file mode 100644
index 0000000..2554063
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenWorkflowFromURLToolbarAction.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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.file.impl.toolbar;
+
+import static net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection.FILE_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.OpenWorkflowFromURLAction;
+
+public class OpenWorkflowFromURLToolbarAction extends AbstractMenuAction {
+	private static final URI FILE_OPEN_FROM_URL_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarOpenFromURL");
+	private final FileManager fileManager;
+
+	public OpenWorkflowFromURLToolbarAction(FileManager fileManager) {
+		super(FILE_TOOLBAR_SECTION, 25, FILE_OPEN_FROM_URL_URI);
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new OpenWorkflowFromURLAction(null, fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/SaveToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/SaveToolbarAction.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/SaveToolbarAction.java
new file mode 100644
index 0000000..53ba720
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/SaveToolbarAction.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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.file.impl.toolbar;
+
+import static net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection.FILE_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.impl.actions.SaveWorkflowAction;
+
+public class SaveToolbarAction extends AbstractMenuAction {
+	private static final URI FILE_SAVE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarSave");
+	private final EditManager editManager;
+	private final FileManager fileManager;
+
+	public SaveToolbarAction(EditManager editManager, FileManager fileManager) {
+		super(FILE_TOOLBAR_SECTION, 40, FILE_SAVE_URI);
+		this.editManager = editManager;
+		this.fileManager = fileManager;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new SaveWorkflowAction(editManager, fileManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..100915c
--- /dev/null
+++ b/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,20 @@
+net.sf.taverna.t2.workbench.file.impl.menu.FileCloseMenuAction
+net.sf.taverna.t2.workbench.file.impl.menu.FileNewMenuAction
+net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuAction
+net.sf.taverna.t2.workbench.file.impl.menu.FileOpenFromURLMenuAction
+net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection
+net.sf.taverna.t2.workbench.file.impl.menu.FileOpenRecentMenuAction
+net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuSection
+net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuAction
+net.sf.taverna.t2.workbench.file.impl.menu.FileSaveAllMenuAction
+net.sf.taverna.t2.workbench.file.impl.menu.FileSaveAsMenuAction
+
+net.sf.taverna.t2.workbench.file.impl.menu.WorkflowsMenu
+net.sf.taverna.t2.workbench.file.impl.menu.FileCloseAllMenuAction
+
+net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection
+net.sf.taverna.t2.workbench.file.impl.toolbar.NewToolbarAction
+net.sf.taverna.t2.workbench.file.impl.toolbar.OpenToolbarAction
+net.sf.taverna.t2.workbench.file.impl.toolbar.OpenWorkflowFromURLToolbarAction
+net.sf.taverna.t2.workbench.file.impl.toolbar.SaveToolbarAction
+net.sf.taverna.t2.workbench.file.impl.toolbar.CloseToolbarAction

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ShutdownSPI
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ShutdownSPI b/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ShutdownSPI
new file mode 100644
index 0000000..cc53d36
--- /dev/null
+++ b/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ShutdownSPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.file.impl.hooks.CloseWorkflowsOnShutdown

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler b/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler
new file mode 100644
index 0000000..cfd1c7a
--- /dev/null
+++ b/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler
@@ -0,0 +1,2 @@
+net.sf.taverna.t2.workbench.file.impl.T2DataflowOpener
+net.sf.taverna.t2.workbench.file.impl.DataflowFromDataflowPersistenceHandler
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.FileManager
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.FileManager b/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.FileManager
new file mode 100644
index 0000000..656feeb
--- /dev/null
+++ b/taverna-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.FileManager
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.file.impl.FileManagerImpl
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/resources/META-INF/spring/file-impl-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/resources/META-INF/spring/file-impl-context-osgi.xml b/taverna-file-impl/src/main/resources/META-INF/spring/file-impl-context-osgi.xml
new file mode 100644
index 0000000..7c6e290
--- /dev/null
+++ b/taverna-file-impl/src/main/resources/META-INF/spring/file-impl-context-osgi.xml
@@ -0,0 +1,100 @@
+<?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="FileCloseMenuAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="file.close" />
+		</service-properties>
+	</service>
+	<service ref="FileNewMenuAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="file.new" />
+		</service-properties>
+	</service>
+	<service ref="FileOpenMenuAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="file.open" />
+		</service-properties>
+	</service>
+	<service ref="FileOpenFromURLMenuAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="file.open.url" />
+		</service-properties>
+	</service>
+	<service ref="FileOpenMenuSection" auto-export="interfaces" />
+	<service ref="FileOpenRecentMenuAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="file.open.recent" />
+		</service-properties>
+	</service>
+	<service ref="FileSaveMenuSection" auto-export="interfaces" />
+	<service ref="FileSaveMenuAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="file.save" />
+		</service-properties>
+	</service>
+	<service ref="FileSaveAllMenuAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="file.save.all" />
+		</service-properties>
+	</service>
+	<service ref="FileSaveAsMenuAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="file.save.as" />
+		</service-properties>
+	</service>
+	<service ref="WorkflowsMenu" auto-export="interfaces" />
+	<service ref="FileCloseAllMenuAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="file.close.all" />
+		</service-properties>
+	</service>
+	<service ref="FileToolbarMenuSection" auto-export="interfaces" />
+	<service ref="NewToolbarAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="toolbar.new" />
+		</service-properties>
+	</service>
+	<service ref="OpenToolbarAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="toolbar.open" />
+		</service-properties>
+	</service>
+	<service ref="OpenWorkflowFromURLToolbarAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="toolbar.open.url" />
+		</service-properties>
+	</service>
+	<service ref="SaveToolbarAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="toolbar.save" />
+		</service-properties>
+	</service>
+	<service ref="CloseToolbarAction" auto-export="interfaces">
+		<service-properties>
+			<beans:entry key="menu.action" value="toolbar.close" />
+		</service-properties>
+	</service>
+
+	<service ref="T2DataflowOpener" interface="net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler" />
+
+	<service ref="WorkflowBundleOpener" interface="net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler" />
+	<service ref="WorkflowBundleSaver" interface="net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler" />
+
+	<service ref="CloseWorkflowsOnShutdown" interface="net.sf.taverna.t2.workbench.ShutdownSPI" />
+
+	<service ref="FileManagerImpl" interface="net.sf.taverna.t2.workbench.file.FileManager" />
+
+	<reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" />
+	<reference id="applicationConfiguration" interface="uk.org.taverna.configuration.app.ApplicationConfiguration" />
+	<reference id="workflowBundleIO" interface="uk.org.taverna.scufl2.api.io.WorkflowBundleIO" />
+
+	<list id="dataflowPersistenceHandlers" interface="net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler" cardinality="0..N">
+		<listener ref="DataflowPersistenceHandlerRegistry" bind-method="update" unbind-method="update" />
+	</list>
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/resources/META-INF/spring/file-impl-context.xml
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/resources/META-INF/spring/file-impl-context.xml b/taverna-file-impl/src/main/resources/META-INF/spring/file-impl-context.xml
new file mode 100644
index 0000000..493df5f
--- /dev/null
+++ b/taverna-file-impl/src/main/resources/META-INF/spring/file-impl-context.xml
@@ -0,0 +1,123 @@
+<?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="FileCloseMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileCloseMenuAction">
+    	<constructor-arg ref="editManager" />
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="FileNewMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileNewMenuAction">
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="FileOpenMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuAction">
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="FileOpenFromURLMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileOpenFromURLMenuAction">
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="FileOpenMenuSection" class="net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection" />
+	<bean id="FileOpenRecentMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileOpenRecentMenuAction">
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+ 		<property name="applicationConfiguration" ref="applicationConfiguration"/>
+    </bean>
+	<bean id="FileSaveMenuSection" class="net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuSection" />
+	<bean id="FileSaveMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuAction">
+    	<constructor-arg ref="editManager" />
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="FileSaveAllMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileSaveAllMenuAction">
+    	<constructor-arg ref="editManager" />
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="FileSaveAsMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileSaveAsMenuAction">
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="WorkflowsMenu" class="net.sf.taverna.t2.workbench.file.impl.menu.WorkflowsMenu">
+	    <constructor-arg ref="editManager" />
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+	</bean>
+	<bean id="FileCloseAllMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileCloseAllMenuAction">
+    	<constructor-arg ref="editManager" />
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="FileToolbarMenuSection" class="net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection" />
+	<bean id="NewToolbarAction" class="net.sf.taverna.t2.workbench.file.impl.toolbar.NewToolbarAction">
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="OpenToolbarAction" class="net.sf.taverna.t2.workbench.file.impl.toolbar.OpenToolbarAction">
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="OpenWorkflowFromURLToolbarAction" class="net.sf.taverna.t2.workbench.file.impl.toolbar.OpenWorkflowFromURLToolbarAction">
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="SaveToolbarAction" class="net.sf.taverna.t2.workbench.file.impl.toolbar.SaveToolbarAction">
+    	<constructor-arg ref="editManager" />
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+	<bean id="CloseToolbarAction" class="net.sf.taverna.t2.workbench.file.impl.toolbar.CloseToolbarAction">
+    	<constructor-arg ref="editManager" />
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+
+	<bean id="T2DataflowOpener" class="net.sf.taverna.t2.workbench.file.impl.T2DataflowOpener">
+			<property name="workflowBundleIO" ref="workflowBundleIO"/>
+	</bean>
+
+	<bean id="WorkflowBundleOpener" class="net.sf.taverna.t2.workbench.file.impl.WorkflowBundleOpener">
+			<property name="workflowBundleIO" ref="workflowBundleIO"/>
+	</bean>
+	<bean id="WorkflowBundleSaver" class="net.sf.taverna.t2.workbench.file.impl.WorkflowBundleSaver">
+			<property name="workflowBundleIO" ref="workflowBundleIO"/>
+	</bean>
+
+	<bean id="CloseWorkflowsOnShutdown" class="net.sf.taverna.t2.workbench.file.impl.hooks.CloseWorkflowsOnShutdown">
+    	<constructor-arg ref="editManager" />
+    	<constructor-arg>
+			<ref local="FileManagerImpl" />
+		</constructor-arg>
+    </bean>
+
+	<bean id="FileManagerImpl" class="net.sf.taverna.t2.workbench.file.impl.FileManagerImpl">
+    	<constructor-arg name="editManager" ref="editManager" />
+    	<property name="dataflowPersistenceHandlerRegistry">
+    		<ref local="DataflowPersistenceHandlerRegistry"/>
+    	</property>
+	</bean>
+
+	<bean id="DataflowPersistenceHandlerRegistry" class="net.sf.taverna.t2.workbench.file.impl.DataflowPersistenceHandlerRegistry">
+    	<property name="dataflowPersistenceHandlers" ref="dataflowPersistenceHandlers" />
+	</bean>
+
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/test/java/net/sf/taverna/t2/workbench/file/impl/FileManagerTest.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/test/java/net/sf/taverna/t2/workbench/file/impl/FileManagerTest.java b/taverna-file-impl/src/test/java/net/sf/taverna/t2/workbench/file/impl/FileManagerTest.java
new file mode 100644
index 0000000..691b278
--- /dev/null
+++ b/taverna-file-impl/src/test/java/net/sf/taverna/t2/workbench/file/impl/FileManagerTest.java
@@ -0,0 +1,385 @@
+/*******************************************************************************
+ * 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.file.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.impl.EditManagerImpl;
+import net.sf.taverna.t2.workbench.file.DataflowInfo;
+import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.file.events.SetCurrentDataflowEvent;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+import net.sf.taverna.t2.workbench.file.exceptions.OverwriteException;
+import net.sf.taverna.t2.workflow.edits.AddProcessorEdit;
+import net.sf.taverna.t2.workflow.edits.RenameEdit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.io.WorkflowBundleIO;
+import uk.org.taverna.scufl2.api.io.WorkflowBundleReader;
+import uk.org.taverna.scufl2.api.io.WorkflowBundleWriter;
+import uk.org.taverna.scufl2.rdfxml.RDFXMLReader;
+import uk.org.taverna.scufl2.rdfxml.RDFXMLWriter;
+import uk.org.taverna.scufl2.translator.t2flow.T2FlowReader;
+
+public class FileManagerTest {
+
+	private static final WorkflowBundleFileType WF_BUNDLE_FILE_TYPE = new WorkflowBundleFileType();
+	private static final T2FlowFileType T2_FLOW_FILE_TYPE = new T2FlowFileType();
+
+	private static final String DUMMY_WORKFLOW_T2FLOW = "dummy-workflow.t2flow";
+
+	private FileManagerImpl fileManager;
+	private EditManager editManager;
+
+	private FileManagerObserver fileManagerObserver= new FileManagerObserver();;
+
+	@Test
+	public void close() throws Exception {
+		assertTrue("Non-empty set of open dataflows", fileManager
+				.getOpenDataflows().isEmpty());
+		WorkflowBundle dataflow = openDataflow();
+		assertEquals("Unexpected list of open dataflows", Arrays
+				.asList(dataflow), fileManager.getOpenDataflows());
+		fileManager.closeDataflow(dataflow, true);
+		assertNotSame(dataflow, fileManager.getOpenDataflows().get(0));
+		assertTrue("Did not insert empty dataflow after close", fileManager
+				.getOpenDataflows().get(0).getMainWorkflow().getProcessors().isEmpty());
+	}
+
+	@Test
+	public void openRemovesEmptyDataflow() throws Exception {
+		WorkflowBundle newDataflow = fileManager.newDataflow();
+		assertEquals("Unexpected list of open dataflows", Arrays
+				.asList(newDataflow), fileManager.getOpenDataflows());
+		WorkflowBundle dataflow = openDataflow();
+		// Should have removed newDataflow
+		assertEquals("Unexpected list of open dataflows", Arrays
+				.asList(dataflow), fileManager.getOpenDataflows());
+	}
+
+	@Test
+	public void isChanged() throws Exception {
+		WorkflowBundle dataflow = openDataflow();
+		assertFalse("Dataflow should not have changed", fileManager
+				.isDataflowChanged(dataflow));
+
+		// Do a change
+		Processor emptyProcessor = new Processor();
+		Edit<Workflow> addProcessorEdit = new AddProcessorEdit(dataflow.getMainWorkflow(),
+				emptyProcessor);
+		editManager.doDataflowEdit(dataflow, addProcessorEdit);
+		assertTrue("Dataflow should have changed", fileManager
+				.isDataflowChanged(dataflow));
+
+		// Save it with the change
+		File dataflowFile = File.createTempFile("test", ".t2flow");
+		dataflowFile.deleteOnExit();
+		dataflowFile.delete();
+
+		fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, false);
+		assertFalse("Dataflow should no longer be marked as changed",
+				fileManager.isDataflowChanged(dataflow));
+	}
+
+	@Ignore("Undo support for ischanged not yet implemented")
+	@Test
+	public void isChangedWithUndo() throws Exception {
+		WorkflowBundle dataflow = openDataflow();
+		// Do a change
+		Processor emptyProcessor = new Processor();
+		Edit<Workflow> addProcessorEdit = new AddProcessorEdit(dataflow.getMainWorkflow(),
+				emptyProcessor);
+		editManager.doDataflowEdit(dataflow, addProcessorEdit);
+		assertTrue("Dataflow should have changed", fileManager
+				.isDataflowChanged(dataflow));
+		editManager.undoDataflowEdit(dataflow);
+		assertFalse(
+				"Dataflow should no longer be marked as changed after undo",
+				fileManager.isDataflowChanged(dataflow));
+		editManager.redoDataflowEdit(dataflow);
+		assertTrue("Dataflow should have changed after redo before save",
+				fileManager.isDataflowChanged(dataflow));
+
+		// Save it with the change
+		File dataflowFile = File.createTempFile("test", ".t2flow");
+		dataflowFile.deleteOnExit();
+		dataflowFile.delete();
+		fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, false);
+		assertFalse("Dataflow should no longer be marked as changed",
+				fileManager.isDataflowChanged(dataflow));
+
+		editManager.undoDataflowEdit(dataflow);
+		assertTrue("Dataflow should have changed after undo", fileManager
+				.isDataflowChanged(dataflow));
+		fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, false);
+		editManager.redoDataflowEdit(dataflow);
+		assertTrue("Dataflow should have changed after redo after save",
+				fileManager.isDataflowChanged(dataflow));
+	}
+
+	@Test
+	public void isListed() throws Exception {
+		assertTrue("Non-empty set of open data flows", fileManager
+				.getOpenDataflows().isEmpty());
+		WorkflowBundle dataflow = openDataflow();
+		assertEquals("Unexpected list of open dataflows", Arrays
+				.asList(dataflow), fileManager.getOpenDataflows());
+	}
+
+	/**
+	 * Always uses a <strong>new</strong> file manager instead of the instance
+	 * one from {@link FileManager#getInstance()}.
+	 *
+	 * @see #getFileManagerInstance()
+	 *
+	 */
+	@Before
+	public void makeFileManager() {
+		System.setProperty("java.awt.headless", "true");
+		editManager = new EditManagerImpl();
+		fileManager = new FileManagerImpl(editManager);
+		fileManagerObserver = new FileManagerObserver();
+		fileManager.addObserver(fileManagerObserver);
+		WorkflowBundleIO workflowBundleIO = new WorkflowBundleIO();
+		workflowBundleIO.setReaders(Arrays.<WorkflowBundleReader>asList(new RDFXMLReader(), new T2FlowReader()));
+		workflowBundleIO.setWriters(Arrays.<WorkflowBundleWriter>asList(new RDFXMLWriter()));
+		T2DataflowOpener t2DataflowOpener = new T2DataflowOpener();
+		t2DataflowOpener.setWorkflowBundleIO(workflowBundleIO);
+		WorkflowBundleOpener workflowBundleOpener = new WorkflowBundleOpener();
+		workflowBundleOpener.setWorkflowBundleIO(workflowBundleIO);
+		WorkflowBundleSaver workflowBundleSaver = new WorkflowBundleSaver();
+		workflowBundleSaver.setWorkflowBundleIO(workflowBundleIO);
+		DataflowPersistenceHandlerRegistry dataflowPersistenceHandlerRegistry = new DataflowPersistenceHandlerRegistry();
+		dataflowPersistenceHandlerRegistry.setDataflowPersistenceHandlers(Arrays.asList(
+				new DataflowPersistenceHandler[] {t2DataflowOpener, workflowBundleOpener, workflowBundleSaver}));
+		dataflowPersistenceHandlerRegistry.updateColletions();
+		fileManager.setDataflowPersistenceHandlerRegistry(dataflowPersistenceHandlerRegistry);
+	}
+
+	@Test
+	public void open() throws Exception {
+		assertTrue("ModelMapObserver already contained messages",
+				fileManagerObserver.messages.isEmpty());
+		WorkflowBundle dataflow = openDataflow();
+		assertNotNull("Dataflow was not loaded", dataflow);
+		assertEquals("Loaded dataflow was not set as current dataflow",
+				dataflow, fileManager.getCurrentDataflow());
+		assertFalse("ModelMapObserver did not contain message",
+				fileManagerObserver.messages.isEmpty());
+		assertEquals("ModelMapObserver contained unexpected messages", 2,
+				fileManagerObserver.messages.size());
+		FileManagerEvent event = fileManagerObserver.messages.get(0);
+		assertTrue(event instanceof SetCurrentDataflowEvent);
+		assertEquals(dataflow, ((SetCurrentDataflowEvent) event).getDataflow());
+	}
+
+	@Test
+	public void openSilently() throws Exception {
+		assertTrue("ModelMapObserver already contained messages",
+				fileManagerObserver.messages.isEmpty());
+		URL url = getClass().getResource(DUMMY_WORKFLOW_T2FLOW);
+		DataflowInfo info = fileManager.openDataflowSilently(T2_FLOW_FILE_TYPE, url);
+
+		WorkflowBundle dataflow = info.getDataflow();
+		assertNotNull("Dataflow was not loaded", dataflow);
+
+		assertNotSame("Loaded dataflow was set as current dataflow",
+				dataflow, fileManager.getCurrentDataflow());
+		assertTrue("ModelMapObserver contained unexpected messages",
+				fileManagerObserver.messages.isEmpty());
+	}
+
+	@Test
+	public void canSaveDataflow() throws Exception {
+		WorkflowBundle savedDataflow = openDataflow();
+		File dataflowFile = File.createTempFile("test", ".t2flow");
+		dataflowFile.deleteOnExit();
+		dataflowFile.delete();
+		fileManager.saveDataflow(savedDataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, true);
+		assertTrue(fileManager.canSaveWithoutDestination(savedDataflow));
+		fileManager.saveDataflow(savedDataflow, true);
+		fileManager.closeDataflow(savedDataflow, true);
+
+		WorkflowBundle otherFlow = fileManager.openDataflow(WF_BUNDLE_FILE_TYPE, dataflowFile.toURI()
+				.toURL());
+		assertTrue(fileManager.canSaveWithoutDestination(otherFlow));
+	}
+
+	@Test
+	public void save() throws Exception {
+		WorkflowBundle savedDataflow = openDataflow();
+		File dataflowFile = File.createTempFile("test", ".t2flow");
+		dataflowFile.deleteOnExit();
+		dataflowFile.delete();
+		assertFalse("File should not exist", dataflowFile.isFile());
+		fileManager.saveDataflow(savedDataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, false);
+		assertTrue("File should exist", dataflowFile.isFile());
+		WorkflowBundle loadedDataflow = fileManager.openDataflow(WF_BUNDLE_FILE_TYPE, dataflowFile.toURI()
+				.toURL());
+		assertNotSame("Dataflow was not reopened", savedDataflow,
+				loadedDataflow);
+		assertEquals("Unexpected number of processors in saved dataflow", 1,
+				savedDataflow.getMainWorkflow().getProcessors().size());
+		assertEquals("Unexpected number of processors in loaded dataflow", 1,
+				loadedDataflow.getMainWorkflow().getProcessors().size());
+
+		Processor savedProcessor = savedDataflow.getMainWorkflow().getProcessors().first();
+		Processor loadedProcessor = loadedDataflow.getMainWorkflow().getProcessors().first();
+		assertEquals("Loaded processor had wrong name", savedProcessor
+				.getName(), loadedProcessor.getName());
+
+		// TODO convert to scufl2
+//		BeanshellActivity savedActivity = (BeanshellActivity) savedProcessor
+//				.getActivityList().get(0);
+//		BeanshellActivity loadedActivity = (BeanshellActivity) loadedProcessor
+//				.getActivityList().get(0);
+//		String savedScript = savedActivity.getConfiguration().getScript();
+//		String loadedScript = loadedActivity.getConfiguration().getScript();
+//		assertEquals("Unexpected saved script",
+//				"String output = input + \"XXX\";", savedScript);
+//		assertEquals("Loaded script did not matched saved script", savedScript,
+//				loadedScript);
+	}
+
+	@Test
+	public void saveSilent() throws Exception {
+		assertTrue("ModelMapObserver contained unexpected messages",
+				fileManagerObserver.messages.isEmpty());
+
+		URL url = getClass().getResource(DUMMY_WORKFLOW_T2FLOW);
+		DataflowInfo info = fileManager.openDataflowSilently(T2_FLOW_FILE_TYPE, url);
+		WorkflowBundle dataflow = info.getDataflow();
+		assertTrue("ModelMapObserver contained unexpected messages",
+				fileManagerObserver.messages.isEmpty());
+
+		File dataflowFile = File.createTempFile("test", ".t2flow");
+		dataflowFile.deleteOnExit();
+		dataflowFile.delete();
+		assertFalse("File should not exist", dataflowFile.isFile());
+
+		fileManager.saveDataflowSilently(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, false);
+		assertTrue("File should exist", dataflowFile.isFile());
+
+		assertTrue("ModelMapObserver contained unexpected messages",
+				fileManagerObserver.messages.isEmpty());
+
+	}
+
+	@Test
+	public void saveOverwriteAgain() throws Exception {
+		WorkflowBundle dataflow = openDataflow();
+		File dataflowFile = File.createTempFile("test", ".t2flow");
+		dataflowFile.delete();
+		dataflowFile.deleteOnExit();
+		// File did NOT exist, should not fail
+		fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, true);
+
+		Processor processor = dataflow.getMainWorkflow().getProcessors().first();
+		Edit<Processor> renameEdit = new RenameEdit<Processor>(processor,
+				processor.getName() + "-changed");
+		editManager.doDataflowEdit(dataflow, renameEdit);
+
+		// Last save was OURs, so should *not* fail - even if we now use
+		// the specific saveDataflow() method
+		fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, true);
+
+		//Thread.sleep(1500);
+		WorkflowBundle otherFlow = openDataflow();
+		// Saving another flow to same file should still fail
+		try {
+			fileManager.saveDataflow(otherFlow,WF_BUNDLE_FILE_TYPE, dataflowFile, true);
+			fail("Should have thrown OverwriteException");
+		} catch (OverwriteException ex) {
+			// Expected
+		}
+	}
+
+	@Test(expected = OverwriteException.class)
+	public void saveOverwriteWarningFails() throws Exception {
+		WorkflowBundle dataflow = openDataflow();
+		File dataflowFile = File.createTempFile("test", ".t2flow");
+		dataflowFile.deleteOnExit();
+		// Should fail as file already exists
+		fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, true);
+	}
+
+	@Test
+	public void saveOverwriteWarningWorks() throws Exception {
+		WorkflowBundle dataflow = openDataflow();
+		File dataflowFile = File.createTempFile("test", ".t2flow");
+		dataflowFile.delete();
+		dataflowFile.deleteOnExit();
+		// File did NOT exist, should not fail
+		fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, true);
+	}
+
+	@After
+	public void stopListeningToModelMap() {
+		fileManager.removeObserver(fileManagerObserver);
+	}
+
+	protected WorkflowBundle openDataflow() throws OpenException {
+		URL url = getClass().getResource(DUMMY_WORKFLOW_T2FLOW);
+		assertNotNull(url);
+		WorkflowBundle dataflow = fileManager.openDataflow(T2_FLOW_FILE_TYPE, url);
+		assertNotNull(dataflow);
+		return dataflow;
+	}
+
+	private final class FileManagerObserver implements Observer<FileManagerEvent> {
+		protected List<FileManagerEvent> messages = new ArrayList<FileManagerEvent>();
+
+		@Override
+		public void notify(Observable<FileManagerEvent> sender, FileManagerEvent message) throws Exception {
+			messages.add(message);
+			if (message instanceof SetCurrentDataflowEvent) {
+				assertTrue("Dataflow was not listed as open when set current",
+						fileManager.getOpenDataflows().contains(
+								((SetCurrentDataflowEvent) message).getDataflow()));
+			}
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/test/resources/net/sf/taverna/t2/workbench/file/impl/dummy-workflow.t2flow
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/test/resources/net/sf/taverna/t2/workbench/file/impl/dummy-workflow.t2flow b/taverna-file-impl/src/test/resources/net/sf/taverna/t2/workbench/file/impl/dummy-workflow.t2flow
new file mode 100644
index 0000000..b9a1075
--- /dev/null
+++ b/taverna-file-impl/src/test/resources/net/sf/taverna/t2/workbench/file/impl/dummy-workflow.t2flow
@@ -0,0 +1,157 @@
+<workflow xmlns="http://taverna.sf.net/2008/xml/t2flow" version="1" producedBy="test">
+	<dataflow id="ec0991ba-275c-49ed-b1d6-38534180fb7c" role="top">
+		<name>simple_workflow_with_input</name>
+		<inputPorts>
+			<port>
+				<name>input</name>
+				<depth>0</depth>
+				<granularDepth>0</granularDepth>
+			</port>
+		</inputPorts>
+		<outputPorts>
+			<port>
+				<name>output</name>
+			</port>
+		</outputPorts>
+		<processors>
+			<processor>
+				<name>Concat_XXX</name>
+				<inputPorts>
+					<port>
+						<name>input</name>
+						<depth>0</depth>
+					</port>
+				</inputPorts>
+				<outputPorts>
+					<port>
+						<name>output</name>
+						<depth>0</depth>
+						<granularDepth>0</granularDepth>
+					</port>
+				</outputPorts>
+				<annotations />
+				<activities>
+					<activity>
+						<class>
+							net.sf.taverna.t2.activities.beanshell.BeanshellActivity
+						</class>
+						<inputMap>
+							<map from="input" to="input" />
+						</inputMap>
+						<outputMap>
+							<map from="output" to="output" />
+						</outputMap>
+						<configBean encoding="xstream">
+							<net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean
+								xmlns="">
+								<script>String output = input + "XXX";</script>
+								<dependencies />
+								<inputs>
+									<net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean>
+										<handledReferenceSchemes />
+										<translatedElementType>java.lang.String</translatedElementType>
+										<allowsLiteralValues>true</allowsLiteralValues>
+										<name>input</name>
+										<depth>0</depth>
+										<mimeTypes>
+											<string>'text/plain'</string>
+										</mimeTypes>
+									</net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean>
+								</inputs>
+								<outputs>
+									<net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean>
+										<granularDepth>0</granularDepth>
+										<name>output</name>
+										<depth>0</depth>
+										<mimeTypes>
+											<string>'text/plain'</string>
+										</mimeTypes>
+									</net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean>
+								</outputs>
+							</net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean>
+						</configBean>
+					</activity>
+				</activities>
+				<dispatchStack>
+					<dispatchLayer>
+						<class>
+							net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize
+						</class>
+						<configBean encoding="xstream">
+							<net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig
+								xmlns="">
+								<maxJobs>1</maxJobs>
+							</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig>
+						</configBean>
+					</dispatchLayer>
+					<dispatchLayer>
+						<class>
+							net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce
+						</class>
+						<configBean encoding="xstream">
+							<null xmlns="" />
+						</configBean>
+					</dispatchLayer>
+					<dispatchLayer>
+						<class>
+							net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover
+						</class>
+						<configBean encoding="xstream">
+							<null xmlns="" />
+						</configBean>
+					</dispatchLayer>
+					<dispatchLayer>
+						<class>
+							net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry
+						</class>
+						<configBean encoding="xstream">
+							<net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig
+								xmlns="">
+								<backoffFactor>1.0</backoffFactor>
+								<initialDelay>0</initialDelay>
+								<maxDelay>0</maxDelay>
+								<maxRetries>0</maxRetries>
+							</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig>
+						</configBean>
+					</dispatchLayer>
+					<dispatchLayer>
+						<class>
+							net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke
+						</class>
+						<configBean encoding="xstream">
+							<null xmlns="" />
+						</configBean>
+					</dispatchLayer>
+				</dispatchStack>
+				<iterationStrategyStack>
+					<iteration>
+						<strategy>
+							<port name="input" depth="0" />
+						</strategy>
+					</iteration>
+				</iterationStrategyStack>
+			</processor>
+		</processors>
+		<conditions />
+		<datalinks>
+			<datalink>
+				<sink type="processor">
+					<processor>Concat_XXX</processor>
+					<port>input</port>
+				</sink>
+				<source type="dataflow">
+					<port>input</port>
+				</source>
+			</datalink>
+			<datalink>
+				<sink type="dataflow">
+					<port>output</port>
+				</sink>
+				<source type="processor">
+					<processor>Concat_XXX</processor>
+					<port>output</port>
+				</source>
+			</datalink>
+		</datalinks>
+	</dataflow>
+</workflow>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-graph-model/pom.xml b/taverna-graph-model/pom.xml
new file mode 100644
index 0000000..c608f3a
--- /dev/null
+++ b/taverna-graph-model/pom.xml
@@ -0,0 +1,142 @@
+<?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>taverna-graph-model</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Graph Model</name>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>javacc-maven-plugin</artifactId>
+				<version>2.6</version>
+				<executions>
+					<execution>
+						<id>javacc</id>
+						<goals>
+							<goal>jjtree-javacc</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+		<pluginManagement>
+			<plugins>
+				<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
+				<plugin>
+					<groupId>org.eclipse.m2e</groupId>
+					<artifactId>lifecycle-mapping</artifactId>
+					<version>1.0.0</version>
+					<configuration>
+						<lifecycleMappingMetadata>
+							<pluginExecutions>
+								<pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>
+											org.codehaus.mojo
+										</groupId>
+										<artifactId>
+											javacc-maven-plugin
+										</artifactId>
+										<versionRange>
+											[2.6,)
+										</versionRange>
+										<goals>
+											<goal>jjtree-javacc</goal>
+										</goals>
+									</pluginExecutionFilter>
+									<action>
+										<execute>
+											<runOnIncremental>false</runOnIncremental>																				
+										</execute>
+									</action>
+								</pluginExecution>
+							</pluginExecutions>
+						</lifecycleMappingMetadata>
+					</configuration>
+				</plugin>
+			</plugins>
+		</pluginManagement>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-edits-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-menu-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-selection-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-onfiguration-impl</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-observer</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna.io</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+			<version>${taverna.language.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.batik</groupId>
+			<artifactId>batik-osgi</artifactId>
+			<version>${batik.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-beanutils</groupId>
+			<artifactId>commons-beanutils</artifactId>
+			<version>${commons.beanutils.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>${log4j.version}</version>
+		</dependency>
+
+	</dependencies>
+</project>


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewEditPasswordEntryDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewEditPasswordEntryDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewEditPasswordEntryDialog.java
new file mode 100644
index 0000000..3e80f8f
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewEditPasswordEntryDialog.java
@@ -0,0 +1,397 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.WEST;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ALERT_TITLE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.WARN_TITLE;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+import javax.swing.JPanel;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+
+import org.apache.log4j.Logger;
+
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+/**
+ * Dialog used for editing or entering new service URI, username or password for
+ * a password entry.
+ *
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class NewEditPasswordEntryDialog extends NonBlockedHelpEnabledDialog
+{
+	private static final Logger logger = Logger
+			.getLogger(NewEditPasswordEntryDialog.class);
+	/** 'Edit' mode constant - the dialog is in the 'edit' entry mode */
+	private static final String EDIT_MODE = "EDIT";
+	/** 'New' mode constant - the dialog is in the 'new' entry mode */
+	private static final String NEW_MODE = "NEW";
+
+	/**
+	 * Mode of this dialog - {@link #NEW_MODE} for entering new password entry
+	 * and {@link #EDIT_MODE} for editting an existing password entry
+	 */
+	String mode;
+	/** Service URI field */
+	private JTextField serviceURIField;
+	/** Username field */
+	private JTextField usernameField;
+	/** First password entry field */
+	private JPasswordField passwordField;
+	/** Password confirmation entry field */
+	private JPasswordField passwordConfirmField;
+	/** Stores service URI entered */
+	private URI serviceURI;
+	/** Stores previous service URI for {@link #EDIT_MODE} */
+	private URI serviceURIOld;
+	/** Stores username entered */
+	private String username;
+    /** Stores password entered*/
+    private String password;
+    private CredentialManager credentialManager;
+
+	public NewEditPasswordEntryDialog(JFrame parent, String title,
+			boolean modal, URI currentURI, String currentUsername,
+			String currentPassword, CredentialManager credentialManager) {
+		super(parent, title, modal);
+		serviceURI = currentURI;
+		username = currentUsername;
+		password = currentPassword;
+		this.credentialManager = credentialManager;
+		if (serviceURI == null && username == null && password == null) {
+			// if passed values are all null
+        	mode = NEW_MODE; // dialog is for entering a new password entry
+		} else {
+            mode = EDIT_MODE; // dialog is for editing an existing entry
+            serviceURIOld = currentURI;
+        }
+        initComponents();
+    }
+
+	public NewEditPasswordEntryDialog(JDialog parent, String title,
+			boolean modal, URI currentURI, String currentUsername,
+			String currentPassword, CredentialManager credentialManager) {
+		super(parent, title, modal);
+        serviceURI = currentURI;
+        username = currentUsername;
+        password = currentPassword;
+		this.credentialManager = credentialManager;
+		if (serviceURI == null && username == null && password == null) {
+			// if passed values are all null
+        	mode = NEW_MODE; // dialog is for entering new password entry
+		} else {
+            mode = EDIT_MODE; // dialog is for editing existing entry
+            serviceURIOld = currentURI;
+        }
+        initComponents();
+    }
+
+	private void initComponents() {
+		getContentPane().setLayout(new BorderLayout());
+
+        JLabel serviceURILabel = new JLabel("Service URI");
+        serviceURILabel.setBorder(new EmptyBorder(0,5,0,0));
+
+        JLabel usernameLabel = new JLabel("Username");
+        usernameLabel.setBorder(new EmptyBorder(0,5,0,0));
+
+        JLabel passwordLabel = new JLabel("Password");
+        passwordLabel.setBorder(new EmptyBorder(0,5,0,0));
+
+        JLabel passwordConfirmLabel = new JLabel("Confirm password");
+        passwordConfirmLabel.setBorder(new EmptyBorder(0,5,0,0));
+
+        serviceURIField = new JTextField();
+        //jtfServiceURI.setBorder(new EmptyBorder(0,0,0,5));
+
+        usernameField = new JTextField(15);
+        //jtfUsername.setBorder(new EmptyBorder(0,0,0,5));
+
+        passwordField = new JPasswordField(15);
+        //jpfFirstPassword.setBorder(new EmptyBorder(0,0,0,5));
+
+        passwordConfirmField = new JPasswordField(15);
+        //jpfConfirmPassword.setBorder(new EmptyBorder(0,0,0,5));
+
+        //If in EDIT_MODE - populate the fields with current values
+		if (mode.equals(EDIT_MODE)) {
+			serviceURIField.setText(serviceURI.toASCIIString());
+			usernameField.setText(username);
+			passwordField.setText(password);
+			passwordConfirmField.setText(password);
+		}
+
+		JButton okButton = new JButton("OK");
+		okButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				okPressed();
+			}
+		});
+
+		JButton cancelButton = new JButton("Cancel");
+		cancelButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				cancelPressed();
+			}
+		});
+
+		JPanel passwordPanel = new JPanel(new GridBagLayout());
+
+		GridBagConstraints gbc = new GridBagConstraints();
+		gbc.weighty = 0.0;
+
+		gbc.weightx = 0.0;
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.fill = NONE;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 0);
+        passwordPanel.add(serviceURILabel, gbc);
+
+		gbc.weightx = 1.0;
+		gbc.gridx = 1;
+		gbc.gridy = 0;
+		gbc.fill = HORIZONTAL;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 5);
+        passwordPanel.add(serviceURIField, gbc);
+
+		gbc.weightx = 0.0;
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.fill = NONE;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 0);
+        passwordPanel.add(usernameLabel, gbc);
+
+		gbc.weightx = 1.0;
+		gbc.gridx = 1;
+		gbc.gridy = 1;
+		gbc.fill = HORIZONTAL;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 5);
+        passwordPanel.add(usernameField, gbc);
+
+		gbc.weightx = 0.0;
+		gbc.gridx = 0;
+		gbc.gridy = 2;
+		gbc.fill = NONE;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 0);
+        passwordPanel.add(passwordLabel, gbc);
+
+		gbc.weightx = 1.0;
+		gbc.gridx = 1;
+		gbc.gridy = 2;
+		gbc.fill = HORIZONTAL;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 5);
+        passwordPanel.add(passwordField, gbc);
+
+		gbc.weightx = 0.0;
+		gbc.gridx = 0;
+		gbc.gridy = 3;
+		gbc.fill = NONE;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 0);
+        passwordPanel.add(passwordConfirmLabel, gbc);
+
+		gbc.weightx = 1.0;
+		gbc.gridx = 1;
+		gbc.gridy = 3;
+		gbc.fill = HORIZONTAL;
+		gbc.anchor = WEST;
+		gbc.insets = new Insets(5, 10, 0, 5);
+		passwordPanel.add(passwordConfirmField, gbc);
+
+		passwordPanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10,
+				10), new EtchedBorder()));
+
+		JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		buttonsPanel.add(okButton);
+		buttonsPanel.add(cancelButton);
+
+		getContentPane().add(passwordPanel, CENTER);
+		getContentPane().add(buttonsPanel, SOUTH);
+
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeDialog();
+			}
+		});
+
+        //setResizable(false);
+        getRootPane().setDefaultButton(okButton);
+        pack();
+    }
+
+	/**
+	 * Get the username entered in the dialog.
+	 */
+	public String getUsername() {
+		return username;
+	}
+
+	/**
+	 * Get the service URI entered in the dialog.
+	 */
+	public URI getServiceURI() {
+		return serviceURI;
+	}
+
+	/**
+	 * Get the password entered in the dialog.
+	 */
+	public String getPassword() {
+		return password;
+	}
+
+	/**
+	 * Checks that the user has entered a non-empty service URI, a non-empty
+	 * username, a non-empty password and that an entry with the same URI
+	 * already does not already exist in the Keystore. Store the new password.
+	 */
+	private boolean checkControls() {
+		String serviceURIString = new String(serviceURIField.getText());
+		if (serviceURIString.isEmpty()) {
+			showMessageDialog(this, "Service URI cannot be empty",
+					WARN_TITLE, WARNING_MESSAGE);
+			return false;
+		}
+    	try {
+			serviceURI = new URI(serviceURIString);
+		} catch (URISyntaxException e) {
+			showMessageDialog(this, "Service URI is not a valid URI",
+					WARN_TITLE, WARNING_MESSAGE);
+			return false;
+		}
+
+		username = new String(usernameField.getText());
+		if (username.isEmpty()) {
+			showMessageDialog(this, "Username cannot be empty", WARN_TITLE,
+					WARNING_MESSAGE);
+			return false;
+		}
+
+		String firstPassword = new String(passwordField.getPassword());
+		String confirmPassword = new String(passwordConfirmField.getPassword());
+
+		if (!firstPassword.equals(confirmPassword)) {
+			// passwords do not match
+			showMessageDialog(this, "Passwords do not match", WARN_TITLE,
+					WARNING_MESSAGE);
+			return false;
+		}
+		if (firstPassword.isEmpty()) {
+			// passwords match but are empty
+			showMessageDialog(this, "Password cannot be empty", WARN_TITLE,
+					WARNING_MESSAGE);
+			return false;
+		}
+
+		// passwords the same and non-empty
+		password = firstPassword;
+
+		// Check if the entered service URL is already associated with another password entry in the Keystore
+    	List<URI> uriList = null;
+    	try {
+			uriList = credentialManager.getServiceURIsForAllUsernameAndPasswordPairs();
+		} catch (CMException cme) {
+			// Failed to instantiate Credential Manager - warn the user and exit
+			String exMessage = "Failed to instantiate Credential Manager to check for duplicate service URIs.";
+			logger.error(exMessage, cme);
+			showMessageDialog(new JFrame(), exMessage, ERROR_TITLE,
+					ERROR_MESSAGE);
+			return false;
+		}
+
+       	if (uriList != null) { // should not be null really (although can be empty). Check anyway.
+       		if (mode.equals(EDIT_MODE)) // edit mode
+            	// Remove the current entry's service URI from the list
+                uriList.remove(serviceURIOld);
+
+   			if (uriList.contains(serviceURI)) { // found another entry for this service URI
+        		// Warn the user and exit
+				showMessageDialog(
+						this,
+						"The entered service URI is already associated with another password entry",
+						ALERT_TITLE, WARNING_MESSAGE);
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	private void okPressed() {
+		if (checkControls())
+			closeDialog();
+	}
+
+	private void cancelPressed() {
+    	// Set all fields to null to indicate that cancel button was pressed
+		serviceURI = null;
+		username = null;
+		password = null;
+		closeDialog();
+	}
+
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewKeyPairEntryDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewKeyPairEntryDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewKeyPairEntryDialog.java
new file mode 100644
index 0000000..36e3015
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewKeyPairEntryDialog.java
@@ -0,0 +1,304 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.BorderLayout.WEST;
+import static java.awt.Font.PLAIN;
+import static javax.swing.BoxLayout.Y_AXIS;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.ListSelectionModel.SINGLE_SELECTION;
+import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
+import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ALERT_TITLE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser;
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+/**
+ * Allows the user import a key pair from a PKCS #12 file (keystore).
+ */
+@SuppressWarnings("serial")
+class NewKeyPairEntryDialog extends NonBlockedHelpEnabledDialog {
+	//private static final Logger logger = Logger.getLogger(NewKeyPairEntryDialog.class);
+
+	/** List of key pairs available for import */
+	private JList<String> keyPairsJList;
+	/** PKCS #12 keystore */
+	private KeyStore pkcs12KeyStore;
+	/** Private key part of the key pair chosen by the user for import */
+	private Key privateKey;
+	/** Certificate chain part of the key pair chosen by the user for import */
+	private Certificate[] certificateChain;
+	/** Key pair alias to be used for this entry in the Keystore */
+	private String alias;
+	private final DistinguishedNameParser dnParser;
+
+	public NewKeyPairEntryDialog(JFrame parent, String title, boolean modal,
+			KeyStore pkcs12KeyStore, DistinguishedNameParser dnParser)
+			throws CMException {
+		super(parent, title, modal);
+		this.pkcs12KeyStore = pkcs12KeyStore;
+		this.dnParser = dnParser;
+		initComponents();
+	}
+
+	public NewKeyPairEntryDialog(JDialog parent, String title, boolean modal,
+			KeyStore pkcs12KeyStore, DistinguishedNameParser dnParser)
+			throws CMException {
+		super(parent, title, modal);
+		this.pkcs12KeyStore = pkcs12KeyStore;
+		this.dnParser = dnParser;
+		initComponents();
+	}
+
+	/**
+	 * Get the private part of the key pair.
+	 */
+	public Key getPrivateKey() {
+		return privateKey;
+	}
+
+	/**
+	 * Get the certificate chain part of the key pair.
+	 */
+	public Certificate[] getCertificateChain() {
+		return certificateChain;
+	}
+
+	/**
+	 * Get the keystore alias of the key pair.
+	 */
+	public String getAlias() {
+		return alias;
+	}
+
+	private void initComponents() throws CMException {
+		// Instructions
+		JLabel instructionsLabel = new JLabel("Select a key pair to import:");
+		instructionsLabel.setFont(new Font(null, PLAIN, 11));
+		instructionsLabel.setBorder(new EmptyBorder(5, 5, 5, 5));
+		JPanel instructionsPanel = new JPanel(new BorderLayout());
+		instructionsPanel.add(instructionsLabel, WEST);
+
+		// Import button
+		final JButton importButton = new JButton("Import");
+		importButton.setEnabled(false);
+		importButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				importPressed();
+			}
+		});
+
+		// Certificate details button
+		final JButton certificateDetailsButton = new JButton("Details");
+		certificateDetailsButton.setEnabled(false);
+		certificateDetailsButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				certificateDetailsPressed();
+			}
+		});
+
+        // List to hold keystore's key pairs
+		keyPairsJList = new JList<>();
+		keyPairsJList.setSelectionMode(SINGLE_SELECTION);
+		keyPairsJList.addListSelectionListener(new ListSelectionListener() {
+			@Override
+			public void valueChanged(ListSelectionEvent evt) {
+				boolean enabled = keyPairsJList.getSelectedIndex() >= 0;
+				importButton.setEnabled(enabled);
+				certificateDetailsButton.setEnabled(enabled);
+			}
+		});
+
+        // Put the key list into a scroll pane
+		JScrollPane keyPairsScrollPane = new JScrollPane(keyPairsJList,
+				VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		keyPairsScrollPane.getViewport().setBackground(
+				keyPairsJList.getBackground());
+        
+        JPanel keyPairsPanel = new JPanel();
+        keyPairsPanel.setLayout(new BoxLayout(keyPairsPanel, Y_AXIS));
+		keyPairsPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
+
+		instructionsPanel.setAlignmentY(LEFT_ALIGNMENT);
+		keyPairsPanel.add(instructionsPanel);
+		keyPairsScrollPane.setAlignmentY(LEFT_ALIGNMENT);
+		keyPairsPanel.add(keyPairsScrollPane);
+
+		// Cancel button
+		final JButton cancelButton = new JButton("Cancel");
+		cancelButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				cancelPressed();
+			}
+		});
+
+		JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		buttonsPanel.add(certificateDetailsButton);
+		buttonsPanel.add(importButton);
+		buttonsPanel.add(cancelButton);
+
+		getContentPane().setLayout(new BorderLayout());
+		getContentPane().add(keyPairsPanel, CENTER);
+		getContentPane().add(buttonsPanel, SOUTH);
+
+		// Populate the list
+		populateKeyPairList();
+
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeDialog();
+			}
+		});
+
+		setResizable(false);
+		getRootPane().setDefaultButton(importButton);
+		pack();
+	}
+
+	/**
+	 * Populate the key pair list with the PKCS #12 keystore's key pair aliases.
+	 */
+	private void populateKeyPairList() throws CMException {
+		try {
+			List<String> keyPairAliases = new ArrayList<>();
+
+			Enumeration<String> aliases = pkcs12KeyStore.aliases();
+			while (aliases.hasMoreElements()) {
+				String alias = aliases.nextElement();
+
+				if (pkcs12KeyStore.isKeyEntry(alias)) {
+					pkcs12KeyStore.getKey(alias, new char[] {});
+					Certificate[] certs = pkcs12KeyStore
+							.getCertificateChain(alias);
+					if (certs != null && certs.length != 0)
+						keyPairAliases.add(alias);
+				}
+			}
+
+            if (!keyPairAliases.isEmpty()) {
+                keyPairsJList.setListData(keyPairAliases.toArray(new String[0]));
+                keyPairsJList.setSelectedIndex(0);
+			} else
+                // No key pairs were found - warn the user
+				showMessageDialog(this,
+						"No private key pairs were found in the file",
+						ALERT_TITLE, WARNING_MESSAGE);
+		} catch (GeneralSecurityException ex) {
+            throw new CMException("Problem occured while reading the PKCS #12 file.",
+                ex);
+        }
+    }
+
+	/**
+	 * Display the selected key pair's certificate.
+	 */
+	private void certificateDetailsPressed() {
+		try {
+			String alias = (String) keyPairsJList.getSelectedValue();
+
+			// Convert the certificate object into an X509Certificate object.
+			X509Certificate cert = dnParser.convertCertificate(pkcs12KeyStore
+					.getCertificate(alias));
+
+			ViewCertDetailsDialog viewCertificateDialog = new ViewCertDetailsDialog(
+					this, "Certificate details", true, (X509Certificate) cert,
+					null, dnParser);
+			viewCertificateDialog.setLocationRelativeTo(this);
+			viewCertificateDialog.setVisible(true);
+		} catch (Exception ex) {
+			showMessageDialog(this,
+					"Failed to obtain certificate details to show",
+					ALERT_TITLE, WARNING_MESSAGE);
+			closeDialog();
+		}
+	}
+
+	public void importPressed() {
+		String alias = (String) keyPairsJList.getSelectedValue();
+		try {
+			privateKey = pkcs12KeyStore.getKey(alias, new char[] {});
+			certificateChain = pkcs12KeyStore.getCertificateChain(alias);
+			this.alias = alias;
+		} catch (Exception ex) {
+			showMessageDialog(
+					this,
+					"Failed to load the private key and certificate chain from the PKCS #12 file.",
+					ERROR_TITLE, ERROR_MESSAGE);
+		}
+
+        closeDialog();
+    }
+
+	public void cancelPressed() {
+		/*
+		 * Set everything to null, just in case some of the values have been set
+		 * previously and the user pressed 'cancel' after that.
+		 */
+		privateKey = null;
+		certificateChain = null;
+		closeDialog();
+	}
+
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewTrustCertsDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewTrustCertsDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewTrustCertsDialog.java
new file mode 100644
index 0000000..26854ec
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewTrustCertsDialog.java
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.BorderLayout.WEST;
+import static java.awt.Font.PLAIN;
+import static javax.security.auth.x500.X500Principal.RFC2253;
+import static javax.swing.BoxLayout.Y_AXIS;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.ListSelectionModel.MULTIPLE_INTERVAL_SELECTION;
+import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
+import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ALERT_TITLE;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser;
+import net.sf.taverna.t2.security.credentialmanager.ParsedDistinguishedName;
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+/**
+ * Allows the user to import one or more trusted certificates from a file.
+ */
+@SuppressWarnings("serial")
+public class NewTrustCertsDialog extends NonBlockedHelpEnabledDialog {
+	private JList<String> trustedCertsJList;
+	/** List of trusted certs read from the file and available for import */
+	private ArrayList<X509Certificate> availableTrustedCerts = new ArrayList<>();
+	/** List of trusted certs selected for import */
+	private ArrayList<X509Certificate> selectedTrustedCerts;
+	private final DistinguishedNameParser dnParser;
+
+	public NewTrustCertsDialog(JFrame parent, String title, boolean modal,
+			ArrayList<X509Certificate> lCerts, DistinguishedNameParser dnParser) {
+		super(parent, title, modal);
+		availableTrustedCerts = lCerts;
+		this.dnParser = dnParser;
+		initComponents();
+	}
+
+	public NewTrustCertsDialog(JDialog parent, String title, boolean modal,
+			ArrayList<X509Certificate> lCerts, DistinguishedNameParser dnParser) {
+		super(parent, title, modal);
+		availableTrustedCerts = lCerts;
+		this.dnParser = dnParser;
+		initComponents();
+	}
+
+	private void initComponents() {
+		// Instructions
+		JLabel instructionsLabel = new JLabel(
+				"Select one or more certificates for import:");
+		instructionsLabel.setFont(new Font(null, PLAIN, 11));
+		instructionsLabel.setBorder(new EmptyBorder(5, 5, 5, 5));
+		JPanel instructionsPanel = new JPanel(new BorderLayout());
+		instructionsPanel.add(instructionsLabel, WEST);
+
+		// Import button
+		final JButton importButton = new JButton("Import");
+		importButton.setEnabled(false);
+		importButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				importPressed();
+			}
+		});
+
+		// Certificate details button
+		final JButton certificateDetailsButton = new JButton(
+				"Certificate Details");
+		certificateDetailsButton.setEnabled(false);
+		certificateDetailsButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				certificateDetailsPressed();
+			}
+		});
+
+		// List with trusted certs' aliases
+		trustedCertsJList = new JList<>();
+		trustedCertsJList.setSelectionMode(MULTIPLE_INTERVAL_SELECTION);
+		trustedCertsJList.addListSelectionListener(new ListSelectionListener() {
+			@Override
+			public void valueChanged(ListSelectionEvent evt) {
+				boolean enabled = trustedCertsJList.getSelectedIndex() >= 0;
+				importButton.setEnabled(enabled);
+				certificateDetailsButton.setEnabled(enabled);
+			}
+		});
+		// Populate the list - get the certificate subjects' CNs
+		ArrayList<String> cns = new ArrayList<>();
+		for (int i = 0; i < availableTrustedCerts.size(); i++) {
+			String subjectDN = ((X509Certificate) availableTrustedCerts.get(i))
+					.getSubjectX500Principal().getName(RFC2253);
+			ParsedDistinguishedName parsedDN = dnParser.parseDN(subjectDN);
+			String subjectCN = parsedDN.getCN();
+			cns.add(i, subjectCN);
+		}
+		trustedCertsJList.setListData(cns.toArray(new String[0]));
+		trustedCertsJList.setSelectedIndex(0);
+
+		// Put the list into a scroll pane
+		JScrollPane trustedCertsScrollPanel = new JScrollPane(
+				trustedCertsJList, VERTICAL_SCROLLBAR_AS_NEEDED,
+				HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		trustedCertsScrollPanel.getViewport().setBackground(
+				trustedCertsJList.getBackground());
+
+		JPanel trustedCertsPanel = new JPanel();
+		trustedCertsPanel.setLayout(new BoxLayout(trustedCertsPanel, Y_AXIS));
+		trustedCertsPanel.setBorder(new CompoundBorder(new CompoundBorder(
+				new EmptyBorder(5, 5, 5, 5), new EtchedBorder()),
+				new EmptyBorder(5, 5, 5, 5)));
+
+		instructionsPanel.setAlignmentY(LEFT_ALIGNMENT);
+		trustedCertsPanel.add(instructionsPanel);
+		trustedCertsScrollPanel.setAlignmentY(LEFT_ALIGNMENT);
+		trustedCertsPanel.add(trustedCertsScrollPanel);
+		certificateDetailsButton.setAlignmentY(RIGHT_ALIGNMENT);
+		trustedCertsPanel.add(certificateDetailsButton);
+
+		// Cancel button
+		final JButton cancelButton = new JButton("Cancel");
+		cancelButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				cancelPressed();
+			}
+		});
+
+		JPanel jpButtons = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		jpButtons.add(importButton);
+		jpButtons.add(cancelButton);
+
+		getContentPane().setLayout(new BorderLayout());
+		getContentPane().add(trustedCertsPanel, CENTER);
+		getContentPane().add(jpButtons, SOUTH);
+
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeDialog();
+			}
+		});
+
+		setResizable(false);
+		getRootPane().setDefaultButton(importButton);
+		pack();
+	}
+
+	/**
+	 * Shows the selected key pair's certificate.
+	 */
+	private void certificateDetailsPressed() {
+		try {
+			int i = trustedCertsJList.getSelectedIndex();
+
+			X509Certificate cert = (X509Certificate) availableTrustedCerts
+					.get(i);
+
+			ViewCertDetailsDialog viewCertificateDialog = new ViewCertDetailsDialog(
+					this, "Certificate details", true, cert, null, dnParser);
+			viewCertificateDialog.setLocationRelativeTo(this);
+			viewCertificateDialog.setVisible(true);
+		} catch (Exception ex) {
+			showMessageDialog(this,
+					"Failed to obtain certificate details to show",
+					ALERT_TITLE, WARNING_MESSAGE);
+			closeDialog();
+		}
+	}
+
+	/**
+	 * Get the trusted certificates selected for import.
+	 */
+	public ArrayList<X509Certificate> getTrustedCertificates() {
+		return selectedTrustedCerts;
+	}
+
+	/**
+	 * Store the selected trusted certs.
+	 */
+	public void importPressed() {
+		int[] selectedValues = trustedCertsJList.getSelectedIndices();
+		selectedTrustedCerts = new ArrayList<>();
+		for (int i = 0; i < selectedValues.length; i++)
+			selectedTrustedCerts.add(availableTrustedCerts
+					.get(selectedValues[i]));
+		closeDialog();
+	}
+
+	public void cancelPressed() {
+		/*
+		 * Set selectedTrustCerts to null to indicate that user has cancelled
+		 * the import
+		 */
+		selectedTrustedCerts = null;
+		closeDialog();
+	}
+
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/PasswordsTableModel.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/PasswordsTableModel.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/PasswordsTableModel.java
new file mode 100644
index 0000000..9715fc6
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/PasswordsTableModel.java
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.KEYSTORE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.PASSWORD_ENTRY_TYPE;
+
+import java.net.URI;
+import java.util.TreeMap;
+
+import javax.swing.JFrame;
+import javax.swing.table.AbstractTableModel;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.security.credentialmanager.KeystoreChangedEvent;
+import net.sf.taverna.t2.security.credentialmanager.UsernamePassword;
+
+import org.apache.log4j.Logger;
+
+/**
+ * The table model used to display the Keystore's username/password pair
+ * entries.
+ * 
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class PasswordsTableModel extends AbstractTableModel implements
+		Observer<KeystoreChangedEvent> {
+	private static final Logger logger = Logger
+			.getLogger(PasswordsTableModel.class);
+
+	// Column names
+	private String[] columnNames;
+	// Table data
+	private Object[][] data;
+	private CredentialManager credManager;
+
+	public PasswordsTableModel(CredentialManager credentialManager) {
+		credManager = credentialManager;
+		if (credentialManager == null) {
+			// Failed to instantiate Credential Manager - warn the user and exit
+			String sMessage = "Failed to instantiate Credential Manager. ";
+			logger.error("CM GUI: " + sMessage);
+			showMessageDialog(new JFrame(), sMessage, ERROR_TITLE,
+					ERROR_MESSAGE);
+			return;
+		}
+
+		data = new Object[0][0];
+		columnNames = new String[] { "Entry Type", // type of the Keystore entry
+				"Service URL", // the service url, part of the actual alias in
+								// the Keystore
+				"Username", // username for the service, part of the password
+							// entry in the Keystore
+				"Last Modified", // last modified date of the entry
+				"Password", // the invisible column holding the password value
+							// of the password entry in the Keystore
+				"Alias" // the invisible column holding the Keystore alias of
+						// the entry
+		};
+
+		try {
+			load();
+		} catch (CMException cme) {
+			String sMessage = "Failed to load username and password pairs";
+			logger.error(sMessage);
+			showMessageDialog(new JFrame(), sMessage, ERROR_TITLE,
+					ERROR_MESSAGE);
+			return;
+		}
+
+		// Start observing changes to the Keystore
+		credManager.addObserver(this);
+	}
+
+	/**
+	 * Load the PasswordsTableModel with the password entries from the Keystore.
+	 */
+	public void load() throws CMException {
+		// Place password entries' aliases in a tree map to sort them
+		TreeMap<String, String> aliases = new TreeMap<>();
+
+		for (String alias : credManager.getAliases(KEYSTORE))
+			/*
+			 * We are only interested in username/password entries here. Alias
+			 * for such entries is constructed as "password#"<SERVICE_URL> where
+			 * service URL is the service this username/password pair is to be
+			 * used for.
+			 */
+			if (alias.startsWith("password#"))
+				aliases.put(alias, alias);
+
+		// Create one table row for each password entry
+		data = new Object[aliases.size()][6];
+
+		/*
+		 * Iterate through the sorted aliases, retrieving the password entries
+		 * and populating the table model
+		 */
+		int iCnt = 0;
+		for (String alias : aliases.values()) {
+			/*
+			 * Populate the type column - it is set with an integer but a custom
+			 * cell renderer will cause a suitable icon to be displayed
+			 */
+			data[iCnt][0] = PASSWORD_ENTRY_TYPE;
+
+			/*
+			 * Populate the service URL column as a substring of alias from the
+			 * first occurrence of '#' till the end of the string
+			 */
+			String serviceURL = alias.substring(alias.indexOf('#') + 1);
+			data[iCnt][1] = serviceURL;
+
+			/*
+			 * Get the username and password pair from the Keystore. They are
+			 * returned in a single string in format
+			 * <USERNAME><SEPARATOR_CHARACTER><PASSWORD>
+			 */
+			UsernamePassword usernamePassword = credManager
+					.getUsernameAndPasswordForService(URI.create(serviceURL),
+							false, "");
+			String username = usernamePassword.getUsername();
+			String password = usernamePassword.getPasswordAsString();
+
+			// Populate the username column
+			data[iCnt][2] = username;
+
+			// Populate the last modified date column ("UBER" keystore type
+			// supports creation date)
+			// data[iCnt][3] =
+			// credManager.getEntryCreationDate(CredentialManager.KEYSTORE,
+			// alias);
+
+			// Populate the invisible password column
+			data[iCnt][4] = password;
+
+			// Populate the invisible alias column
+			data[iCnt][5] = alias;
+
+			iCnt++;
+		}
+
+		fireTableDataChanged();
+	}
+
+	/**
+	 * Get the number of columns in the table.
+	 */
+	@Override
+	public int getColumnCount() {
+		return columnNames.length;
+	}
+
+	/**
+	 * Get the number of rows in the table.
+	 */
+	@Override
+	public int getRowCount() {
+		return data.length;
+	}
+
+	/**
+	 * Get the name of the column at the given position.
+	 */
+	@Override
+	public String getColumnName(int iCol) {
+		return columnNames[iCol];
+	}
+
+	/**
+	 * Get the cell value at the given row and column position.
+	 */
+	@Override
+	public Object getValueAt(int iRow, int iCol) {
+		return data[iRow][iCol];
+	}
+
+	/**
+	 * Get the class at of the cells at the given column position.
+	 */
+	@Override
+	public Class<? extends Object> getColumnClass(int iCol) {
+		return getValueAt(0, iCol).getClass();
+	}
+
+	/**
+	 * Is the cell at the given row and column position editable?
+	 */
+	@Override
+	public boolean isCellEditable(int iRow, int iCol) {
+		// The table is always read-only
+		return false;
+	}
+
+	@Override
+	public void notify(Observable<KeystoreChangedEvent> sender,
+			KeystoreChangedEvent message) throws Exception {
+		// reload the table
+		if (message.keystoreType.equals(KEYSTORE))
+			load();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/SetMasterPasswordDialog.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/SetMasterPasswordDialog.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/SetMasterPasswordDialog.java
new file mode 100644
index 0000000..bae6068
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/SetMasterPasswordDialog.java
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.Font.PLAIN;
+import static javax.swing.BoxLayout.Y_AXIS;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.WARN_TITLE;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+
+import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog;
+
+/**
+ * Dialog used for user to set a master password for Credential Manager.
+ * 
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class SetMasterPasswordDialog extends NonBlockedHelpEnabledDialog {
+	/** Password entry field */
+	private JPasswordField passwordField;
+	/** Password confirmation entry field */
+	private JPasswordField passwordConfirmField;
+	/** The entered password */
+	private String password = null;
+	/** Instructions for the user */
+	private String instructions;
+
+	public SetMasterPasswordDialog(JFrame parent, String title, boolean modal,
+			String instructions) {
+		super(parent, title, modal);
+		this.instructions = instructions;
+		initComponents();
+	}
+
+	private void initComponents() {
+		getContentPane().setLayout(new BorderLayout());
+
+		JLabel instructionsLabel = new JLabel(instructions);
+		instructionsLabel.setFont(new Font(null, PLAIN, 11));
+
+		JPanel instructionsPanel = new JPanel();
+		instructionsPanel.setLayout(new BoxLayout(instructionsPanel, Y_AXIS));
+		instructionsPanel.add(instructionsLabel);
+		instructionsPanel.setBorder(new EmptyBorder(10, 5, 10, 0));
+
+		JLabel passwordLabel = new JLabel("Master password");
+		passwordLabel.setBorder(new EmptyBorder(0, 5, 0, 0));
+
+		JLabel passwordConfirmLabel = new JLabel("Confirm master password");
+		passwordConfirmLabel.setBorder(new EmptyBorder(0, 5, 0, 0));
+
+		passwordField = new JPasswordField(15);
+		passwordConfirmField = new JPasswordField(15);
+
+		JPanel passwordPanel = new JPanel(new GridLayout(2, 2, 5, 5));
+		passwordPanel.add(passwordLabel);
+		passwordPanel.add(passwordField);
+		passwordPanel.add(passwordConfirmLabel);
+		passwordPanel.add(passwordConfirmField);
+
+		JPanel mainPanel = new JPanel(new BorderLayout());
+		mainPanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, 10),
+				new EtchedBorder()));
+		mainPanel.add(instructionsPanel, NORTH);
+		mainPanel.add(passwordPanel, CENTER);
+
+		JButton okButton = new JButton("OK");
+		okButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				okPressed();
+			}
+		});
+
+		JButton cancelButton = new JButton("Cancel");
+		cancelButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				cancelPressed();
+			}
+		});
+		JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		buttonsPanel.add(okButton);
+		buttonsPanel.add(cancelButton);
+
+		getContentPane().add(mainPanel, CENTER);
+		getContentPane().add(buttonsPanel, SOUTH);
+
+		addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent evt) {
+				closeDialog();
+			}
+		});
+
+		setResizable(false);
+		getRootPane().setDefaultButton(okButton);
+		pack();
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	/**
+	 * Check that the user has entered a non-empty password and store the new
+	 * password.
+	 */
+	private boolean checkPassword() {
+		String firstPassword = new String(passwordField.getPassword());
+		String confirmPassword = new String(passwordConfirmField.getPassword());
+
+		if (!firstPassword.equals(confirmPassword)) {
+			showMessageDialog(this, "The passwords do not match", WARN_TITLE,
+					WARNING_MESSAGE);
+			return false;
+		}
+		if (firstPassword.isEmpty()) {
+			// passwords match but are empty
+			showMessageDialog(this, "The password cannot be empty", WARN_TITLE,
+					WARNING_MESSAGE);
+			return false;
+		}
+
+		// passwords match and not empty
+		password = firstPassword;
+		return true;
+	}
+
+	private void okPressed() {
+		if (checkPassword())
+			closeDialog();
+	}
+
+	private void cancelPressed() {
+		/*
+		 * Set the password to null as it might have changed in the meantime if
+		 * user entered something then cancelled.
+		 */
+		password = null;
+		closeDialog();
+	}
+
+	private void closeDialog() {
+		setVisible(false);
+		dispose();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableCellRenderer.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableCellRenderer.java
new file mode 100644
index 0000000..0eaae99
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableCellRenderer.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.KEY_PAIR_ENTRY_TYPE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.PASSWORD_ENTRY_TYPE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.TRUST_CERT_ENTRY_TYPE;
+
+import java.awt.Component;
+//import java.text.DateFormat;
+//import java.util.Date;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.border.EmptyBorder;
+import javax.swing.table.DefaultTableCellRenderer;
+//import net.sf.taverna.t2.workbench.ui.credentialmanager.KeyPairsTableModel;
+//import net.sf.taverna.t2.workbench.ui.credentialmanager.PasswordsTableModel;
+//import net.sf.taverna.t2.workbench.ui.credentialmanager.TrustedCertsTableModel;
+
+/**
+ * Custom cell renderer for the cells of the tables displaying
+ * Keystore/Truststore contents.
+ * 
+ * @author Alex Nenadic
+ */
+public class TableCellRenderer extends DefaultTableCellRenderer {
+	private static final long serialVersionUID = -3983986682794010259L;
+
+	private final ImageIcon passwordEntryIcon = new ImageIcon(
+			TableCellRenderer.class.getResource("/images/table/key_entry.png"));
+	private final ImageIcon keypairEntryIcon = new ImageIcon(
+			TableCellRenderer.class
+					.getResource("/images/table/keypair_entry.png"));
+	private final ImageIcon trustcertEntryIcon = new ImageIcon(
+			TableCellRenderer.class
+					.getResource("/images/table/trustcert_entry.png"));
+
+	/**
+	 * Get the rendered cell for the supplied value and column.
+	 */
+	@Override
+	public Component getTableCellRendererComponent(JTable keyStoreTable,
+			Object value, boolean bIsSelected, boolean bHasFocus, int iRow,
+			int iCol) {
+		JLabel cell = (JLabel) super.getTableCellRendererComponent(
+				keyStoreTable, value, bIsSelected, bHasFocus, iRow, iCol);
+
+		if (value != null) {
+        	// Type column - display an icon representing the type
+			if (iCol == 0)
+				configureTypeColumn(value, cell);
+            // Last Modified column - format date (if date supplied)        
+            /*else if (((keyStoreTable.getModel() instanceof PasswordsTableModel) && (iCol == 3)) || 
+            	((keyStoreTable.getModel() instanceof KeyPairsTableModel) && (iCol == 4))||
+            	((keyStoreTable.getModel() instanceof TrustedCertsTableModel) && (iCol == 4))){
+            	if (value instanceof Date) {
+            		// Include timezone
+            		cell.setText(DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
+            			DateFormat.LONG).format((Date) value));
+            	} else {
+            		cell.setText(value.toString());
+            	}
+            }*/
+            // Other columns - just use their text values
+			else
+				cell.setText(value.toString());
+		}
+
+		cell.setBorder(new EmptyBorder(0, 5, 0, 5));
+		return cell;
+	}
+
+	private void configureTypeColumn(Object value, JLabel cell) {
+		ImageIcon icon = null;
+		// The cell is in the first column of Passwords table
+		if (PASSWORD_ENTRY_TYPE.equals(value)) {
+			icon = passwordEntryIcon; // key (i.e. password) entry image
+		}
+		// The cell is in the first column of Key Pairs table
+		else if (KEY_PAIR_ENTRY_TYPE.equals(value)) {
+			icon = keypairEntryIcon; // key pair entry image
+		}
+		// The cell is in the first column of Trusted Certificates table
+		else if (TRUST_CERT_ENTRY_TYPE.equals(value)) {
+			icon = trustcertEntryIcon; // trust. certificate entry image
+		}
+
+		cell.setIcon(icon);
+		cell.setText("");
+		cell.setVerticalAlignment(CENTER);
+		cell.setHorizontalAlignment(CENTER);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableHeaderRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableHeaderRenderer.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableHeaderRenderer.java
new file mode 100644
index 0000000..8070b98
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableHeaderRenderer.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static javax.swing.border.BevelBorder.RAISED;
+
+import java.awt.Component;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.border.BevelBorder;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.table.DefaultTableCellRenderer;
+
+import net.sf.taverna.t2.workbench.ui.credentialmanager.KeyPairsTableModel;
+import net.sf.taverna.t2.workbench.ui.credentialmanager.PasswordsTableModel;
+import net.sf.taverna.t2.workbench.ui.credentialmanager.TableHeaderRenderer;
+import net.sf.taverna.t2.workbench.ui.credentialmanager.TrustedCertsTableModel;
+
+/**
+ * Custom cell renderer for the headers of the tables displaying 
+ * the Keystore/Truststore contents.
+ */
+@SuppressWarnings("serial")
+public class TableHeaderRenderer extends DefaultTableCellRenderer {
+	private final ImageIcon entryTypeIcon = new ImageIcon(
+			TableHeaderRenderer.class
+					.getResource("/images/table/entry_heading.png"));
+
+	@Override
+	public Component getTableCellRendererComponent(JTable jtKeyStoreTable,
+			Object value, boolean bIsSelected, boolean bHasFocus, int iRow,
+			int iCol) {
+        // Get header renderer
+        JLabel header = (JLabel) jtKeyStoreTable.getColumnModel().getColumn(iCol).getHeaderRenderer();
+
+        // The entry type header contains an icon for every table
+        if (iCol == 0) {
+            header.setText("");
+            header.setIcon(entryTypeIcon); // entry type icon (header for the first column of the table)
+            header.setHorizontalAlignment(CENTER);
+            header.setVerticalAlignment(CENTER);
+            header.setToolTipText("Entry type");
+        }
+        // All other headers contain text
+        else {
+            header.setText((String) value);
+            header.setHorizontalAlignment(LEFT);
+            
+            // Passwords table
+            if (jtKeyStoreTable.getModel() instanceof PasswordsTableModel){
+                if (iCol == 1) //Service URL column
+					header.setToolTipText("URL of the service username and password will be used for");
+				else if (iCol == 2) // Username column
+					header.setToolTipText("Username for the service");
+			}
+            // Key pairs table
+			else if (jtKeyStoreTable.getModel() instanceof KeyPairsTableModel) {
+				if (iCol == 1) // Owner
+					header.setToolTipText("Certificate's owner");
+				else if (iCol == 2) // Issuer
+					header.setToolTipText("Certificate's issuer");
+				else if (iCol == 3) // Serial number
+					header.setToolTipText("Certificate's serial number");
+            }       
+            // Trusted certs table
+			else if (jtKeyStoreTable.getModel() instanceof TrustedCertsTableModel) {
+				if (iCol == 1) // Owner
+					header.setToolTipText("Certificate's owner");
+				else if (iCol == 2) // Issuer
+					header.setToolTipText("Certificate's issuer");
+				else if (iCol == 3) // Serial number
+					header.setToolTipText("Certificate's serial number");
+            }         
+        }
+		header.setBorder(new CompoundBorder(new BevelBorder(RAISED),
+				new EmptyBorder(0, 5, 0, 5)));
+		return header;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TrustedCertsTableModel.java
----------------------------------------------------------------------
diff --git a/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TrustedCertsTableModel.java b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TrustedCertsTableModel.java
new file mode 100644
index 0000000..5189eb2
--- /dev/null
+++ b/taverna-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TrustedCertsTableModel.java
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * 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.ui.credentialmanager;
+
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.TRUSTSTORE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE;
+import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.TRUST_CERT_ENTRY_TYPE;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.swing.JFrame;
+import javax.swing.table.AbstractTableModel;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.security.credentialmanager.KeystoreChangedEvent;
+
+import org.apache.log4j.Logger;
+
+/**
+ * The table model used to display the Keystore's trusted certificate entries.
+ * 
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class TrustedCertsTableModel extends AbstractTableModel implements
+		Observer<KeystoreChangedEvent> {
+	private static final Logger logger = Logger
+			.getLogger(TrustedCertsTableModel.class);
+
+	// Column names
+	private String[] columnNames;
+	// Table data
+	private Object[][] data;
+	private CredentialManager credManager;
+
+	public TrustedCertsTableModel(CredentialManager credentialManager) {
+		credManager = credentialManager;
+		if (credentialManager == null) {
+			// Failed to instantiate Credential Manager - warn the user and exit
+			String sMessage = "Failed to instantiate Credential Manager. ";
+			logger.error("CM GUI: "+ sMessage);
+			showMessageDialog(new JFrame(), sMessage, ERROR_TITLE,
+					ERROR_MESSAGE);
+			return;
+        }
+
+       	data = new Object[0][0];
+        columnNames = new String[] {
+        	"Entry Type", // type of the Keystore entry
+        	"Owner", // owner's common name
+        	"Issuer", // issuer's common name
+        	"Serial Number", // public key certificate's serial number
+            "Last Modified", // last modified date of the entry
+            "Alias" // the invisible column holding the actual alias in the Keystore
+        };
+
+        try {
+			load();
+		} catch (CMException cme) {
+			String sMessage = "Failed to load trusted certificates";
+			logger.error(sMessage);
+			showMessageDialog(new JFrame(), sMessage, ERROR_TITLE,
+					ERROR_MESSAGE);
+			return;
+		}
+
+        // Start observing changes to the Keystore
+        credManager.addObserver(this);
+    }
+
+    /**
+     * Load the TrustCertsTableModel with trusted certificate entries from the Keystore.
+     */
+	public void load() throws CMException {
+		/*
+		 * Place trusted certificate entries' aliases in a tree map to sort them
+		 */
+		Set<String> aliases = new TreeSet<>();
+		for (String alias : credManager.getAliases(TRUSTSTORE))
+			/*
+			 * We are only interested in trusted certificate entries here. Alias
+			 * for such entries is constructed as
+			 * "trustedcert#<CERT_SERIAL_NUMBER>#<CERT_COMMON_NAME>"
+			 */
+			if (alias.startsWith("trustedcert#"))
+				aliases.add(alias);
+
+		/*
+		 * Create one table row for each trusted certificate entry Each row has
+		 * 4 fields - type, owner name, last modified data and the invisible
+		 * alias
+		 */
+		data = new Object[aliases.size()][6];
+
+		/*
+		 * Iterate through the sorted aliases, retrieving the trusted
+		 * certificate entries and populating the table model
+		 */
+		int i = 0;
+		for (String alias : aliases) {
+			/*
+			 * Populate the type column - it is set with an integer but a custom
+			 * cell renderer will cause a suitable icon to be displayed
+			 */
+			data[i][0] = TRUST_CERT_ENTRY_TYPE;
+
+			/*
+			 * Split the alias string to extract owner, issuer and serial number
+			 * alias =
+			 * "trustedcert#<CERT_SUBJECT_COMMON_NAME>"#"<CERT_ISSUER_COMMON_NAME>"
+			 * #"<CERT_SERIAL_NUMBER>
+			 */
+			String[] aliasComponents = alias.split("#");
+
+			// Populate the owner column extracted from the alias
+			data[i][1] = aliasComponents[1];
+
+			// Populate the issuer column extracted from the alias
+			data[i][2] = aliasComponents[2];
+
+			// Populate the serial number column extracted from the alias
+			data[i][3] = aliasComponents[3];
+
+			// Populate the modified date column
+			//data[iCnt][4] = credManager.getEntryCreationDate(CredentialManager.TRUSTSTORE, alias);
+
+			// Populate the invisible alias column
+			data[i][5] = alias;
+
+			i++;
+		}
+
+        fireTableDataChanged();
+    }
+
+	/**
+	 * Get the number of columns in the table.
+	 */
+	@Override
+	public int getColumnCount() {
+		return columnNames.length;
+	}
+
+	/**
+	 * Get the number of rows in the table.
+	 */
+	@Override
+	public int getRowCount() {
+		return data.length;
+	}
+
+	/**
+	 * Get the name of the column at the given position.
+	 */
+	@Override
+	public String getColumnName(int iCol) {
+		return columnNames[iCol];
+	}
+
+	/**
+	 * Get the cell value at the given row and column position.
+	 */
+	@Override
+	public Object getValueAt(int iRow, int iCol) {
+		return data[iRow][iCol];
+	}
+
+	/**
+	 * Get the class at of the cells at the given column position.
+	 */
+	@Override
+	public Class<? extends Object> getColumnClass(int iCol) {
+		return getValueAt(0, iCol).getClass();
+	}
+
+	/**
+	 * Is the cell at the given row and column position editable?
+	 */
+	@Override
+	public boolean isCellEditable(int iRow, int iCol) {
+		// The table is always read-only
+		return false;
+	}
+
+	@Override
+	public void notify(Observable<KeystoreChangedEvent> sender,
+			KeystoreChangedEvent message) throws Exception {
+		// reload the table
+		if (message.keystoreType.equals(TRUSTSTORE))
+			load();
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthChecker.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthChecker.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthChecker.java
new file mode 100644
index 0000000..c30ec8f
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthChecker.java
@@ -0,0 +1,199 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.log4j.Logger;
+import org.biocatalogue.x2009.xml.rest.MonitoringStatusLabel;
+import org.biocatalogue.x2009.xml.rest.Service;
+import org.biocatalogue.x2009.xml.rest.ServiceTest;
+import org.biocatalogue.x2009.xml.rest.TestScript;
+
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+import net.sf.taverna.t2.activities.wsdl.WSDLActivity;
+import net.sf.taverna.t2.activities.wsdl.WSDLActivityConfigurationBean;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
+import net.sf.taverna.t2.visit.VisitReport;
+import net.sf.taverna.t2.visit.VisitReport.Status;
+import net.sf.taverna.t2.workflowmodel.health.HealthChecker;
+
+
+/**
+ * A {@link HealthChecker} for a {@link WSDLActivity}.
+ *
+ * @author Sergejs Aleksejevs
+ */
+public class BioCatalogueWSDLActivityHealthChecker implements HealthChecker<WSDLActivity>
+{
+  private static final int MILLIS_IN_THE_PAST_FOR_OLDEST_MONITORING_DATA = 48 * 60 * 60 * 1000;  // 48hrs
+  
+  
+  private Logger logger;
+  
+  public BioCatalogueWSDLActivityHealthChecker() {
+    logger = Logger.getLogger(BioCatalogueWSDLActivityHealthChecker.class);
+  }
+  
+  
+  public boolean canVisit(Object subject) {
+    return (subject instanceof WSDLActivity);
+  }
+  
+  
+  public VisitReport visit(WSDLActivity activity, List<Object> ancestors)
+  {
+    WSDLActivityConfigurationBean configBean = activity.getConfiguration();
+    SoapOperationIdentity soapOpIdentity = new SoapOperationIdentity(configBean.getWsdl(), configBean.getOperation(), null);
+    
+    try {
+      // make BioCatalogue API request to fetch the data
+      Service serviceWithMonitoringData = BioCatalogueClient.getInstance().lookupParentServiceMonitoringData(soapOpIdentity);
+      MonitoringStatusLabel.Enum serviceStatusLabel = null;
+      
+      
+      VisitReport.Status status = null;
+      String visitReportLabel = null;
+      String visitReportExplanation = null;
+      List<VisitReport> subReports = new ArrayList<VisitReport>();
+      
+      
+      if (serviceWithMonitoringData == null) {
+        // BioCatalogue doesn't "know" about this service - it appears not to be registered;
+        // --> nothing to report to Taverna
+        return (null);
+      }
+      else if (serviceWithMonitoringData.getLatestMonitoringStatus() == null) {
+        // BioCatalogue "knows" this service, but for some reason there was no monitoring data available;
+        // possibly an API change? either way --> nothing to report to Taverna
+        return (null);
+      }
+      else
+      {
+        Calendar lastCheckedAt = serviceWithMonitoringData.getLatestMonitoringStatus().getLastChecked();
+        String agoString = Util.getAgoString(lastCheckedAt, Calendar.getInstance(), MILLIS_IN_THE_PAST_FOR_OLDEST_MONITORING_DATA);
+        if (agoString == null) {
+          return (null);
+        }
+        
+        serviceStatusLabel = serviceWithMonitoringData.getLatestMonitoringStatus().getLabel();
+        switch (serviceStatusLabel.intValue()) {
+          case MonitoringStatusLabel.INT_PASSED:
+            visitReportLabel = "Service Catalogue: all tests passed " + agoString;
+            visitReportExplanation = "The Service Catalogue reports that all available tests for this WSDL service have " +
+            		                     "been successful. They have been last executed " + agoString;
+            status = Status.OK;
+            break;
+                  
+          case MonitoringStatusLabel.INT_WARNING:
+          case MonitoringStatusLabel.INT_FAILED:
+            visitReportLabel = "Service Catalogue: some tests failed " + agoString;
+            visitReportExplanation = "Some test scripts for this WSDL service have failed";
+            
+            // only extract data about failing test scripts
+            subReports = createTestScriptSubReportsForFailingService(activity, serviceWithMonitoringData);
+            if (subReports.size() == 0) {
+              // failing tests must have been for endpoint / WSDL location - but not for scripts;
+              // Taverna doesn't need to know about the former, as it replicates internal checks
+              return (null);
+            }
+            else {
+              // determine the worst status and report as the one of the collection of subreports
+              status = VisitReport.getWorstStatus(subReports);
+            }
+            break;
+          
+          case MonitoringStatusLabel.INT_UNCHECKED:
+            // monitoring record states that the status of this service was not (yet) checked;
+            // possibly monitoring on BioCatalogue was switched off before this service was registered;
+            // --> nothing to report to Taverna
+            return (null);
+                  
+          default:
+            visitReportLabel = "Service Catalogue: unknown monitoring status received - \"" + serviceStatusLabel.toString() + "\"";
+            visitReportExplanation = "The Service Catalogue has returned a new monitoring status for this service: \"" +
+                                     serviceStatusLabel.toString() + "\"\n\n" +
+                                     "It has never been used before and probably indicates a change in the Service Catalogue API. " +
+                                     "Please report this issue to the Service Catalogue developers.";
+            status = Status.WARNING;
+            break;
+        }
+      }
+      
+      // wrap determined values into a single VisitReport object; then attach data to identify
+      // this service in associated VisitExplainer
+      VisitReport report = new VisitReport(BioCatalogueWSDLActivityHealthCheck.getInstance(), activity, 
+                                           visitReportLabel, BioCatalogueWSDLActivityHealthCheck.MESSAGE_IN_VISIT_REPORT, status, subReports);
+      report.setProperty(BioCatalogueWSDLActivityHealthCheck.WSDL_LOCATION_PROPERTY, soapOpIdentity.getWsdlLocation());
+      report.setProperty(BioCatalogueWSDLActivityHealthCheck.OPERATION_NAME_PROPERTY, soapOpIdentity.getOperationName());
+      report.setProperty(BioCatalogueWSDLActivityHealthCheck.EXPLANATION_MSG_PROPERTY, visitReportExplanation);
+      
+      return (report);
+    }
+    catch (Exception e) {
+      // not sure what could have happened - it will be visible in the logs
+      logger.error("Unexpected error while performing health check for " + 
+                   soapOpIdentity.getWsdlLocation() + " service.", e);
+      return (null);
+    }
+  }
+  
+  
+  private List<VisitReport> createTestScriptSubReportsForFailingService(WSDLActivity activity, Service serviceWithMonitoringData)
+  {
+    List<VisitReport> subReports = new ArrayList<VisitReport>();
+    
+    try {
+      List<ServiceTest> serviceTests = serviceWithMonitoringData.getMonitoring().getTests().getServiceTestList();
+      for (ServiceTest test : serviceTests)
+      {
+        if (test.getTestType().getTestScript() != null &&
+            test.getTestType().getTestScript()instanceof TestScript)
+        {
+          TestScript testScript = test.getTestType().getTestScript();
+          
+          String agoString = Util.getAgoString(test.getLatestStatus().getLastChecked(), Calendar.getInstance(), 
+                                               MILLIS_IN_THE_PAST_FOR_OLDEST_MONITORING_DATA);
+          
+          // only proceed if this test wasn't run too long ago
+          if (agoString != null) {
+            String label = "Service Catalogue: \"" + testScript.getName() + "\" test script " + test.getLatestStatus().getLabel();
+            VisitReport report = new VisitReport(BioCatalogueWSDLActivityHealthCheck.getInstance(), activity, 
+                label, BioCatalogueWSDLActivityHealthCheck.MESSAGE_IN_VISIT_REPORT,
+                ServiceMonitoringStatusInterpreter.translateBioCatalogueStatusForTaverna(test.getLatestStatus().getLabel()));
+            report.setProperty(BioCatalogueWSDLActivityHealthCheck.WSDL_LOCATION_PROPERTY, activity.getConfiguration().getWsdl());
+            report.setProperty(BioCatalogueWSDLActivityHealthCheck.OPERATION_NAME_PROPERTY, activity.getConfiguration().getOperation());
+            report.setProperty(BioCatalogueWSDLActivityHealthCheck.EXPLANATION_MSG_PROPERTY,
+                               "This test was last executed " + agoString + "." +
+                               "\n\n" + StringEscapeUtils.escapeHtml(test.getLatestStatus().getMessage()) +
+                               "\n\n---- Test script description ----\n" + StringEscapeUtils.escapeHtml(testScript.getDescription()));
+            
+            subReports.add(report);
+          }
+        }
+      }
+    }
+    catch (Exception e) {
+      // log the error, but do not terminate the method - maybe some sub reports were successfully
+      // generated, in which case at least partial result can be returned
+      logger.error("Encountered unexpected problem while trying to generate a collection of sub-reports " +
+      		         "for a failing service: " + activity.getConfiguration().getWsdl(), e);
+    }
+    
+    return (subReports);
+  }
+  
+  
+  /**
+   * Health check for the WSDL activities involves fetching
+   * the monitoring status of each activity from BioCatalogue - 
+   * this *may* be time consuming.
+   */
+  public boolean isTimeConsuming() {
+    return true;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/ServiceHealthChecker.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/ServiceHealthChecker.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/ServiceHealthChecker.java
new file mode 100644
index 0000000..8e85e61
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/ServiceHealthChecker.java
@@ -0,0 +1,280 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.border.EmptyBorder;
+
+import org.apache.log4j.Logger;
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+import org.biocatalogue.x2009.xml.rest.RestMethod;
+import org.biocatalogue.x2009.xml.rest.Service;
+import org.biocatalogue.x2009.xml.rest.ServiceTest;
+import org.biocatalogue.x2009.xml.rest.SoapOperation;
+
+import net.sf.taverna.biocatalogue.model.BioCataloguePluginConstants;
+import net.sf.taverna.biocatalogue.model.Resource;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.biocatalogue.model.SoapProcessorIdentity;
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+import net.sf.taverna.biocatalogue.ui.JClickableLabel;
+import net.sf.taverna.biocatalogue.ui.JWaitDialog;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponent;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
+
+
+/**
+ * This class helps with "health checks" of individual Taverna processors
+ * (i.e. SOAP operations) and the workflows in general (by iterating through
+ * the processors).
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class ServiceHealthChecker
+{
+    private static Logger logger = Logger.getLogger(ServiceHealthChecker.class);
+
+    // deny creation of instances of this class
+  private ServiceHealthChecker() { };
+  
+  
+  // =====================================================================================================
+  //                      *** Health Check of Individual Service / Processor ***
+  // =====================================================================================================
+  
+  /**
+   * @param serviceURL URL of SOAP service or REST service on BioCatalogue;
+   *                   URL should be of the 
+   */
+  public static void checkServiceByURL(String serviceURL)
+  {
+    if (serviceURL != null) {
+      checkMonitoringStatusRoutine(serviceURL);
+    }
+    else {
+      // for some reason the URL of the service wasn't provided...
+      JOptionPane.showMessageDialog(null, "Cannot provide monitoring status for this service - " +
+      		                          "unknown service URL", "Service Catalogue Error", JOptionPane.ERROR_MESSAGE);
+    }
+  }
+  
+  
+  /**
+   * @param  
+   */
+  public static void checkResource(ResourceLink serviceOrOperationOrMethod)
+  {
+    if (serviceOrOperationOrMethod != null) {
+      checkMonitoringStatusRoutine(serviceOrOperationOrMethod);
+    }
+    else {
+      // for some reason resource object wasn't provided...
+      JOptionPane.showMessageDialog(null, "Cannot provide monitoring status - " +
+                                    "null reference received", "Service Catalogue Error", JOptionPane.ERROR_MESSAGE);
+    }
+  }
+  
+  
+  /**
+   * Used when invoked from the workflow diagram - e.g. when a URL of the specific
+   * resource on BioCatalogue is not known, but have enough of identifying data
+   * to proceed with health check.
+   * 
+   * @param soapOperationDetails
+   */
+  public static void checkWSDLProcessor(SoapOperationIdentity soapOperationDetails)
+  {
+    if (!soapOperationDetails.hasError()) {
+      checkMonitoringStatusRoutine(soapOperationDetails);
+    }
+    else {
+      // this error message comes from Integration class extracting SOAP operation details from the contextual selection
+      JOptionPane.showMessageDialog(null, soapOperationDetails.getErrorDetails(), "Service Catalogue Error", JOptionPane.WARNING_MESSAGE);
+    }
+  }
+  
+  
+  /**
+   * @param serviceOrSoapOperationToCheck Instance of SoapOperationIdentity representing Taverna processor
+   *                                      or String representing a URL of the service to check health for.
+   */
+  private static void checkMonitoringStatusRoutine(final Object serviceOrSoapOperationToCheck)
+  {
+    // helper variable to determine the kind of check to perform - the difference is minimal:
+    // wording in the status messages ("Web Service" | "processor" | "REST Service") and which method to call on
+    // the BioCatalogue client to fetch monitoring data
+    final boolean bCheckingService = (serviceOrSoapOperationToCheck instanceof String);
+    final boolean bCheckingWSDLProcessor = (serviceOrSoapOperationToCheck instanceof SoapOperationIdentity);
+    final boolean bCheckingResource = (serviceOrSoapOperationToCheck instanceof ResourceLink);
+    
+    final StringBuilder itemToCheck = new StringBuilder();
+    if (bCheckingService) {
+      itemToCheck.append("service");
+    }
+    else if (bCheckingWSDLProcessor) {
+      itemToCheck.append("WSDL service"); 
+    }
+    else if (bCheckingResource) {
+      TYPE resourceType = Resource.getResourceTypeFromResourceURL(((ResourceLink)serviceOrSoapOperationToCheck).getHref());
+      itemToCheck.append(resourceType.getTypeName());
+    }
+    
+    // create the wait dialog, but don't make it visible - first need to start the background processing thread
+    final JWaitDialog jwd = new JWaitDialog(MainComponent.dummyOwnerJFrame, "Checking "+itemToCheck+" status",
+    "Please wait while status of selected "+itemToCheck+" is being checked...");
+
+    new Thread(itemToCheck + " lookup and health check operation") {
+      public void run() {
+        try
+        {
+          BioCatalogueClient client = BioCatalogueClient.getInstance();
+          Service serviceMonitoringData = null;
+          
+          // attempt to get monitoring data from BioCatalogue - for this need to identify what type of
+          // item was provided as a parameter
+          if (bCheckingService) {
+            serviceMonitoringData = client.getBioCatalogueServiceMonitoringData((String)serviceOrSoapOperationToCheck);
+          }
+          else if (bCheckingWSDLProcessor) {
+            serviceMonitoringData = client.lookupParentServiceMonitoringData((SoapOperationIdentity)serviceOrSoapOperationToCheck); 
+          }
+          else if (bCheckingResource) {
+            String resourceURL = ((ResourceLink)serviceOrSoapOperationToCheck).getHref();
+            TYPE resourceType = Resource.getResourceTypeFromResourceURL(resourceURL);
+            
+//            if (resourceType == TYPE.Service) {
+//              serviceMonitoringData = client.getBioCatalogueServiceMonitoringData(resourceURL);
+//            }
+//            else
+            	if (resourceType == TYPE.SOAPOperation) {
+              String parentServiceURL = ((SoapOperation)serviceOrSoapOperationToCheck).getAncestors().getService().getHref();
+              serviceMonitoringData = client.getBioCatalogueServiceMonitoringData(parentServiceURL);
+            }
+            else if (resourceType == TYPE.RESTMethod) {
+              String parentServiceURL = ((RestMethod)serviceOrSoapOperationToCheck).getAncestors().getService().getHref();
+              serviceMonitoringData = client.getBioCatalogueServiceMonitoringData(parentServiceURL);
+            }
+            else {
+              JOptionPane.showMessageDialog(jwd, "Unexpected resource type - can't execute health check for this",
+                  "Service Catalogue Error", JOptionPane.ERROR_MESSAGE);
+              	logger.error("Service Catalogue: Could not perform health check for" + resourceType);
+            }
+          }
+          
+          
+          // need to make this assignment to make the variable final - otherwise unavailable inside the new thread...
+          final Service serviceWithMonitoringData = serviceMonitoringData;
+          SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+              if (serviceWithMonitoringData == null) {
+                jwd.setTitle("Service Catalogue - Information");
+                jwd.waitFinished(new JLabel("There is no information about this "+itemToCheck+" in the Service Catalogue",
+                    UIManager.getIcon("OptionPane.informationIcon"), JLabel.CENTER));
+              }
+              else if (serviceWithMonitoringData.getLatestMonitoringStatus() == null) {
+                jwd.setTitle("Service Catalogue Warning");
+                jwd.waitFinished(new JLabel("This "+itemToCheck+" is known to the Service Catalogue, but no monitoring data was available.",
+                    UIManager.getIcon("OptionPane.warningIcon"), JLabel.CENTER));
+              }
+              else
+              {
+                // set the overall status message
+                String overallStatusLabel = "<html><b>Overall status:</b><br>" +
+                         serviceWithMonitoringData.getLatestMonitoringStatus().getMessage() + "<br>" +
+                         "(last checked";
+                if (serviceWithMonitoringData.getLatestMonitoringStatus().getLastChecked() == null) {
+                  overallStatusLabel += ": never";
+                }
+                else {
+                  overallStatusLabel += " at " + BioCatalogueClient.getShortDateFormatter().format(
+                      serviceWithMonitoringData.getLatestMonitoringStatus().getLastChecked().getTime());
+                }
+                overallStatusLabel += ")</html>";
+                JLabel jlOverallStatus = new JLabel(overallStatusLabel);
+                
+                // create panel for additional status messages (e.g. endpoint, wsdl location, etc)
+                JPanel jpStatusMessages = new JPanel();
+                jpStatusMessages.setLayout(new BoxLayout(jpStatusMessages, BoxLayout.Y_AXIS));
+                
+                for (ServiceTest test : serviceWithMonitoringData.getMonitoring().getTests().getServiceTestList())
+                {
+                  // First get the service type
+                  String testLabel = "<html><br><b>";
+                  if (test.getTestType().getUrlMonitor() != null)
+                  {
+                    if (test.getTestType().getUrlMonitor().getUrl().endsWith("wsdl")) {
+                      // WSDL location test
+                      testLabel += "WSDL Location Availability:</b><br>" +
+                                   "URL: " + test.getTestType().getUrlMonitor().getUrl();
+                    }
+                    else {
+                      // Endpoint availability test
+                      testLabel += "Endpoint Availability:</b><br>" +
+                                   "URL: " + test.getTestType().getUrlMonitor().getUrl();
+                    }
+                  }
+                  else if (test.getTestType().getTestScript() != null) {
+                    // test script
+                    testLabel += "Test Script: " + test.getTestType().getTestScript().getName() + "</b>";
+                  }
+                  else {
+                    testLabel += "Unknown test type</b>";
+                  }
+                  testLabel += "<br>";
+                  
+                  // Add service results
+                  testLabel += test.getLatestStatus().getMessage() + "</html>";
+                  
+                  // Add the current test into the test messages panel
+                  jpStatusMessages.add(new JLabel(testLabel));
+                }
+                
+                // either way add the overall status on top of everything
+                jpStatusMessages.add(jlOverallStatus, 0);
+                jpStatusMessages.setBorder(new EmptyBorder(10,10,10,10));
+                JScrollPane jspStatusMessages = new JScrollPane(jpStatusMessages);
+                jspStatusMessages.setBorder(BorderFactory.createEmptyBorder());
+                
+                // *** Put everything together ***
+                JPanel jpHealthCheckStatus = new JPanel(new BorderLayout(15, 10));
+                jpHealthCheckStatus.add(new JLabel(ServiceMonitoringStatusInterpreter.getStatusIcon(serviceWithMonitoringData, false)),
+                             BorderLayout.WEST);
+                jpHealthCheckStatus.add(jspStatusMessages, BorderLayout.CENTER);
+                
+                jwd.setTitle("Service Catalogue - Monitoring Status");
+                jwd.waitFinished(jpHealthCheckStatus);
+              }
+            }
+          });
+        }
+        catch (Exception e) {
+          logger.error("Service Catalogue: Error occurred while checking status of selected", e);
+          jwd.setTitle("Service Catalogue - Error");
+          jwd.waitFinished(new JLabel("<html>An unexpected error occurred while checking status of selected " +
+                                      itemToCheck + "<br>Please see error log for details...",
+                                      UIManager.getIcon("OptionPane.errorIcon"), JLabel.CENTER));
+        }
+      }
+    }.start();
+    
+    jwd.setVisible(true);
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/ServiceMonitoringStatusInterpreter.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/ServiceMonitoringStatusInterpreter.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/ServiceMonitoringStatusInterpreter.java
new file mode 100644
index 0000000..0742cef
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/ServiceMonitoringStatusInterpreter.java
@@ -0,0 +1,77 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check;
+
+import java.net.URL;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.t2.visit.VisitReport;
+import net.sf.taverna.t2.visit.VisitReport.Status;
+
+import org.biocatalogue.x2009.xml.rest.MonitoringStatus;
+import org.biocatalogue.x2009.xml.rest.MonitoringStatusLabel;
+import org.biocatalogue.x2009.xml.rest.Service;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class ServiceMonitoringStatusInterpreter
+{
+  // prevent instantiation of this class
+  private ServiceMonitoringStatusInterpreter() { /* do nothing */ }
+  
+  
+  /**
+   * @param serviceWithMonitoringData
+   * @param listingIconRequired True to get a small icon suitable for a JList entry;
+   *                            false to get a larger icon.
+   * @return
+   */
+  public static ImageIcon getStatusIcon(Service serviceWithMonitoringData, boolean listingIconRequired)
+  {
+    MonitoringStatus latestMonitoringStatus = serviceWithMonitoringData.getLatestMonitoringStatus();
+    if (latestMonitoringStatus == null) {
+    	return ResourceManager.getImageIcon((listingIconRequired ?
+                ResourceManager.SERVICE_STATUS_UNCHECKED_ICON :
+                    ResourceManager.SERVICE_STATUS_UNCHECKED_ICON_LARGE));
+    }
+	MonitoringStatusLabel.Enum serviceStatusLabel = latestMonitoringStatus.getLabel();
+    
+    switch (serviceStatusLabel.intValue()) {
+      case MonitoringStatusLabel.INT_PASSED:
+              return ResourceManager.getImageIcon((listingIconRequired ?
+                                                          ResourceManager.SERVICE_STATUS_PASSED_ICON :
+                                                          ResourceManager.SERVICE_STATUS_PASSED_ICON_LARGE));
+      case MonitoringStatusLabel.INT_WARNING:
+              return ResourceManager.getImageIcon((listingIconRequired ?
+                                                          ResourceManager.SERVICE_STATUS_WARNING_ICON :
+                                                          ResourceManager.SERVICE_STATUS_WARNING_ICON_LARGE));
+      case MonitoringStatusLabel.INT_FAILED:
+              return ResourceManager.getImageIcon((listingIconRequired ?
+                                                          ResourceManager.SERVICE_STATUS_FAILED_ICON :
+                                                          ResourceManager.SERVICE_STATUS_FAILED_ICON_LARGE));
+      case MonitoringStatusLabel.INT_UNCHECKED:
+              return ResourceManager.getImageIcon((listingIconRequired ?
+                                                          ResourceManager.SERVICE_STATUS_UNCHECKED_ICON :
+                                                          ResourceManager.SERVICE_STATUS_UNCHECKED_ICON_LARGE));
+      default:
+              return (ResourceManager.getImageIcon(ResourceManager.SERVICE_STATUS_UNKNOWN_ICON));
+    }
+    
+  }
+  
+  
+  public static VisitReport.Status translateBioCatalogueStatusForTaverna(MonitoringStatusLabel.Enum monitoringStatusLabelEnum)
+  {
+    switch (monitoringStatusLabelEnum.intValue()) {
+      case MonitoringStatusLabel.INT_PASSED:    return Status.OK;
+      case MonitoringStatusLabel.INT_WARNING:   return Status.WARNING;
+      case MonitoringStatusLabel.INT_FAILED:    return Status.SEVERE;
+      case MonitoringStatusLabel.INT_UNCHECKED: return Status.OK;      // not really OK, but Taverna isn't interested in missing data anyway 
+      default:                                  return Status.WARNING; // could be worth to pop up a warning in this case, as it may mean something has changed
+    }
+  }
+  
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/BioCatalogueContextualMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/BioCatalogueContextualMenuSection.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/BioCatalogueContextualMenuSection.java
new file mode 100644
index 0000000..a5fb7a8
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/BioCatalogueContextualMenuSection.java
@@ -0,0 +1,62 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.menus;
+
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DefaultContextualMenu;
+import net.sf.taverna.t2.workflowmodel.Dataflow;
+import net.sf.taverna.t2.workflowmodel.InputPort;
+import net.sf.taverna.t2.workflowmodel.Processor;
+
+
+public class BioCatalogueContextualMenuSection extends AbstractMenuSection implements ContextualMenuComponent
+{
+  // TODO - this shouldn't be here, must reference this field in AbstractMenuSection!!
+  public static final String SECTION_COLOR = "sectionColor";
+
+  
+  public static final URI BIOCATALOGUE_MENU_SECTION_ID = URI.create("http://biocatalogue.org/2010/contextMenu/biocatalogue_section");
+  private static final String SECTION_TITLE = "Service Catalogue";
+  
+  private ContextualSelection contextualSelection;
+  
+  
+  public BioCatalogueContextualMenuSection() {
+          super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 100000, BIOCATALOGUE_MENU_SECTION_ID);
+  }
+
+  public ContextualSelection getContextualSelection() {
+          return contextualSelection;
+  }
+  
+  public void setContextualSelection(ContextualSelection contextualSelection) {
+    this.contextualSelection = contextualSelection;
+  }
+
+  @Override
+  public boolean isEnabled() {
+    return super.isEnabled()
+                    && (getContextualSelection().getSelection() instanceof Dataflow ||
+                        getContextualSelection().getSelection() instanceof Processor ||
+                        getContextualSelection().getSelection() instanceof InputPort);
+  }
+  
+  @SuppressWarnings("serial")
+  protected Action createAction()
+  {
+    Action action = new AbstractAction(SECTION_TITLE, ResourceManager.getImageIcon(ResourceManager.FAVICON)) {
+      public void actionPerformed(ActionEvent e) {
+      }
+    };
+    action.putValue(SECTION_COLOR, ShadedLabel.GREEN);
+    return (action);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/MenuActionInputPort.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/MenuActionInputPort.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/MenuActionInputPort.java
new file mode 100644
index 0000000..f94b0e6
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/MenuActionInputPort.java
@@ -0,0 +1,43 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.menus;
+
+import java.awt.event.ActionEvent;
+import java.net.URISyntaxException;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JOptionPane;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+
+import net.sf.taverna.t2.workflowmodel.InputPort;
+
+
+/**
+ * This class currently won't be used, as an entry for it was removed from
+ * META-INF/services/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
+ * 
+ * This is because no useful action is yet available for input/output ports.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class MenuActionInputPort extends AbstractContextualMenuAction {
+
+	public MenuActionInputPort() throws URISyntaxException {
+		super(BioCatalogueContextualMenuSection.BIOCATALOGUE_MENU_SECTION_ID, 15);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new AbstractAction("InputPort") {
+			public void actionPerformed(ActionEvent e) {
+				JOptionPane.showMessageDialog(getContextualSelection().getRelativeToComponent(), "Hoho!");
+			}
+		};
+	}
+
+	@Override
+	public boolean isEnabled() {
+	  return (super.isEnabled() && getContextualSelection().getSelection() instanceof InputPort);
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/MenuActionProcessorHealthCheck.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/MenuActionProcessorHealthCheck.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/MenuActionProcessorHealthCheck.java
new file mode 100644
index 0000000..c66d72b
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/menus/MenuActionProcessorHealthCheck.java
@@ -0,0 +1,51 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.menus;
+
+import java.awt.event.ActionEvent;
+import java.net.URISyntaxException;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.Integration;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check.ServiceHealthChecker;
+import net.sf.taverna.t2.workflowmodel.Processor;
+
+
+public class MenuActionProcessorHealthCheck extends AbstractContextualMenuAction {
+
+  public MenuActionProcessorHealthCheck() throws URISyntaxException {
+    super(BioCatalogueContextualMenuSection.BIOCATALOGUE_MENU_SECTION_ID, 20);
+  }
+
+  @SuppressWarnings("serial")
+@Override
+  protected Action createAction()
+  {
+    Action action = new AbstractAction("Service Health Check") {
+      public void actionPerformed(ActionEvent e) {
+        SoapOperationIdentity soapOperationDetails = Integration.extractSoapOperationDetailsFromProcessorContextualSelection(getContextualSelection());
+        ServiceHealthChecker.checkWSDLProcessor(soapOperationDetails);
+      }
+    };
+    action.putValue(Action.SHORT_DESCRIPTION, "Check monitoring status of this service");
+    return (action);
+  }
+
+  @Override
+  public boolean isEnabled()
+  {
+    // FIXME - this will only work for SOAP processors for now..
+    boolean isEnabled = super.isEnabled() && getContextualSelection().getSelection() instanceof Processor;
+    
+    if (isEnabled) {
+      SoapOperationIdentity soapOperationDetails = Integration.extractSoapOperationDetailsFromProcessorContextualSelection(getContextualSelection());
+      isEnabled = !soapOperationDetails.hasError();
+    }
+    
+    return isEnabled;
+  }
+	
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueRESTServiceProvider.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueRESTServiceProvider.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueRESTServiceProvider.java
new file mode 100644
index 0000000..3c39b1d
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueRESTServiceProvider.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (C) 2008-2010 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.ui.perspectives.biocatalogue.integration.service_panel;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.Icon;
+
+import org.apache.log4j.Logger;
+
+//import net.sf.taverna.t2.activities.rest.ui.servicedescription.RESTActivityIcon;
+import net.sf.taverna.t2.servicedescriptions.AbstractConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionRegistryImpl;
+
+/**
+ * Service provider for REST service added to the Service Panel through the
+ * BioCatalogue perspective.
+ * 
+ * @author Alex Nenadic
+ */
+public class BioCatalogueRESTServiceProvider extends
+	AbstractConfigurableServiceProvider<RESTFromBioCatalogueServiceDescription> {
+
+	public static final String PROVIDER_NAME = "Service Catalogue - selected services";
+	  
+	private static final URI providerId = URI
+	.create("http://taverna.sf.net/2010/service-provider/servicecatalogue/rest");
+	
+	private static Logger logger = Logger.getLogger(BioCatalogueRESTServiceProvider.class);
+
+	public BioCatalogueRESTServiceProvider(
+			RESTFromBioCatalogueServiceDescription restServiceDescription) {
+		super(restServiceDescription);
+	}
+	
+	public BioCatalogueRESTServiceProvider() {
+		super(new RESTFromBioCatalogueServiceDescription());
+	}
+	
+	@Override
+	protected List<? extends Object> getIdentifyingData() {
+		return getConfiguration().getIdentifyingData();
+	}
+
+	@Override
+	public void findServiceDescriptionsAsync(
+			FindServiceDescriptionsCallBack callBack) {
+	    callBack.status("Starting Service Catalogue REST Service Provider");
+		registerNewRESTMethod(getConfiguration(), callBack);
+	}
+
+	@Override
+	public Icon getIcon() {
+//		return RESTActivityIcon.getRESTActivityIcon();
+		return getConfiguration().getIcon();
+	}
+
+	@Override
+	public String getId() {
+		return providerId.toString();
+	}
+
+	@Override
+	public String getName() {
+		return "Service Catalogue REST";
+	}
+	
+	@Override
+	public String toString() {
+		return "Service Catalogue REST service " + getConfiguration().getName();
+	}
+	
+	public static boolean registerNewRESTMethod(
+			RESTFromBioCatalogueServiceDescription restServiceDescription,
+			FindServiceDescriptionsCallBack callBack)	{
+		if (callBack == null) {
+			// We are not adding service through a callback and
+			// findServiceDescriptionsAsync() -
+			// we are adding directly from the BioCatalogue perspective.
+			ServiceDescriptionRegistryImpl serviceDescriptionRegistry = ServiceDescriptionRegistryImpl
+					.getInstance();
+			serviceDescriptionRegistry
+					.addServiceDescriptionProvider(new BioCatalogueRESTServiceProvider(
+							restServiceDescription));
+			return true;
+		} else {
+			{
+				// Add the REST method to the Service Panel through the callback
+				callBack.partialResults(Collections
+						.singletonList(restServiceDescription));
+				callBack.finished();
+				return (true);
+			}
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueServiceProvider.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueServiceProvider.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueServiceProvider.java
new file mode 100644
index 0000000..87518c3
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueServiceProvider.java
@@ -0,0 +1,274 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.service_panel;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.Icon;
+import javax.wsdl.Operation;
+import javax.wsdl.WSDLException;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.log4j.Logger;
+import org.xml.sax.SAXException;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.io.xml.DomDriver;
+
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.t2.activities.wsdl.WSDLActivityHealthChecker;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.BioCataloguePerspective;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.config.BioCataloguePluginConfiguration;
+import net.sf.taverna.wsdl.parser.UnknownOperationException;
+import net.sf.taverna.wsdl.parser.WSDLParser;
+
+public class BioCatalogueServiceProvider implements ServiceDescriptionProvider
+{
+  public static final String PROVIDER_NAME = "Service Catalogue - selected services";
+  
+  private static BioCatalogueServiceProvider instanceOfSelf = null;
+  private static FindServiceDescriptionsCallBack callBack;
+  
+  private static List<SoapOperationIdentity> registeredSOAPOperations;
+  private static List<RESTFromBioCatalogueServiceDescription> registeredRESTMethods;
+  
+  private static Logger logger = Logger.getLogger(BioCatalogueServiceProvider.class);
+  
+  
+	public BioCatalogueServiceProvider() {
+	  BioCatalogueServiceProvider.instanceOfSelf = this;
+	}
+	
+	@SuppressWarnings("unchecked")
+  public void findServiceDescriptionsAsync(FindServiceDescriptionsCallBack callBack)
+	{
+		BioCatalogueServiceProvider.callBack = callBack;
+    callBack.status("Starting Service Catalogue Service Provider");
+		
+    // --- Initilise the service provider with stored services ---
+    
+    // read stored settings
+    // NB! it's crucial to set the custom classloader, otherwise XStream would fail,
+    //     as it would attempt to use the default one, which wouldn't know about the
+    //     plugin's classes
+    logger.info("Starting to deserialise the list of services stored in the configuration file");
+    XStream xstream = new XStream(new DomDriver());
+    xstream.setClassLoader(BioCataloguePerspective.class.getClassLoader());
+    
+    BioCataloguePluginConfiguration configuration = BioCataloguePluginConfiguration.getInstance();
+    
+    // *** load stored SOAP operations ***
+    String loadedSOAPServicesXMLString = configuration.getProperty(BioCataloguePluginConfiguration.SOAP_OPERATIONS_IN_SERVICE_PANEL);
+    
+    Object loadedSOAPServices = (loadedSOAPServicesXMLString == null ?
+                                 null :
+                                 xstream.fromXML(loadedSOAPServicesXMLString));
+    
+    registeredSOAPOperations = (loadedSOAPServices == null || !(loadedSOAPServices instanceof List<?>) ?
+                                new ArrayList<SoapOperationIdentity>() :
+                                (List<SoapOperationIdentity>)loadedSOAPServices
+                               );
+    logger.info("Deserialised " + registeredSOAPOperations.size() + Util.pluraliseNoun("SOAP operation", registeredSOAPOperations.size()));
+    
+    // prepare the correct format of data for initialisation
+    List<ServiceDescription> results = new ArrayList<ServiceDescription>();
+    for (SoapOperationIdentity opId : registeredSOAPOperations) {
+      results.add(new WSDLOperationFromBioCatalogueServiceDescription(opId));
+    }
+    
+    
+    // *** load stored REST methods ***
+    String loadedRESTMethodsXMLString = configuration.getProperty(BioCataloguePluginConfiguration.REST_METHODS_IN_SERVICE_PANEL);
+    
+    Object loadedRESTMethods = (loadedRESTMethodsXMLString == null ?
+                                null :
+                                xstream.fromXML(loadedRESTMethodsXMLString));
+    
+    registeredRESTMethods = (loadedRESTMethods == null || !(loadedRESTMethods instanceof List<?>) ?
+                             new ArrayList<RESTFromBioCatalogueServiceDescription>() :
+                             (List<RESTFromBioCatalogueServiceDescription>)loadedRESTMethods);
+    logger.info("Deserialised " + registeredRESTMethods.size() + Util.pluraliseNoun("REST method", registeredRESTMethods.size()));
+    
+    results.addAll(registeredRESTMethods);
+		
+    
+		// *** send the services to the Service Panel ***
+		callBack.partialResults(results);
+		
+		
+		// NB! This is to be called when it is known that no more items will be added - 
+		// it's never true for this provider, as items may be added on user request
+		// at any time!
+		//
+		// callBack.finished();
+	}
+	
+	public Icon getIcon() {
+		return null;
+	}
+	
+	public String getName(){
+	  // TODO - not sure where this is used
+		return "My dummy service";
+	}
+	
+	public String getId() {
+    return "http://www.taverna.org.uk/2010/services/servicecatalogue";
+  }
+	
+	
+	/**
+	 * Adds a new "processor" - i.e. a WSDL operation into the main Service Panel.
+	 * 
+	 * @param wsdlLocation URL of the WSDL location of the operation to add.
+	 * @param operationName Name of the operation within specified WSDL document.
+	 * @return True if the operation was added;
+	 *         false if the service provided was not yet initiliased (unlikely) or
+	 *         when supplied strings were empty/null.
+	 */
+	public static boolean registerNewWSDLOperation(SoapOperationIdentity soapOperationDetails)
+	{
+	  if (BioCatalogueServiceProvider.instanceOfSelf == null || soapOperationDetails == null ||
+	      soapOperationDetails.getWsdlLocation() == null || soapOperationDetails.getWsdlLocation().length() == 0 ||
+	      soapOperationDetails.getOperationName() == null || soapOperationDetails.getOperationName().length() == 0)
+	  {
+	    // the service provider hasn't been initialised yet
+	    // OR not all details available
+	    return (false);
+	  }
+	  else
+	  {
+	    // record the newly added operation in the internal list
+	    registeredSOAPOperations.add(soapOperationDetails);
+	    
+	    // add the provided operation to the Service Panel
+	    ServiceDescription service = new WSDLOperationFromBioCatalogueServiceDescription(soapOperationDetails);
+	    BioCatalogueServiceProvider.callBack.partialResults(Collections.singletonList(service));
+	    return (true);
+	  }
+	    
+	}
+	
+	/**
+	 * Adds a SOAP/WSDL service and all of its operations into the Taverna's Service Panel.
+	 */
+	public static boolean registerNewWSDLService(String wsdlURL)
+	{
+	  if (BioCatalogueServiceProvider.instanceOfSelf == null || wsdlURL == null)
+	  {
+	    // the service provider hasn't been initialised yet
+	    // OR not all details available
+	    return (false);
+	  }
+	  else
+	  {
+		  // Do the same thing as in the WSDL service provider
+			callBack.status("Service Catalogue service provider: Parsing wsdl: " + wsdlURL);
+			WSDLParser parser = null;
+			try {
+				parser = new WSDLParser(wsdlURL);
+				List<Operation> operations = parser.getOperations();
+				callBack.status("Found " + operations.size() + " WSDL operations of service "
+						+ wsdlURL);
+				List<WSDLOperationFromBioCatalogueServiceDescription> items = new ArrayList<WSDLOperationFromBioCatalogueServiceDescription>();
+				for (Operation operation : operations) {
+					WSDLOperationFromBioCatalogueServiceDescription item;
+					try {
+						String operationName = operation.getName();
+						String operationDesc = parser.getOperationDocumentation(operationName);
+						String use = parser.getUse(operationName);
+						String style = parser.getStyle();
+						if (!WSDLActivityHealthChecker.checkStyleAndUse(style, use)) {
+							logger.warn("Unsupported style and use combination " + style + "/" + use + " for operation " + operationName + " from " + wsdlURL);
+							continue;
+						}
+						item = new WSDLOperationFromBioCatalogueServiceDescription(wsdlURL, operationName, operationDesc);
+						items.add(item);
+						
+					    // Record the newly added operation in the internal list
+						SoapOperationIdentity soapOperationDetails = new SoapOperationIdentity(wsdlURL, operationName, operationDesc);
+					    registeredSOAPOperations.add(soapOperationDetails);
+					} catch (UnknownOperationException e) {
+						String message = "Encountered an unexpected operation name:"
+								+ operation.getName();
+						callBack.fail(message, e);
+					    return false;
+					}
+				}
+				callBack.partialResults(items);
+				callBack.finished();
+				return true;
+			} catch (ParserConfigurationException e) {
+				String message = "Error configuring the WSDL parser";
+				callBack.fail(message, e);
+			    return false;
+			} catch (WSDLException e) {
+				String message = "There was an error with the wsdl: " + wsdlURL;
+				callBack.fail(message, e);
+			    return false;
+			} catch (IOException e) {
+				String message = "There was an IO error parsing the wsdl: " + wsdlURL
+						+ " Possible reason: the wsdl location was incorrect.";
+				callBack.fail(message, e);
+			    return false;
+			} catch (SAXException e) {
+				String message = "There was an error with the XML in the wsdl: "
+						+ wsdlURL;
+				callBack.fail(message, e);
+			    return false;
+			} catch (IllegalArgumentException e) { // a problem with the wsdl url
+				String message = "There was an error with the wsdl: " + wsdlURL + " "
+						+ "Possible reason: the wsdl location was incorrect.";
+				callBack.fail(message, e);
+			    return false;
+			} catch (Exception e) { // anything else we did not expect
+				String message = "There was an error with the wsdl: " + wsdlURL;
+				callBack.fail(message, e);
+			    return false;
+			}
+		}
+	    
+	}
+	
+	
+	public static boolean registerNewRESTMethod(RESTFromBioCatalogueServiceDescription restServiceDescription)
+	{
+	  if (restServiceDescription == null) {
+	    return (false);
+	  }
+	  else
+	  {
+	    // record the newly added method in the internal list
+	    registeredRESTMethods.add(restServiceDescription);
+	    
+	    // add the provided method to the Service Panel
+	    BioCatalogueServiceProvider.callBack.partialResults(Collections.singletonList(restServiceDescription));
+	    return (true);
+	  }
+	}
+	
+	
+	public static List<SoapOperationIdentity> getRegisteredSOAPOperations() {
+	  return (registeredSOAPOperations);
+	}
+	
+	public static List<RESTFromBioCatalogueServiceDescription> getRegisteredRESTMethods() {
+    return (registeredRESTMethods);
+  }
+  
+	
+	/**
+	 * Clears internal lists of stored SOAP operations / REST methods.
+	 * Therefore, once Taverna is restarted, the stored services will
+	 * be effectively "forgotten".
+	 */
+	public static void clearRegisteredServices() {
+	  registeredRESTMethods.clear();
+	  registeredSOAPOperations.clear();
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueWSDLOperationServiceProvider.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueWSDLOperationServiceProvider.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueWSDLOperationServiceProvider.java
new file mode 100644
index 0000000..62a39d7
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/BioCatalogueWSDLOperationServiceProvider.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (C) 2008-2010 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.ui.perspectives.biocatalogue.integration.service_panel;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.Icon;
+import javax.wsdl.Operation;
+import javax.wsdl.WSDLException;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.log4j.Logger;
+import org.xml.sax.SAXException;
+
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.t2.activities.wsdl.WSDLActivityHealthChecker;
+import net.sf.taverna.t2.servicedescriptions.AbstractConfigurableServiceProvider;
+import net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionRegistryImpl;
+import net.sf.taverna.wsdl.parser.UnknownOperationException;
+import net.sf.taverna.wsdl.parser.WSDLParser;
+
+/**
+ * Service provider for WSDL operations added to the Service Panel through the
+ * BioCatalogue perspective.
+ * 
+ * @author Alex Nenadic
+ */
+public class BioCatalogueWSDLOperationServiceProvider extends
+	AbstractConfigurableServiceProvider<WSDLOperationFromBioCatalogueServiceDescription> {
+
+	public BioCatalogueWSDLOperationServiceProvider(
+			WSDLOperationFromBioCatalogueServiceDescription wsdlOperationDescription) {
+		super(wsdlOperationDescription);
+	}
+
+	public BioCatalogueWSDLOperationServiceProvider() {
+		super(new WSDLOperationFromBioCatalogueServiceDescription(new SoapOperationIdentity("", "", "")));
+	}
+	
+	public static final String PROVIDER_NAME = "Service Catalogue - selected services";
+	  
+	private static final URI providerId = URI
+	.create("http://taverna.sf.net/2010/service-provider/servicecatalogue/wsdl");
+	
+	private static Logger logger = Logger.getLogger(BioCatalogueWSDLOperationServiceProvider.class);
+
+	@Override
+	protected List<? extends Object> getIdentifyingData() {
+		return getConfiguration().getIdentifyingData();
+	}
+
+	@Override
+	public void findServiceDescriptionsAsync(
+			FindServiceDescriptionsCallBack callBack) {
+	    callBack.status("Starting Service Catalogue WSDL Service Provider");
+		registerWSDLOperation(getConfiguration(), callBack);
+	}
+
+	@Override
+	public Icon getIcon() {
+		return getConfiguration().getIcon();
+	}
+
+	@Override
+	public String getId() {
+		return providerId.toString();
+	}
+
+	@Override
+	public String getName() {
+		return "Service Catalogue WSDL";
+	}
+	
+	@Override
+	public String toString() {
+		return "Service Catalogue WSDL service " + getConfiguration().getName();
+	}
+	
+	public static boolean registerWSDLOperation(
+			WSDLOperationFromBioCatalogueServiceDescription wsdlOperationDescription,
+			FindServiceDescriptionsCallBack callBack)	{
+		
+		if (callBack == null) {
+			// We are not adding service through Taverna service registry's callback and
+			// findServiceDescriptionsAsync() -
+			// we are adding directly from the BioCatalogue perspective.
+			ServiceDescriptionRegistryImpl serviceDescriptionRegistry = ServiceDescriptionRegistryImpl
+					.getInstance();
+			serviceDescriptionRegistry
+					.addServiceDescriptionProvider(new BioCatalogueWSDLOperationServiceProvider(
+							wsdlOperationDescription));
+			return true;
+		} else {
+			// Add the WSDL operation to the Service Panel through the callback
+			callBack.partialResults(Collections
+					.singletonList(wsdlOperationDescription));
+			callBack.finished();
+			return (true);
+		}
+	}
+
+	/**
+	 * Adds a SOAP/WSDL service and all of its operations into the Taverna's Service Panel.
+	 */
+	public static boolean registerWSDLService(String wsdlURL, FindServiceDescriptionsCallBack callBack)
+	{
+		String errorMessage = null;
+		Exception ex = null;
+		
+		List<Operation> operations = null;
+		List<WSDLOperationFromBioCatalogueServiceDescription> items = null;
+		
+		// Do the same thing as in the WSDL service provider
+		WSDLParser parser = null;
+		try {
+			parser = new WSDLParser(wsdlURL);
+			operations = parser.getOperations();
+			items = new ArrayList<WSDLOperationFromBioCatalogueServiceDescription>();
+			for (Operation operation : operations) {
+				WSDLOperationFromBioCatalogueServiceDescription item;
+				try {
+					String operationName = operation.getName();
+					String operationDesc = parser.getOperationDocumentation(operationName);
+					String use = parser.getUse(operationName);
+					String style = parser.getStyle();
+					if (!WSDLActivityHealthChecker.checkStyleAndUse(style, use)) {
+						logger.warn("Unsupported style and use combination " + style + "/" + use + " for operation " + operationName + " from " + wsdlURL);
+						continue;
+					}
+					item = new WSDLOperationFromBioCatalogueServiceDescription(wsdlURL, operationName, operationDesc);
+					items.add(item);					
+				} catch (UnknownOperationException e) {
+					errorMessage = "Encountered an unexpected operation name:"
+							+ operation.getName();
+					ex = e;
+				}
+			}
+		} catch (ParserConfigurationException e) {
+			errorMessage = "Error configuring the WSDL parser";
+			ex = e;
+		} catch (WSDLException e) {
+			errorMessage = "There was an error with the wsdl: " + wsdlURL;
+			ex = e;
+		} catch (IOException e) {
+			errorMessage = "There was an IO error parsing the wsdl: " + wsdlURL
+					+ " Possible reason: the wsdl location was incorrect.";
+			ex = e;
+		} catch (SAXException e) {
+			errorMessage = "There was an error with the XML in the wsdl: "
+					+ wsdlURL;
+			ex = e;
+		} catch (IllegalArgumentException e) { // a problem with the wsdl url
+			errorMessage = "There was an error with the wsdl: " + wsdlURL + " "
+					+ "Possible reason: the wsdl location was incorrect.";
+			ex = e;
+		} catch (Exception e) { // anything else we did not expect
+			errorMessage = "There was an error with the wsdl: " + wsdlURL;
+			ex = e;
+		}
+		
+		if (callBack == null) {
+			if (errorMessage != null){
+				logger.error(errorMessage, ex);
+				return false;
+			}
+			else{
+				// We are not adding service through Taverna service registry's callback and
+				// findServiceDescriptionsAsync() -
+				// we are adding directly from the BioCatalogue perspective.
+				ServiceDescriptionRegistryImpl serviceDescriptionRegistry = ServiceDescriptionRegistryImpl
+						.getInstance();
+				for (WSDLOperationFromBioCatalogueServiceDescription item : items) {
+					serviceDescriptionRegistry
+							.addServiceDescriptionProvider(new BioCatalogueWSDLOperationServiceProvider(
+									item));
+				}
+				return true;
+			}
+		} else {
+			if (errorMessage != null){
+				callBack.fail(errorMessage, ex);
+				return false;
+			}
+			else{
+				callBack.status("Found " + operations.size() + " WSDL operations of service "
+						+ wsdlURL);
+				callBack.partialResults(items);
+				callBack.finished();
+				return true;
+			}
+		}   
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/RESTFromBioCatalogueServiceDescription.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/RESTFromBioCatalogueServiceDescription.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/RESTFromBioCatalogueServiceDescription.java
new file mode 100644
index 0000000..1551294
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/RESTFromBioCatalogueServiceDescription.java
@@ -0,0 +1,194 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.service_panel;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.Icon;
+
+import net.sf.taverna.t2.lang.beans.PropertyAnnotation;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+import net.sf.taverna.t2.workflowmodel.processor.activity.Activity;
+
+import net.sf.taverna.t2.activities.rest.RESTActivity;
+import net.sf.taverna.t2.activities.rest.RESTActivity.DATA_FORMAT;
+import net.sf.taverna.t2.activities.rest.RESTActivity.HTTP_METHOD;
+import net.sf.taverna.t2.activities.rest.RESTActivityConfigurationBean;
+import net.sf.taverna.t2.activities.rest.ui.servicedescription.RESTActivityIcon;
+
+/**
+ * This class is solely intended to support import of REST services from BioCatalogue.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+/*******************************************************************************
+ * Copyright (C) 2008-2010 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
+ ******************************************************************************/
+public class RESTFromBioCatalogueServiceDescription extends ServiceDescription<RESTActivityConfigurationBean>
+{
+  private static final int SHORT_DESCRIPTION_MAX_LENGTH = 200;
+  
+  private static final String FULL_DESCRIPTION = "Full description";
+  
+  public static final int AMBIGUOUS_ACCEPT_HEADER_VALUE = 100;
+  public static final int DEFAULT_ACCEPT_HEADER_VALUE = 110;
+  public static final int AMBIGUOUS_CONTENT_TYPE_HEADER_VALUE = 200;
+  public static final int DEFAULT_CONTENT_TYPE_HEADER_VALUE = 210;
+  
+  
+	private RESTActivityConfigurationBean serviceConfigBean;
+	private String serviceName;
+	private String description;
+	
+	private List<Integer> dataWarnings;
+	
+	
+	/**
+	 * Constructor instantiates config bean and pre-populates
+	 * it with default values.
+	 */
+	public RESTFromBioCatalogueServiceDescription()
+	{
+	  // apply default name in case it won't be set manually later
+	  this.serviceName = "REST Service";
+	  this.serviceConfigBean = RESTActivityConfigurationBean.getDefaultInstance();
+	  this.dataWarnings = new ArrayList<Integer>();
+	}
+  
+  /**
+	 * The subclass of Activity which should be instantiated when adding a service
+	 * for this description.
+	 */
+	@Override
+	public Class<? extends Activity<RESTActivityConfigurationBean>> getActivityClass() {
+		return RESTActivity.class;
+	}
+
+	/**
+	 * The configuration bean which is to be used for configuring the instantiated activity.
+	 * 
+	 * Values are to be set through individual setters provided in this class.
+	 */
+	@Override
+	public RESTActivityConfigurationBean getActivityConfiguration() {
+		return serviceConfigBean;
+	}
+
+	/**
+	 * An icon to represent this service type in the service palette.
+	 */
+	@Override
+	public Icon getIcon() {
+	  return RESTActivityIcon.getRESTActivityIcon();
+	}
+
+	/**
+	 * The display name that will be shown in service palette and will
+	 * be used as a template for processor name when added to workflow.
+	 */
+	@Override
+	public String getName() {
+		return serviceName;
+	}
+	
+	
+	/**
+   * Truncates the description if necessary to {@link WSDLOperationFromBioCatalogueServiceDescription#SHORT_DESCRIPTION_MAX_LENGTH} --
+   * to get full description, use {@link WSDLOperationFromBioCatalogueServiceDescription#getFullDescription()}
+   */
+  public String getDescription() {
+    if (this.description != null && this.description.length() > SHORT_DESCRIPTION_MAX_LENGTH) {
+      return (this.description.substring(0, SHORT_DESCRIPTION_MAX_LENGTH) + "(...)");
+    }
+    else {
+      return this.description;
+    }
+  }
+  
+  @PropertyAnnotation(displayName = FULL_DESCRIPTION)
+  public String getFullDescription() {
+    return this.description;
+  }
+	
+
+	/**
+	 * The path to this service description in the service palette. Folders
+	 * will be created for each element of the returned path.
+	 * 
+	 * (Shouldn't really be ever used, as instances of different type are
+	 *  added into the Service Panel).
+	 */
+	@Override
+	public List<String> getPath() {
+		// For deeper paths you may return several strings
+		return Arrays.asList(BioCatalogueRESTServiceProvider.PROVIDER_NAME, "REST @ " + serviceConfigBean.getUrlSignature());
+	}
+
+	/**
+	 * Return a list of data values uniquely identifying this service
+	 * description (to avoid duplicates). Include only primary key like fields,
+	 * ie. ignore descriptions, icons, etc.
+	 */
+	@Override
+	protected List<? extends Object> getIdentifyingData() {
+		return Arrays.<Object>asList(serviceConfigBean.getUrlSignature(), serviceConfigBean.getHttpMethod());
+	}
+	
+	
+	public List<Integer> getDataWarnings() {
+	  return dataWarnings;
+	}
+	
+	
+	public void setURLSignature(String urlSignature) {
+	  this.serviceConfigBean.setUrlSignature(urlSignature);
+	}
+	
+	
+	public void setHttpMethod(HTTP_METHOD httpMethod) {
+    this.serviceConfigBean.setHttpMethod(httpMethod);
+  }
+	
+	
+	public void setAcceptHeaderValue(String acceptHeaderValue) {
+    this.serviceConfigBean.setAcceptsHeaderValue(acceptHeaderValue);
+  }
+	
+	
+	public void setOutgoingContentType(String outgoingContentType) 
+	{
+    this.serviceConfigBean.setUrlSignature(outgoingContentType);
+    
+    // automatically infer data format - string/binary from the content type
+    if (outgoingContentType.startsWith("text")) { this.serviceConfigBean.setOutgoingDataFormat(DATA_FORMAT.String); }
+    else { this.serviceConfigBean.setOutgoingDataFormat(DATA_FORMAT.Binary); }
+  }
+	
+	
+	public void setServiceName(String name) {
+	  this.serviceName = name;
+	}
+	
+	
+	public void setDescription(String description) {
+	  this.description = description;
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/WSDLOperationFromBioCatalogueServiceDescription.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/WSDLOperationFromBioCatalogueServiceDescription.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/WSDLOperationFromBioCatalogueServiceDescription.java
new file mode 100644
index 0000000..f5de088
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/service_panel/WSDLOperationFromBioCatalogueServiceDescription.java
@@ -0,0 +1,116 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.service_panel;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.Icon;
+
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.t2.activities.wsdl.WSDLActivity;
+import net.sf.taverna.t2.activities.wsdl.WSDLActivityConfigurationBean;
+import net.sf.taverna.t2.activities.wsdl.servicedescriptions.WSDLActivityIcon;
+import net.sf.taverna.t2.lang.beans.PropertyAnnotation;
+import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
+
+/*******************************************************************************
+ * Copyright (C) 2008-2010 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
+ ******************************************************************************/
+public class WSDLOperationFromBioCatalogueServiceDescription extends ServiceDescription<WSDLActivityConfigurationBean>
+{
+  private static final int SHORT_DESCRIPTION_MAX_LENGTH = 200;
+  
+  private static final String FULL_DESCRIPTION = "Full description";
+  
+  
+  private final String wsdlLocation;
+  private final String operationName;
+  private final String description;
+  
+  
+  public WSDLOperationFromBioCatalogueServiceDescription(String wsdlLocation, String operationName, String description)
+  {
+    this.wsdlLocation = wsdlLocation;
+    this.operationName = operationName;
+    this.description = description;
+  }
+  
+  public WSDLOperationFromBioCatalogueServiceDescription(SoapOperationIdentity soapOpearationIdentity)
+  {
+    this.wsdlLocation = soapOpearationIdentity.getWsdlLocation();
+    this.operationName = soapOpearationIdentity.getOperationName();
+    this.description = soapOpearationIdentity.getDescription();
+  }
+  
+  
+	@Override
+	public Class getActivityClass() {
+		return WSDLActivity.class;
+	}
+
+	@Override
+	public WSDLActivityConfigurationBean getActivityConfiguration() {
+		WSDLActivityConfigurationBean bean = new WSDLActivityConfigurationBean();
+		bean.setOperation(operationName);
+		bean.setWsdl(wsdlLocation);
+		return bean;
+	}
+
+	@Override
+	public Icon getIcon() {
+		return WSDLActivityIcon.getWSDLIcon();
+	}
+
+	@Override
+	public String getName() {
+		return (this.operationName);
+	}
+
+	/**
+	 * Truncates the description if necessary to {@link WSDLOperationFromBioCatalogueServiceDescription#SHORT_DESCRIPTION_MAX_LENGTH} --
+	 * to get full description, use {@link WSDLOperationFromBioCatalogueServiceDescription#getFullDescription()}
+	 */
+	public String getDescription() {
+    if (this.description != null && this.description.length() > SHORT_DESCRIPTION_MAX_LENGTH) {
+      return (this.description.substring(0, SHORT_DESCRIPTION_MAX_LENGTH) + "(...)");
+    }
+    else {
+      return this.description;
+    }
+  }
+	
+	@PropertyAnnotation(displayName = FULL_DESCRIPTION)
+	public String getFullDescription() {
+	  return this.description;
+	}
+	
+	@Override
+	public List<String> getPath() {
+		return Arrays.asList(BioCatalogueWSDLOperationServiceProvider.PROVIDER_NAME, "WSDL @ " + this.wsdlLocation);
+	}
+	
+	@Override
+	protected List<? extends Object> getIdentifyingData()
+	{
+	  // This is important - Taverna won't add identical operations
+	  // into the Service Panel. These tokens distinguish added items.
+		return Arrays.asList(wsdlLocation, operationName);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider
new file mode 100644
index 0000000..44cf82b
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider
@@ -0,0 +1,2 @@
+net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.service_panel.BioCatalogueWSDLOperationServiceProvider
+net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.service_panel.BioCatalogueRESTServiceProvider

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..8bdcfa5
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,2 @@
+net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.menus.BioCatalogueContextualMenuSection
+net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.menus.MenuActionProcessorHealthCheck
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ShutdownSPI
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ShutdownSPI b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ShutdownSPI
new file mode 100644
index 0000000..b036654
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ShutdownSPI
@@ -0,0 +1 @@
+#net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentShutdownHook
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
new file mode 100644
index 0000000..bc9b362
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@
+net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.config.BioCataloguePluginConfigurationUIFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.report.explainer.VisitExplainer
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.report.explainer.VisitExplainer b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.report.explainer.VisitExplainer
new file mode 100644
index 0000000..5b64699
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.report.explainer.VisitExplainer
@@ -0,0 +1 @@
+net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check.BioCatalogueWSDLActivityHealthCheckVisitExplainer
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
new file mode 100644
index 0000000..a26e638
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1,3 @@
+net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.contextual_views.BioCataloguePluginProcessorContextViewFactory
+#net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.contextual_views.BioCataloguePluginInputPortContextViewFactory
+#net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.contextual_views.BioCataloguePluginOutputPortContextViewFactory

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI
new file mode 100644
index 0000000..eb7f253
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.ui.perspectives.biocatalogue.BioCataloguePerspective

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
new file mode 100644
index 0000000..1c7d751
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
new file mode 100644
index 0000000..40b15c7
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponent

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workflowmodel.health.HealthChecker
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workflowmodel.health.HealthChecker b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workflowmodel.health.HealthChecker
new file mode 100644
index 0000000..f24a3dd
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/META-INF/services/net.sf.taverna.t2.workflowmodel.health.HealthChecker
@@ -0,0 +1 @@
+net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check.BioCatalogueWSDLActivityHealthChecker
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-grey-bert2-still.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-grey-bert2-still.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-grey-bert2-still.png
new file mode 100644
index 0000000..12abdf6
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-grey-bert2-still.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-grey-bert2.gif
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-grey-bert2.gif b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-grey-bert2.gif
new file mode 100644
index 0000000..c4793ec
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-grey-bert2.gif differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-orange-bert2-still.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-orange-bert2-still.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-orange-bert2-still.png
new file mode 100644
index 0000000..9b6784e
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-orange-bert2-still.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-orange-bert2.gif
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-orange-bert2.gif b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-orange-bert2.gif
new file mode 100644
index 0000000..c6745b9
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/ajax-loader-orange-bert2.gif differ


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64.java
new file mode 100644
index 0000000..00af810
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Base64.java
@@ -0,0 +1,1813 @@
+package net.sf.taverna.t2.ui.perspectives.myexperiment.model;
+
+import net.sf.taverna.t2.activities.dataflow.views.DataflowActivityConfigView;
+
+import org.apache.log4j.Logger;
+
+/**
+ * <p>Encodes and decodes to and from Base64 notation.</p>
+ * <p>Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.</p>
+ *
+ * <p>The <tt>options</tt> parameter, which appears in a few places, is used to pass 
+ * several pieces of information to the encoder. In the "higher level" methods such as 
+ * encodeBytes( bytes, options ) the options parameter can be used to indicate such 
+ * things as first gzipping the bytes before encoding them, not inserting linefeeds 
+ * (though that breaks strict Base64 compatibility), and encoding using the URL-safe 
+ * and Ordered dialects.</p>
+ *
+ * <p>The constants defined in Base64 can be OR-ed together to combine options, so you 
+ * might make a call like this:</p>
+ *
+ * <code>String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DONT_BREAK_LINES );</code>
+ *
+ * <p>to compress the data before encoding it and then making the output have no newline characters.</p>
+ *
+ *
+ * <p>
+ * Change Log:
+ * </p>
+ * <ul>
+ *  <li>v2.2.2 - Fixed encodeFileToFile and decodeFileToFile to use the
+ *   Base64.InputStream class to encode and decode on the fly which uses
+ *   less memory than encoding/decoding an entire file into memory before writing.</li>
+ *  <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug
+ *   when using very small files (~< 40 bytes).</li>
+ *  <li>v2.2 - Added some helper methods for encoding/decoding directly from
+ *   one file to the next. Also added a main() method to support command line
+ *   encoding/decoding from one file to the next. Also added these Base64 dialects:
+ *   <ol>
+ *   <li>The default is RFC3548 format.</li>
+ *   <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates
+ *   URL and file name friendly format as described in Section 4 of RFC3548.
+ *   http://www.faqs.org/rfcs/rfc3548.html</li>
+ *   <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates
+ *   URL and file name friendly format that preserves lexical ordering as described
+ *   in http://www.faqs.org/qa/rfcc-1940.html</li>
+ *   </ol>
+ *   Special thanks to Jim Kellerman at <a href="http://www.powerset.com/">http://www.powerset.com/</a>
+ *   for contributing the new Base64 dialects.
+ *  </li>
+ * 
+ *  <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
+ *   some convenience methods for reading and writing to and from files.</li>
+ *  <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
+ *   with other encodings (like EBCDIC).</li>
+ *  <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
+ *   encoded data was a single byte.</li>
+ *  <li>v2.0 - I got rid of methods that used booleans to set options. 
+ *   Now everything is more consolidated and cleaner. The code now detects
+ *   when data that's being decoded is gzip-compressed and will decompress it
+ *   automatically. Generally things are cleaner. You'll probably have to
+ *   change some method calls that you were making to support the new
+ *   options format (<tt>int</tt>s that you "OR" together).</li>
+ *  <li>v1.5.1 - Fixed bug when decompressing and decoding to a             
+ *   byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.      
+ *   Added the ability to "suspend" encoding in the Output Stream so        
+ *   you can turn on and off the encoding if you need to embed base64       
+ *   data in an otherwise "normal" stream (like an XML file).</li>  
+ *  <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
+ *      This helps when using GZIP streams.
+ *      Added the ability to GZip-compress objects before encoding them.</li>
+ *  <li>v1.4 - Added helper methods to read/write files.</li>
+ *  <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
+ *  <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
+ *      where last buffer being read, if not completely full, was not returned.</li>
+ *  <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
+ *  <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
+ * </ul>
+ *
+ * <p>
+ * I am placing this code in the Public Domain. Do with it as you will.
+ * This software comes with no guarantees or warranties but with
+ * plenty of well-wishing instead!
+ * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
+ * periodically to check for updates or to contribute improvements.
+ * </p>
+ *
+ * @author Robert Harder
+ * @author rob@iharder.net
+ * @version 2.2.2
+ */
+public class Base64
+{
+    
+	private static Logger logger = Logger
+	.getLogger(Base64.class);
+
+	/* ********  P U B L I C   F I E L D S  ******** */   
+    
+    
+    /** No options specified. Value is zero. */
+    public final static int NO_OPTIONS = 0;
+    
+    /** Specify encoding. */
+    public final static int ENCODE = 1;
+    
+    
+    /** Specify decoding. */
+    public final static int DECODE = 0;
+    
+    
+    /** Specify that data should be gzip-compressed. */
+    public final static int GZIP = 2;
+    
+    
+    /** Don't break lines when encoding (violates strict Base64 specification) */
+    public final static int DONT_BREAK_LINES = 8;
+	
+	/** 
+	 * Encode using Base64-like encoding that is URL- and Filename-safe as described
+	 * in Section 4 of RFC3548: 
+	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
+	 * It is important to note that data encoded this way is <em>not</em> officially valid Base64, 
+	 * or at the very least should not be called Base64 without also specifying that is
+	 * was encoded using the URL- and Filename-safe dialect.
+	 */
+	 public final static int URL_SAFE = 16;
+	 
+	 
+	 /**
+	  * Encode using the special "ordered" dialect of Base64 described here:
+	  * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
+	  */
+	 public final static int ORDERED = 32;
+    
+    
+/* ********  P R I V A T E   F I E L D S  ******** */  
+    
+    
+    /** Maximum line length (76) of Base64 output. */
+    private final static int MAX_LINE_LENGTH = 76;
+    
+    
+    /** The equals sign (=) as a byte. */
+    private final static byte EQUALS_SIGN = (byte)'=';
+    
+    
+    /** The new line character (\n) as a byte. */
+    private final static byte NEW_LINE = (byte)'\n';
+    
+    
+    /** Preferred encoding. */
+    private final static String PREFERRED_ENCODING = "UTF-8";
+    
+	
+    // I think I end up not using the BAD_ENCODING indicator.
+    //private final static byte BAD_ENCODING    = -9; // Indicates error in encoding
+    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
+    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
+	
+	
+/* ********  S T A N D A R D   B A S E 6 4   A L P H A B E T  ******** */	
+    
+    /** The 64 valid Base64 values. */
+    //private final static byte[] ALPHABET;
+	/* Host platform me be something funny like EBCDIC, so we hardcode these values. */
+	private final static byte[] _STANDARD_ALPHABET =
+    {
+        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
+        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+        (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+        (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+        (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
+        (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+        (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
+        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
+    };
+	
+    
+    /** 
+     * Translates a Base64 value to either its 6-bit reconstruction value
+     * or a negative number indicating some other meaning.
+     **/
+    private final static byte[] _STANDARD_DECODABET =
+    {   
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+        -5,-5,                                      // Whitespace: Tab and Linefeed
+        -9,-9,                                      // Decimal 11 - 12
+        -5,                                         // Whitespace: Carriage Return
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+        -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+        -5,                                         // Whitespace: Space
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+        62,                                         // Plus sign at decimal 43
+        -9,-9,-9,                                   // Decimal 44 - 46
+        63,                                         // Slash at decimal 47
+        52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
+        -9,-9,-9,                                   // Decimal 58 - 60
+        -1,                                         // Equals sign at decimal 61
+        -9,-9,-9,                                      // Decimal 62 - 64
+        0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
+        14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
+        -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
+        26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
+        39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
+        -9,-9,-9,-9                                 // Decimal 123 - 126
+        /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+	
+	
+/* ********  U R L   S A F E   B A S E 6 4   A L P H A B E T  ******** */
+	
+	/**
+	 * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: 
+	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
+	 * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
+	 */
+    private final static byte[] _URL_SAFE_ALPHABET =
+    {
+      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
+      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
+      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
+      (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'
+    };
+	
+	/**
+	 * Used in decoding URL- and Filename-safe dialects of Base64.
+	 */
+    private final static byte[] _URL_SAFE_DECODABET =
+    {   
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+      -5,-5,                                      // Whitespace: Tab and Linefeed
+      -9,-9,                                      // Decimal 11 - 12
+      -5,                                         // Whitespace: Carriage Return
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+      -5,                                         // Whitespace: Space
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+      -9,                                         // Plus sign at decimal 43
+      -9,                                         // Decimal 44
+      62,                                         // Minus sign at decimal 45
+      -9,                                         // Decimal 46
+      -9,                                         // Slash at decimal 47
+      52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
+      -9,-9,-9,                                   // Decimal 58 - 60
+      -1,                                         // Equals sign at decimal 61
+      -9,-9,-9,                                   // Decimal 62 - 64
+      0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
+      14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
+      -9,-9,-9,-9,                                // Decimal 91 - 94
+      63,                                         // Underscore at decimal 95
+      -9,                                         // Decimal 96
+      26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
+      39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
+      -9,-9,-9,-9                                 // Decimal 123 - 126
+      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+
+
+/* ********  O R D E R E D   B A S E 6 4   A L P H A B E T  ******** */
+
+	/**
+	 * I don't get the point of this technique, but it is described here:
+	 * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
+	 */
+    private final static byte[] _ORDERED_ALPHABET =
+    {
+      (byte)'-',
+      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',
+      (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',
+      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+      (byte)'_',
+      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z'
+    };
+	
+	/**
+	 * Used in decoding the "ordered" dialect of Base64.
+	 */
+    private final static byte[] _ORDERED_DECODABET =
+    {   
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+      -5,-5,                                      // Whitespace: Tab and Linefeed
+      -9,-9,                                      // Decimal 11 - 12
+      -5,                                         // Whitespace: Carriage Return
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+      -5,                                         // Whitespace: Space
+      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+      -9,                                         // Plus sign at decimal 43
+      -9,                                         // Decimal 44
+      0,                                          // Minus sign at decimal 45
+      -9,                                         // Decimal 46
+      -9,                                         // Slash at decimal 47
+      1,2,3,4,5,6,7,8,9,10,                       // Numbers zero through nine
+      -9,-9,-9,                                   // Decimal 58 - 60
+      -1,                                         // Equals sign at decimal 61
+      -9,-9,-9,                                   // Decimal 62 - 64
+      11,12,13,14,15,16,17,18,19,20,21,22,23,     // Letters 'A' through 'M'
+      24,25,26,27,28,29,30,31,32,33,34,35,36,     // Letters 'N' through 'Z'
+      -9,-9,-9,-9,                                // Decimal 91 - 94
+      37,                                         // Underscore at decimal 95
+      -9,                                         // Decimal 96
+      38,39,40,41,42,43,44,45,46,47,48,49,50,     // Letters 'a' through 'm'
+      51,52,53,54,55,56,57,58,59,60,61,62,63,     // Letters 'n' through 'z'
+      -9,-9,-9,-9                                 // Decimal 123 - 126
+      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+
+	
+/* ********  D E T E R M I N E   W H I C H   A L H A B E T  ******** */
+
+
+	/**
+	 * Returns one of the _SOMETHING_ALPHABET byte arrays depending on
+	 * the options specified.
+	 * It's possible, though silly, to specify ORDERED and URLSAFE
+	 * in which case one of them will be picked, though there is
+	 * no guarantee as to which one will be picked.
+	 */
+	private final static byte[] getAlphabet( int options )
+	{
+		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET;
+		else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET;
+		else return _STANDARD_ALPHABET;
+		
+	}	// end getAlphabet
+	
+	
+	/**
+	 * Returns one of the _SOMETHING_DECODABET byte arrays depending on
+	 * the options specified.
+	 * It's possible, though silly, to specify ORDERED and URL_SAFE
+	 * in which case one of them will be picked, though there is
+	 * no guarantee as to which one will be picked.
+	 */
+	private final static byte[] getDecodabet( int options )
+	{
+		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET;
+		else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET;
+		else return _STANDARD_DECODABET;
+		
+	}	// end getAlphabet
+        
+
+    
+    /** Defeats instantiation. */
+    private Base64(){}
+    
+
+    /**
+     * Encodes or decodes two files from the command line;
+     * <strong>feel free to delete this method (in fact you probably should)
+     * if you're embedding this code into a larger program.</strong>
+     */
+    public final static void main( String[] args )
+    {
+        if( args.length < 3 ){
+            usage("Not enough arguments.");
+        }   // end if: args.length < 3
+        else {
+            String flag = args[0];
+            String infile = args[1];
+            String outfile = args[2];
+            if( flag.equals( "-e" ) ){
+                Base64.encodeFileToFile( infile, outfile );
+            }   // end if: encode
+            else if( flag.equals( "-d" ) ) {
+                Base64.decodeFileToFile( infile, outfile );
+            }   // end else if: decode    
+            else {
+                usage( "Unknown flag: " + flag );
+            }   // end else    
+        }   // end else
+    }   // end main
+
+    /**
+     * Prints command line usage.
+     *
+     * @param msg A message to include with usage info.
+     */
+    private final static void usage( String msg )
+    {
+        logger.error( msg );
+        logger.error( "Usage: java Base64 -e|-d inputfile outputfile" );
+    }   // end usage
+    
+    
+/* ********  E N C O D I N G   M E T H O D S  ******** */    
+    
+    
+    /**
+     * Encodes up to the first three bytes of array <var>threeBytes</var>
+     * and returns a four-byte array in Base64 notation.
+     * The actual number of significant bytes in your array is
+     * given by <var>numSigBytes</var>.
+     * The array <var>threeBytes</var> needs only be as big as
+     * <var>numSigBytes</var>.
+     * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
+     *
+     * @param b4 A reusable byte array to reduce array instantiation
+     * @param threeBytes the array to convert
+     * @param numSigBytes the number of significant bytes in your array
+     * @return four byte array in Base64 notation.
+     * @since 1.5.1
+     */
+    private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options )
+    {
+        encode3to4( threeBytes, 0, numSigBytes, b4, 0, options );
+        return b4;
+    }   // end encode3to4
+
+    
+    /**
+     * <p>Encodes up to three bytes of the array <var>source</var>
+     * and writes the resulting four Base64 bytes to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying 
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 3 for
+     * the <var>source</var> array or <var>destOffset</var> + 4 for
+     * the <var>destination</var> array.
+     * The actual number of significant bytes in your array is
+     * given by <var>numSigBytes</var>.</p>
+	 * <p>This is the lowest level of the encoding methods with
+	 * all possible parameters.</p>
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param numSigBytes the number of significant bytes in your array
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+     * @return the <var>destination</var> array
+     * @since 1.3
+     */
+    private static byte[] encode3to4( 
+     byte[] source, int srcOffset, int numSigBytes,
+     byte[] destination, int destOffset, int options )
+    {
+		byte[] ALPHABET = getAlphabet( options ); 
+	
+        //           1         2         3  
+        // 01234567890123456789012345678901 Bit position
+        // --------000000001111111122222222 Array position from threeBytes
+        // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
+        //          >>18  >>12  >> 6  >> 0  Right shift necessary
+        //                0x3f  0x3f  0x3f  Additional AND
+        
+        // Create buffer with zero-padding if there are only one or two
+        // significant bytes passed in the array.
+        // We have to shift left 24 in order to flush out the 1's that appear
+        // when Java treats a value as negative that is cast from a byte to an int.
+        int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
+                     | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
+                     | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
+
+        switch( numSigBytes )
+        {
+            case 3:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
+                return destination;
+                
+            case 2:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+                
+            case 1:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = EQUALS_SIGN;
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+                
+            default:
+                return destination;
+        }   // end switch
+    }   // end encode3to4
+    
+    
+    
+    /**
+     * Serializes an object and returns the Base64-encoded
+     * version of that serialized object. If the object
+     * cannot be serialized or there is another error,
+     * the method will return <tt>null</tt>.
+     * The object is not GZip-compressed before being encoded.
+     *
+     * @param serializableObject The object to encode
+     * @return The Base64-encoded object
+     * @since 1.4
+     */
+    public static String encodeObject( java.io.Serializable serializableObject )
+    {
+        return encodeObject( serializableObject, NO_OPTIONS );
+    }   // end encodeObject
+    
+
+
+    /**
+     * Serializes an object and returns the Base64-encoded
+     * version of that serialized object. If the object
+     * cannot be serialized or there is another error,
+     * the method will return <tt>null</tt>.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     * @param serializableObject The object to encode
+     * @param options Specified options
+     * @return The Base64-encoded object
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeObject( java.io.Serializable serializableObject, int options )
+    {
+        // Streams
+        java.io.ByteArrayOutputStream  baos  = null; 
+        java.io.OutputStream           b64os = null; 
+        java.io.ObjectOutputStream     oos   = null; 
+        java.util.zip.GZIPOutputStream gzos  = null;
+        
+        // Isolate options
+        int gzip           = (options & GZIP);
+        int dontBreakLines = (options & DONT_BREAK_LINES);
+        
+        try
+        {
+            // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
+            baos  = new java.io.ByteArrayOutputStream();
+            b64os = new Base64.OutputStream( baos, ENCODE | options );
+    
+            // GZip?
+            if( gzip == GZIP )
+            {
+                gzos = new java.util.zip.GZIPOutputStream( b64os );
+                oos  = new java.io.ObjectOutputStream( gzos );
+            }   // end if: gzip
+            else
+                oos   = new java.io.ObjectOutputStream( b64os );
+            
+            oos.writeObject( serializableObject );
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error("Could not Base64 encode object");
+            return null;
+        }   // end catch
+        finally
+        {
+            try{ oos.close();   } catch( Exception e ){logger.error("", e);}
+        }   // end finally
+        
+        // Return value according to relevant encoding.
+        try 
+        {
+            return new String( baos.toByteArray(), PREFERRED_ENCODING );
+        }   // end try
+        catch (java.io.UnsupportedEncodingException uue)
+        {
+            return new String( baos.toByteArray() );
+        }   // end catch
+        
+    }   // end encode
+    
+    
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source )
+    {
+        return encodeBytes( source, 0, source.length, NO_OPTIONS );
+    }   // end encodeBytes
+    
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param options Specified options
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int options )
+    {   
+        return encodeBytes( source, 0, source.length, options );
+    }   // end encodeBytes
+    
+    
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source, int off, int len )
+    {
+        return encodeBytes( source, off, len, NO_OPTIONS );
+    }   // end encodeBytes
+    
+    
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @param options Specified options
+	 * @param options alphabet type is pulled from this (standard, url-safe, ordered)
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int off, int len, int options )
+    {
+        // Isolate options
+        int dontBreakLines = ( options & DONT_BREAK_LINES );
+        int gzip           = ( options & GZIP   );
+        
+        // Compress?
+        if( gzip == GZIP )
+        {
+            java.io.ByteArrayOutputStream  baos  = null;
+            java.util.zip.GZIPOutputStream gzos  = null;
+            Base64.OutputStream            b64os = null;
+            
+    
+            try
+            {
+                // GZip -> Base64 -> ByteArray
+                baos = new java.io.ByteArrayOutputStream();
+                b64os = new Base64.OutputStream( baos, ENCODE | options );
+                gzos  = new java.util.zip.GZIPOutputStream( b64os ); 
+            
+                gzos.write( source, off, len );
+                gzos.close();
+            }   // end try
+            catch( java.io.IOException e )
+            {
+            	logger.error("Could not encode bytes", e);
+               return null;
+            }   // end catch
+            finally
+            {
+                try{ gzos.close();  } catch( Exception e ){logger.error("", e);}
+                try{ b64os.close(); } catch( Exception e ){logger.error("", e);}
+                try{ baos.close();  } catch( Exception e ){logger.error("", e);}
+            }   // end finally
+
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( baos.toByteArray(), PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( baos.toByteArray() );
+            }   // end catch
+        }   // end if: compress
+        
+        // Else, don't compress. Better not to use streams at all then.
+        else
+        {
+            // Convert option to boolean in way that code likes it.
+            boolean breakLines = dontBreakLines == 0;
+            
+            int    len43   = len * 4 / 3;
+            byte[] outBuff = new byte[   ( len43 )                      // Main 4:3
+                                       + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding
+                                       + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines      
+            int d = 0;
+            int e = 0;
+            int len2 = len - 2;
+            int lineLength = 0;
+            for( ; d < len2; d+=3, e+=4 )
+            {
+                encode3to4( source, d+off, 3, outBuff, e, options );
+
+                lineLength += 4;
+                if( breakLines && lineLength == MAX_LINE_LENGTH )
+                {   
+                    outBuff[e+4] = NEW_LINE;
+                    e++;
+                    lineLength = 0;
+                }   // end if: end of line
+            }   // en dfor: each piece of array
+
+            if( d < len )
+            {
+                encode3to4( source, d+off, len - d, outBuff, e, options );
+                e += 4;
+            }   // end if: some padding needed
+
+            
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( outBuff, 0, e, PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( outBuff, 0, e );
+            }   // end catch
+            
+        }   // end else: don't compress
+        
+    }   // end encodeBytes
+    
+
+    
+    
+    
+/* ********  D E C O D I N G   M E T H O D S  ******** */
+    
+    
+    /**
+     * Decodes four bytes from array <var>source</var>
+     * and writes the resulting bytes (up to three of them)
+     * to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying 
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 4 for
+     * the <var>source</var> array or <var>destOffset</var> + 3 for
+     * the <var>destination</var> array.
+     * This method returns the actual number of bytes that 
+     * were converted from the Base64 encoding.
+	 * <p>This is the lowest level of the decoding methods with
+	 * all possible parameters.</p>
+     * 
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+	 * @param options alphabet type is pulled from this (standard, url-safe, ordered)
+     * @return the number of decoded bytes converted
+     * @since 1.3
+     */
+    private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options )
+    {
+		byte[] DECODABET = getDecodabet( options ); 
+	
+        // Example: Dk==
+        if( source[ srcOffset + 2] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
+            
+            destination[ destOffset ] = (byte)( outBuff >>> 16 );
+            return 1;
+        }
+        
+        // Example: DkL=
+        else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );
+            
+            destination[ destOffset     ] = (byte)( outBuff >>> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );
+            return 2;
+        }
+        
+        // Example: DkLE
+        else
+        {
+            try{
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
+            //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)
+                          | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );
+
+            
+            destination[ destOffset     ] = (byte)( outBuff >> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );
+            destination[ destOffset + 2 ] = (byte)( outBuff       );
+
+            return 3;
+            }catch( Exception e){
+                logger.error(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) , e);
+                logger.error(""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ), e);
+                logger.error(""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ), e);
+                logger.error(""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ), e);
+                return -1;
+            }   // end catch
+        }
+    }   // end decodeToBytes
+    
+    
+    
+    
+    /**
+     * Very low-level access to decoding ASCII characters in
+     * the form of a byte array. Does not support automatically
+     * gunzipping or any other "fancy" features.
+     *
+     * @param source The Base64 encoded data
+     * @param off    The offset of where to begin decoding
+     * @param len    The length of characters to decode
+     * @return decoded data
+     * @since 1.3
+     */
+    public static byte[] decode( byte[] source, int off, int len, int options )
+    {
+		byte[] DECODABET = getDecodabet( options );
+	
+        int    len34   = len * 3 / 4;
+        byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
+        int    outBuffPosn = 0;
+        
+        byte[] b4        = new byte[4];
+        int    b4Posn    = 0;
+        int    i         = 0;
+        byte   sbiCrop   = 0;
+        byte   sbiDecode = 0;
+        for( i = off; i < off+len; i++ )
+        {
+            sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
+            sbiDecode = DECODABET[ sbiCrop ];
+            
+            if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
+            {
+                if( sbiDecode >= EQUALS_SIGN_ENC )
+                {
+                    b4[ b4Posn++ ] = sbiCrop;
+                    if( b4Posn > 3 )
+                    {
+                        outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );
+                        b4Posn = 0;
+                        
+                        // If that was the equals sign, break out of 'for' loop
+                        if( sbiCrop == EQUALS_SIGN )
+                            break;
+                    }   // end if: quartet built
+                    
+                }   // end if: equals sign or better
+                
+            }   // end if: white space, equals sign or better
+            else
+            {
+                logger.error( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
+                return null;
+            }   // end else: 
+        }   // each input character
+                                   
+        byte[] out = new byte[ outBuffPosn ];
+        System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 
+        return out;
+    }   // end decode
+    
+    
+	
+	
+    /**
+     * Decodes data from Base64 notation, automatically
+     * detecting gzip-compressed data and decompressing it.
+     *
+     * @param s the string to decode
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s )
+	{
+		return decode( s, NO_OPTIONS );
+	}
+    
+    
+    /**
+     * Decodes data from Base64 notation, automatically
+     * detecting gzip-compressed data and decompressing it.
+     *
+     * @param s the string to decode
+	 * @param options encode options such as URL_SAFE
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s, int options )
+    {   
+        byte[] bytes;
+        try
+        {
+            bytes = s.getBytes( PREFERRED_ENCODING );
+        }   // end try
+        catch( java.io.UnsupportedEncodingException uee )
+        {
+            bytes = s.getBytes();
+        }   // end catch
+		//</change>
+        
+        // Decode
+        bytes = decode( bytes, 0, bytes.length, options );
+        
+        
+        // Check to see if it's gzip-compressed
+        // GZIP Magic Two-Byte Number: 0x8b1f (35615)
+        if( bytes != null && bytes.length >= 4 )
+        {
+            
+            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);       
+            if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) 
+            {
+                java.io.ByteArrayInputStream  bais = null;
+                java.util.zip.GZIPInputStream gzis = null;
+                java.io.ByteArrayOutputStream baos = null;
+                byte[] buffer = new byte[2048];
+                int    length = 0;
+
+                try
+                {
+                    baos = new java.io.ByteArrayOutputStream();
+                    bais = new java.io.ByteArrayInputStream( bytes );
+                    gzis = new java.util.zip.GZIPInputStream( bais );
+
+                    while( ( length = gzis.read( buffer ) ) >= 0 )
+                    {
+                        baos.write(buffer,0,length);
+                    }   // end while: reading input
+
+                    // No error? Get new bytes.
+                    bytes = baos.toByteArray();
+
+                }   // end try
+                catch( java.io.IOException e )
+                {
+                    // Just return originally-decoded bytes
+                }   // end catch
+                finally
+                {
+                    try{ baos.close(); } catch( Exception e ){logger.error("", e);}
+                    try{ gzis.close(); } catch( Exception e ){logger.error("", e);}
+                    try{ bais.close(); } catch( Exception e ){logger.error("", e);}
+                }   // end finally
+
+            }   // end if: gzipped
+        }   // end if: bytes.length >= 2
+        
+        return bytes;
+    }   // end decode
+
+
+    
+
+    /**
+     * Attempts to decode Base64 data and deserialize a Java
+     * Object within. Returns <tt>null</tt> if there was an error.
+     *
+     * @param encodedObject The Base64 data to decode
+     * @return The decoded and deserialized object
+     * @since 1.5
+     */
+    public static Object decodeToObject( String encodedObject )
+    {
+        // Decode and gunzip if necessary
+        byte[] objBytes = decode( encodedObject );
+        
+        java.io.ByteArrayInputStream  bais = null;
+        java.io.ObjectInputStream     ois  = null;
+        Object obj = null;
+        
+        try
+        {
+            bais = new java.io.ByteArrayInputStream( objBytes );
+            ois  = new java.io.ObjectInputStream( bais );
+        
+            obj = ois.readObject();
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error("Could not decode Base64 object", e);
+            obj = null;
+        }   // end catch
+        catch( java.lang.ClassNotFoundException e )
+        {
+            logger.error("Could not decode Base64 object", e);
+            obj = null;
+        }   // end catch
+        finally
+        {
+            try{ bais.close(); } catch( Exception e ){logger.error("", e);}
+            try{ ois.close();  } catch( Exception e ){logger.error("", e);}
+        }   // end finally
+        
+        return obj;
+    }   // end decodeObject
+    
+    
+    
+    /**
+     * Convenience method for encoding data to a file.
+     *
+     * @param dataToEncode byte array of data to encode in base64 form
+     * @param filename Filename for saving encoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     *
+     * @since 2.1
+     */
+    public static boolean encodeToFile( byte[] dataToEncode, String filename )
+    {
+        boolean success = false;
+        Base64.OutputStream bos = null;
+        try
+        {
+            bos = new Base64.OutputStream( 
+                      new java.io.FileOutputStream( filename ), Base64.ENCODE );
+            bos.write( dataToEncode );
+            success = true;
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            
+            success = false;
+        }   // end catch: IOException
+        finally
+        {
+            try{ bos.close(); } catch( Exception e ){logger.error("", e);}
+        }   // end finally
+        
+        return success;
+    }   // end encodeToFile
+    
+    
+    /**
+     * Convenience method for decoding data to a file.
+     *
+     * @param dataToDecode Base64-encoded data as a string
+     * @param filename Filename for saving decoded data
+     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
+     *
+     * @since 2.1
+     */
+    public static boolean decodeToFile( String dataToDecode, String filename )
+    {
+        boolean success = false;
+        Base64.OutputStream bos = null;
+        try
+        {
+                bos = new Base64.OutputStream( 
+                          new java.io.FileOutputStream( filename ), Base64.DECODE );
+                bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
+                success = true;
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            success = false;
+        }   // end catch: IOException
+        finally
+        {
+                try{ bos.close(); } catch( Exception e ){logger.error("", e);}
+        }   // end finally
+        
+        return success;
+    }   // end decodeToFile
+    
+    
+    
+    
+    /**
+     * Convenience method for reading a base64-encoded
+     * file and decoding it.
+     *
+     * @param filename Filename for reading encoded data
+     * @return decoded byte array or null if unsuccessful
+     *
+     * @since 2.1
+     */
+    public static byte[] decodeFromFile( String filename )
+    {
+        byte[] decodedData = null;
+        Base64.InputStream bis = null;
+        try
+        {
+            // Set up some useful variables
+            java.io.File file = new java.io.File( filename );
+            byte[] buffer = null;
+            int length   = 0;
+            int numBytes = 0;
+            
+            // Check for size of file
+            if( file.length() > Integer.MAX_VALUE )
+            {
+                logger.error( "File is too big for this convenience method (" + file.length() + " bytes)." );
+                return null;
+            }   // end if: file too big for int index
+            buffer = new byte[ (int)file.length() ];
+            
+            // Open a stream
+            bis = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( file ) ), Base64.DECODE );
+            
+            // Read until done
+            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
+                length += numBytes;
+            
+            // Save in a variable to return
+            decodedData = new byte[ length ];
+            System.arraycopy( buffer, 0, decodedData, 0, length );
+            
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error( "Error decoding from file " + filename, e);
+        }   // end catch: IOException
+        finally
+        {
+            try{ bis.close(); } catch( Exception e) {logger.error("", e);}
+        }   // end finally
+        
+        return decodedData;
+    }   // end decodeFromFile
+    
+    
+    
+    /**
+     * Convenience method for reading a binary file
+     * and base64-encoding it.
+     *
+     * @param filename Filename for reading binary data
+     * @return base64-encoded string or null if unsuccessful
+     *
+     * @since 2.1
+     */
+    public static String encodeFromFile( String filename )
+    {
+        String encodedData = null;
+        Base64.InputStream bis = null;
+        try
+        {
+            // Set up some useful variables
+            java.io.File file = new java.io.File( filename );
+            byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1)
+            int length   = 0;
+            int numBytes = 0;
+            
+            // Open a stream
+            bis = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( file ) ), Base64.ENCODE );
+            
+            // Read until done
+            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
+                length += numBytes;
+            
+            // Save in a variable to return
+            encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
+                
+        }   // end try
+        catch( java.io.IOException e )
+        {
+            logger.error( "Error encoding from file " + filename, e);
+        }   // end catch: IOException
+        finally
+        {
+            try{ bis.close(); } catch( Exception e) {logger.error("", e);}
+        }   // end finally
+        
+        return encodedData;
+        }   // end encodeFromFile
+    
+    
+    
+    
+    /**
+     * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
+     *
+     * @param infile Input file
+     * @param outfile Output file
+     * @return true if the operation is successful
+     * @since 2.2
+     */
+    public static boolean encodeFileToFile( String infile, String outfile )
+    {
+        boolean success = false;
+        java.io.InputStream in = null;
+        java.io.OutputStream out = null;
+        try{
+            in  = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( infile ) ), 
+                      Base64.ENCODE );
+            out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) );
+            byte[] buffer = new byte[65536]; // 64K
+            int read = -1;
+            while( ( read = in.read(buffer) ) >= 0 ){
+                out.write( buffer,0,read );
+            }   // end while: through file
+            success = true;
+        } catch( java.io.IOException exc ){
+            logger.error("Could not encode Base64 file", exc);
+        } finally{
+            try{ in.close();  } catch( Exception exc ){logger.error("", exc);}
+            try{ out.close(); } catch( Exception exc ){logger.error("", exc);}
+        }   // end finally
+        
+        return success;
+    }   // end encodeFileToFile
+    
+    
+    
+    /**
+     * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
+     *
+     * @param infile Input file
+     * @param outfile Output file
+     * @return true if the operation is successful
+     * @since 2.2
+     */
+    public static boolean decodeFileToFile( String infile, String outfile )
+    {
+        boolean success = false;
+        java.io.InputStream in = null;
+        java.io.OutputStream out = null;
+        try{
+            in  = new Base64.InputStream( 
+                      new java.io.BufferedInputStream( 
+                      new java.io.FileInputStream( infile ) ), 
+                      Base64.DECODE );
+            out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) );
+            byte[] buffer = new byte[65536]; // 64K
+            int read = -1;
+            while( ( read = in.read(buffer) ) >= 0 ){
+                out.write( buffer,0,read );
+            }   // end while: through file
+            success = true;
+        } catch( java.io.IOException exc ){
+            logger.error("Could not decode Base64 file", exc);
+       } finally{
+            try{ in.close();  } catch( Exception exc ){logger.error("", exc);}
+            try{ out.close(); } catch( Exception exc ){logger.error("", exc);}
+        }   // end finally
+        
+        return success;
+    }   // end decodeFileToFile
+    
+    
+    /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
+    
+    
+    
+    /**
+     * A {@link Base64.InputStream} will read data from another
+     * <tt>java.io.InputStream</tt>, given in the constructor,
+     * and encode/decode to/from Base64 notation on the fly.
+     *
+     * @see Base64
+     * @since 1.3
+     */
+    public static class InputStream extends java.io.FilterInputStream
+    {
+        private boolean encode;         // Encoding or decoding
+        private int     position;       // Current position in the buffer
+        private byte[]  buffer;         // Small buffer holding converted data
+        private int     bufferLength;   // Length of buffer (3 or 4)
+        private int     numSigBytes;    // Number of meaningful bytes in the buffer
+        private int     lineLength;
+        private boolean breakLines;     // Break lines at less than 80 characters
+		private int     options;        // Record options used to create the stream.
+		private byte[]  alphabet;	    // Local copies to avoid extra method calls
+		private byte[]  decodabet;		// Local copies to avoid extra method calls
+        
+        
+        /**
+         * Constructs a {@link Base64.InputStream} in DECODE mode.
+         *
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @since 1.3
+         */
+        public InputStream( java.io.InputStream in )
+        {   
+            this( in, DECODE );
+        }   // end constructor
+        
+        
+        /**
+         * Constructs a {@link Base64.InputStream} in
+         * either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:<pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+         * </pre>
+         * <p>
+         * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
+         *
+         *
+         * @param in the <tt>java.io.InputStream</tt> from which to read data.
+         * @param options Specified options
+         * @see Base64#ENCODE
+         * @see Base64#DECODE
+         * @see Base64#DONT_BREAK_LINES
+         * @since 2.0
+         */
+        public InputStream( java.io.InputStream in, int options )
+        {   
+            super( in );
+            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode       = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 4 : 3;
+            this.buffer       = new byte[ bufferLength ];
+            this.position     = -1;
+            this.lineLength   = 0;
+			this.options      = options; // Record for later, mostly to determine which alphabet to use
+			this.alphabet     = getAlphabet(options);
+			this.decodabet    = getDecodabet(options);
+        }   // end constructor
+        
+        /**
+         * Reads enough of the input stream to convert
+         * to/from Base64 and returns the next byte.
+         *
+         * @return next byte
+         * @since 1.3
+         */
+        public int read() throws java.io.IOException 
+        { 
+            // Do we need to get data?
+            if( position < 0 )
+            {
+                if( encode )
+                {
+                    byte[] b3 = new byte[3];
+                    int numBinaryBytes = 0;
+                    for( int i = 0; i < 3; i++ )
+                    {
+                        try
+                        { 
+                            int b = in.read();
+                            
+                            // If end of stream, b is -1.
+                            if( b >= 0 )
+                            {
+                                b3[i] = (byte)b;
+                                numBinaryBytes++;
+                            }   // end if: not end of stream
+                            
+                        }   // end try: read
+                        catch( java.io.IOException e )
+                        {   
+                            // Only a problem if we got no data at all.
+                            if( i == 0 )
+                                throw e;
+                            
+                        }   // end catch
+                    }   // end for: each needed input byte
+                    
+                    if( numBinaryBytes > 0 )
+                    {
+                        encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
+                        position = 0;
+                        numSigBytes = 4;
+                    }   // end if: got data
+                    else
+                    {
+                        return -1;
+                    }   // end else
+                }   // end if: encoding
+                
+                // Else decoding
+                else
+                {
+                    byte[] b4 = new byte[4];
+                    int i = 0;
+                    for( i = 0; i < 4; i++ )
+                    {
+                        // Read four "meaningful" bytes:
+                        int b = 0;
+                        do{ b = in.read(); }
+                        while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );
+                        
+                        if( b < 0 )
+                            break; // Reads a -1 if end of stream
+                        
+                        b4[i] = (byte)b;
+                    }   // end for: each needed input byte
+                    
+                    if( i == 4 )
+                    {
+                        numSigBytes = decode4to3( b4, 0, buffer, 0, options );
+                        position = 0;
+                    }   // end if: got four characters
+                    else if( i == 0 ){
+                        return -1;
+                    }   // end else if: also padded correctly
+                    else
+                    {
+                        // Must have broken out from above.
+                        throw new java.io.IOException( "Improperly padded Base64 input." );
+                    }   // end 
+                    
+                }   // end else: decode
+            }   // end else: get data
+            
+            // Got data?
+            if( position >= 0 )
+            {
+                // End of relevant data?
+                if( /*!encode &&*/ position >= numSigBytes )
+                    return -1;
+                
+                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
+                {
+                    lineLength = 0;
+                    return '\n';
+                }   // end if
+                else
+                {
+                    lineLength++;   // This isn't important when decoding
+                                    // but throwing an extra "if" seems
+                                    // just as wasteful.
+                    
+                    int b = buffer[ position++ ];
+
+                    if( position >= bufferLength )
+                        position = -1;
+
+                    return b & 0xFF; // This is how you "cast" a byte that's
+                                     // intended to be unsigned.
+                }   // end else
+            }   // end if: position >= 0
+            
+            // Else error
+            else
+            {   
+                // When JDK1.4 is more accepted, use an assertion here.
+                throw new java.io.IOException( "Error in Base64 code reading stream." );
+            }   // end else
+        }   // end read
+        
+        
+        /**
+         * Calls {@link #read()} repeatedly until the end of stream
+         * is reached or <var>len</var> bytes are read.
+         * Returns number of bytes read into array or -1 if
+         * end of stream is encountered.
+         *
+         * @param dest array to hold values
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @return bytes read into array or -1 if end of stream is encountered.
+         * @since 1.3
+         */
+        public int read( byte[] dest, int off, int len ) throws java.io.IOException
+        {
+            int i;
+            int b;
+            for( i = 0; i < len; i++ )
+            {
+                b = read();
+                
+                //if( b < 0 && i == 0 )
+                //    return -1;
+                
+                if( b >= 0 )
+                    dest[off + i] = (byte)b;
+                else if( i == 0 )
+                    return -1;
+                else
+                    break; // Out of 'for' loop
+            }   // end for: each byte read
+            return i;
+        }   // end read
+        
+    }   // end inner class InputStream
+    
+    
+    
+    
+    
+    
+    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
+    
+    
+    
+    /**
+     * A {@link Base64.OutputStream} will write data to another
+     * <tt>java.io.OutputStream</tt>, given in the constructor,
+     * and encode/decode to/from Base64 notation on the fly.
+     *
+     * @see Base64
+     * @since 1.3
+     */
+    public static class OutputStream extends java.io.FilterOutputStream
+    {
+        private boolean encode;
+        private int     position;
+        private byte[]  buffer;
+        private int     bufferLength;
+        private int     lineLength;
+        private boolean breakLines;
+        private byte[]  b4; // Scratch used in a few places
+        private boolean suspendEncoding;
+		private int options; // Record for later
+		private byte[]  alphabet;	    // Local copies to avoid extra method calls
+		private byte[]  decodabet;		// Local copies to avoid extra method calls
+        
+        /**
+         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
+         *
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out )
+        {   
+            this( out, ENCODE );
+        }   // end constructor
+        
+        
+        /**
+         * Constructs a {@link Base64.OutputStream} in
+         * either ENCODE or DECODE mode.
+         * <p>
+         * Valid options:<pre>
+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+         * </pre>
+         * <p>
+         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
+         *
+         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
+         * @param options Specified options.
+         * @see Base64#ENCODE
+         * @see Base64#DECODE
+         * @see Base64#DONT_BREAK_LINES
+         * @since 1.3
+         */
+        public OutputStream( java.io.OutputStream out, int options )
+        {   
+            super( out );
+            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
+            this.encode       = (options & ENCODE) == ENCODE;
+            this.bufferLength = encode ? 3 : 4;
+            this.buffer       = new byte[ bufferLength ];
+            this.position     = 0;
+            this.lineLength   = 0;
+            this.suspendEncoding = false;
+            this.b4           = new byte[4];
+			this.options      = options;
+			this.alphabet     = getAlphabet(options);
+			this.decodabet    = getDecodabet(options);
+        }   // end constructor
+        
+        
+        /**
+         * Writes the byte to the output stream after
+         * converting to/from Base64 notation.
+         * When encoding, bytes are buffered three
+         * at a time before the output stream actually
+         * gets a write() call.
+         * When decoding, bytes are buffered four
+         * at a time.
+         *
+         * @param theByte the byte to write
+         * @since 1.3
+         */
+        public void write(int theByte) throws java.io.IOException
+        {
+            // Encoding suspended?
+            if( suspendEncoding )
+            {
+                super.out.write( theByte );
+                return;
+            }   // end if: supsended
+            
+            // Encode?
+            if( encode )
+            {
+                buffer[ position++ ] = (byte)theByte;
+                if( position >= bufferLength )  // Enough to encode.
+                {
+                    out.write( encode3to4( b4, buffer, bufferLength, options ) );
+
+                    lineLength += 4;
+                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
+                    {
+                        out.write( NEW_LINE );
+                        lineLength = 0;
+                    }   // end if: end of line
+
+                    position = 0;
+                }   // end if: enough to output
+            }   // end if: encoding
+
+            // Else, Decoding
+            else
+            {
+                // Meaningful Base64 character?
+                if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC )
+                {
+                    buffer[ position++ ] = (byte)theByte;
+                    if( position >= bufferLength )  // Enough to output.
+                    {
+                        int len = Base64.decode4to3( buffer, 0, b4, 0, options );
+                        out.write( b4, 0, len );
+                        //out.write( Base64.decode4to3( buffer ) );
+                        position = 0;
+                    }   // end if: enough to output
+                }   // end if: meaningful base64 character
+                else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC )
+                {
+                    throw new java.io.IOException( "Invalid character in Base64 data." );
+                }   // end else: not white space either
+            }   // end else: decoding
+        }   // end write
+        
+        
+        
+        /**
+         * Calls {@link #write(int)} repeatedly until <var>len</var> 
+         * bytes are written.
+         *
+         * @param theBytes array from which to read bytes
+         * @param off offset for array
+         * @param len max number of bytes to read into array
+         * @since 1.3
+         */
+        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
+        {
+            // Encoding suspended?
+            if( suspendEncoding )
+            {
+                super.out.write( theBytes, off, len );
+                return;
+            }   // end if: supsended
+            
+            for( int i = 0; i < len; i++ )
+            {
+                write( theBytes[ off + i ] );
+            }   // end for: each byte written
+            
+        }   // end write
+        
+        
+        
+        /**
+         * Method added by PHIL. [Thanks, PHIL. -Rob]
+         * This pads the buffer without closing the stream.
+         */
+        public void flushBase64() throws java.io.IOException 
+        {
+            if( position > 0 )
+            {
+                if( encode )
+                {
+                    out.write( encode3to4( b4, buffer, position, options ) );
+                    position = 0;
+                }   // end if: encoding
+                else
+                {
+                    throw new java.io.IOException( "Base64 input not properly padded." );
+                }   // end else: decoding
+            }   // end if: buffer partially full
+
+        }   // end flush
+
+        
+        /** 
+         * Flushes and closes (I think, in the superclass) the stream. 
+         *
+         * @since 1.3
+         */
+        public void close() throws java.io.IOException
+        {
+            // 1. Ensure that pending characters are written
+            flushBase64();
+
+            // 2. Actually close the stream
+            // Base class both flushes and closes.
+            super.close();
+            
+            buffer = null;
+            out    = null;
+        }   // end close
+        
+        
+        
+        /**
+         * Suspends encoding of the stream.
+         * May be helpful if you need to embed a piece of
+         * base640-encoded data in a stream.
+         *
+         * @since 1.5.1
+         */
+        public void suspendEncoding() throws java.io.IOException 
+        {
+            flushBase64();
+            this.suspendEncoding = true;
+        }   // end suspendEncoding
+        
+        
+        /**
+         * Resumes encoding of the stream.
+         * May be helpful if you need to embed a piece of
+         * base640-encoded data in a stream.
+         *
+         * @since 1.5.1
+         */
+        public void resumeEncoding()
+        {
+            this.suspendEncoding = false;
+        }   // end resumeEncoding
+        
+        
+        
+    }   // end inner class OutputStream
+    
+    
+}   // end class Base64

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Comment.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Comment.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Comment.java
new file mode 100644
index 0000000..c5d9101
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/Comment.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment.model;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.jdom.Document;
+import org.jdom.Element;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class Comment extends Resource {
+  private User user;
+  private String comment;
+  private int typeOfCommentedResource;
+  private String uriOfCommetedResource;
+
+  public Comment() {
+	super();
+	this.setItemType(Resource.COMMENT);
+  }
+
+  public User getUser() {
+	return (this.user);
+  }
+
+  public void setUser(User user) {
+	this.user = user;
+  }
+
+  public String getComment() {
+	return (this.comment);
+  }
+
+  public void setComment(String comment) {
+	this.comment = comment;
+  }
+
+  public int getTypeOfCommentedResource() {
+	return (this.typeOfCommentedResource);
+  }
+
+  public void setTypeOfCommentedResource(int typeOfCommentedResource) {
+	this.typeOfCommentedResource = typeOfCommentedResource;
+  }
+
+  public String getURIOfCommentedResource() {
+	return (this.uriOfCommetedResource);
+  }
+
+  public void setURIOfCommentedResource(String uriOfCommetedResource) {
+	this.uriOfCommetedResource = uriOfCommetedResource;
+  }
+
+  /**
+   * A helper method to return a set of API elements that are needed to satisfy
+   * request of a particular type - e.g. creating a listing of resources or
+   * populating full preview, etc.
+   * 
+   * @param iRequestType
+   *          A constant value from Resource class.
+   * @return Comma-separated string containing values of required API elements.
+   */
+  public static String getRequiredAPIElements(int iRequestType) {
+	String strElements = "";
+
+	// cases higher up in the list are supersets of those that come below -
+	// hence no "break" statements are required, because 'falling through' the
+	// switch statement is the desired behaviour in this case
+	switch (iRequestType) {
+	  case Resource.REQUEST_DEFAULT_FROM_API:
+		strElements += ""; // no change needed - defaults will be used
+	}
+
+	return (strElements);
+  }
+
+  //class method to build a comment instance from XML
+  public static Comment buildFromXML(Document doc, Logger logger) {
+	// if no XML was supplied, return null to indicate an error
+	if (doc == null)
+	  return (null);
+
+	Comment c = new Comment();
+
+	try {
+	  Element root = doc.getRootElement();
+
+	  c.setResource(root.getAttributeValue("resource"));
+	  c.setURI(root.getAttributeValue("uri"));
+
+	  c.setTitle(root.getChildText("comment"));
+	  c.setComment(root.getChildText("comment"));
+
+	  Element commentedResourceElement = root.getChild("subject");
+	  if (commentedResourceElement != null) {
+		c.setTypeOfCommentedResource(Resource.getResourceTypeFromVisibleName(commentedResourceElement.getName()));
+		c.setURIOfCommentedResource(commentedResourceElement.getAttributeValue("uri"));
+	  }
+
+	  Element userElement = root.getChild("author");
+	  c.setUser(Util.instantiatePrimitiveUserFromElement(userElement));
+
+	  String createdAt = root.getChildText("created-at");
+	  if (createdAt != null && !createdAt.equals("")) {
+		c.setCreatedAt(MyExperimentClient.parseDate(createdAt));
+	  }
+
+	  logger.debug("Found information for comment with ID: " + c.getID()
+		  + ", URI: " + c.getURI());
+	} catch (Exception e) {
+	  logger.error("Failed midway through creating comment object from XML", e);
+	}
+
+	// return created comment instance
+	return (c);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/File.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/File.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/File.java
new file mode 100644
index 0000000..83853ee
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/model/File.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment.model;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.jdom.Document;
+import org.jdom.Element;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class File extends Resource {
+  private int accessType;
+
+  private User uploader;
+  private License license;
+  private String filename;
+  private String visibleType;
+  private String contentType;
+  private List<Tag> tags;
+  private List<Comment> comments;
+  private List<Resource> credits;
+  private List<Resource> attributions;
+
+  public File() {
+	super();
+	this.setItemType(Resource.FILE);
+  }
+
+  public int getAccessType() {
+	return this.accessType;
+  }
+
+  public void setAccessType(int accessType) {
+	this.accessType = accessType;
+  }
+
+  public List<Tag> getTags() {
+	return tags;
+  }
+
+  public User getUploader() {
+	return uploader;
+  }
+
+  public void setUploader(User uploader) {
+	this.uploader = uploader;
+  }
+
+  public License getLicense() {
+	return license;
+  }
+
+  public void setLicense(License license) {
+	this.license = license;
+  }
+
+  public String getFilename() {
+	return this.filename;
+  }
+
+  public void setFilename(String filename) {
+	this.filename = filename;
+  }
+
+  public String getContentType() {
+	return contentType;
+  }
+
+  public void setContentType(String contentType) {
+	this.contentType = contentType;
+  }
+
+  public String getVisibleType() {
+	return this.visibleType;
+  }
+
+  public void setVisibleType(String visibleType) {
+	this.visibleType = visibleType;
+  }
+
+  public List<Comment> getComments() {
+	return this.comments;
+  }
+
+  public List<Resource> getCredits() {
+	return this.credits;
+  }
+
+  public List<Resource> getAttributions() {
+	return this.attributions;
+  }
+
+  /**
+   * A helper method to return a set of API elements that are needed to satisfy
+   * request of a particular type - e.g. creating a listing of resources or
+   * populating full preview, etc.
+   * 
+   * @param iRequestType
+   *          A constant value from Resource class.
+   * @return Comma-separated string containing values of required API elements.
+   */
+  public static String getRequiredAPIElements(int iRequestType) {
+	String strElements = "";
+
+	// cases higher up in the list are supersets of those that come below -
+	// hence no "break" statements are required, because 'falling through' the
+	// switch statement is the desired behaviour in this case
+	switch (iRequestType) {
+	  case Resource.REQUEST_FULL_PREVIEW:
+		strElements += "filename,content-type,created-at,updated-at,"
+			+ "license-type,tags,comments,credits,attributions,";
+	  case Resource.REQUEST_FULL_LISTING:
+		strElements += "uploader,type,";
+	  case Resource.REQUEST_SHORT_LISTING:
+		strElements += "id,title,description,privileges";
+	}
+
+	return (strElements);
+  }
+
+  public static File buildFromXML(Document doc, Logger logger) {
+	// if no XML document was supplied, return NULL
+	if (doc == null)
+	  return (null);
+
+	// call main method which parses XML document starting from root element
+	return (File.buildFromXML(doc.getRootElement(), logger));
+  }
+
+  //class method to build a file instance from XML
+  public static File buildFromXML(Element docRootElement, Logger logger) {
+	// return null to indicate an error if XML document contains no root element
+	if (docRootElement == null)
+	  return (null);
+
+	File f = new File();
+
+	try {
+	  // Access type
+	  f.setAccessType(Util.getAccessTypeFromXMLElement(docRootElement.getChild("privileges")));
+
+	  // URI
+	  f.setURI(docRootElement.getAttributeValue("uri"));
+
+	  // Resource URI
+	  f.setResource(docRootElement.getAttributeValue("resource"));
+
+	  // Id
+	  String id = docRootElement.getChildText("id");
+	  if (id == null || id.equals("")) {
+		id = "API Error - No file ID supplied";
+		logger.error("Error while parsing file XML data - no ID provided for file with title: \""
+			+ docRootElement.getChildText("title") + "\"");
+	  }
+	  f.setID(id);
+
+	  // Filename
+	  f.setFilename(docRootElement.getChildText("filename"));
+
+	  // Title
+	  f.setTitle(docRootElement.getChildText("title"));
+
+	  // Description
+	  f.setDescription(docRootElement.getChildText("description"));
+
+	  // Uploader
+	  Element uploaderElement = docRootElement.getChild("uploader");
+	  f.setUploader(Util.instantiatePrimitiveUserFromElement(uploaderElement));
+
+	  // Created at
+	  String createdAt = docRootElement.getChildText("created-at");
+	  if (createdAt != null && !createdAt.equals("")) {
+		f.setCreatedAt(MyExperimentClient.parseDate(createdAt));
+	  }
+
+	  // Updated at
+	  String updatedAt = docRootElement.getChildText("updated-at");
+	  if (updatedAt != null && !updatedAt.equals("")) {
+		f.setUpdatedAt(MyExperimentClient.parseDate(updatedAt));
+	  }
+
+	  // License
+	  f.setLicense(License.getInstance(docRootElement.getChildText("license-type")));
+
+	  // Type and Content-Type
+	  f.setVisibleType(docRootElement.getChildText("type"));
+	  f.setContentType(docRootElement.getChildText("content-type"));
+
+	  // Tags
+	  f.tags = new ArrayList<Tag>();
+	  f.getTags().addAll(Util.retrieveTags(docRootElement));
+
+	  // Comments
+	  f.comments = new ArrayList<Comment>();
+	  f.getComments().addAll(Util.retrieveComments(docRootElement, f));
+
+	  // Credits
+	  f.credits = new ArrayList<Resource>();
+	  f.getCredits().addAll(Util.retrieveCredits(docRootElement));
+
+	  // Attributions
+	  f.attributions = new ArrayList<Resource>();
+	  f.getAttributions().addAll(Util.retrieveAttributions(docRootElement));
+
+	  logger.debug("Found information for file with ID: " + f.getID()
+		  + ", Title: " + f.getTitle());
+	} catch (Exception e) {
+	  logger.error("Failed midway through creating file object from XML", e);
+	}
+
+	// return created file instance
+	return (f);
+  }
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowConfigureMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowConfigureMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowConfigureMenuAction.java
new file mode 100644
index 0000000..49c07a2
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowConfigureMenuAction.java
@@ -0,0 +1,165 @@
+/**********************************************************************
+ * 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.ui.menu.items.contextualviews;
+
+import java.awt.KeyboardFocusManager;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.KeyStroke;
+import javax.swing.text.JTextComponent;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.ui.menu.DesignOnlyAction;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.design.actions.EditDataflowInputPortAction;
+import net.sf.taverna.t2.workbench.design.actions.EditDataflowOutputPortAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.merge.MergeConfigurationView;
+import net.sf.taverna.t2.workbench.ui.workflowview.WorkflowView;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+
+public class ShowConfigureMenuAction extends AbstractMenuAction {
+
+	private static Logger logger = Logger.getLogger(ShowConfigureMenuAction.class);
+
+	public static final URI GRAPH_DETAILS_MENU_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphDetailsMenuSection");
+
+	private static final URI SHOW_CONFIGURE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuShowConfigureComponent");
+
+	private EditManager editManager;
+
+	private SelectionManager selectionManager;
+
+	private MenuManager menuManager;
+
+	private Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	public ShowConfigureMenuAction() {
+		super(GRAPH_DETAILS_MENU_SECTION, 20, SHOW_CONFIGURE_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new ShowConfigureAction();
+	}
+
+	@SuppressWarnings("serial")
+	protected class ShowConfigureAction extends AbstractAction implements DesignOnlyAction {
+
+		private boolean enabled;
+
+		ShowConfigureAction() {
+			super();
+			putValue(NAME, "Configure");
+			putValue(SHORT_DESCRIPTION, "Configure selected component");
+			putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false));
+
+			KeyboardFocusManager focusManager = KeyboardFocusManager
+					.getCurrentKeyboardFocusManager();
+			focusManager.addPropertyChangeListener(new PropertyChangeListener() {
+				public void propertyChange(PropertyChangeEvent e) {
+					String prop = e.getPropertyName();
+					if ("focusOwner".equals(prop)) {
+						if (e.getNewValue() instanceof JTextComponent) {
+							ShowConfigureAction.super.setEnabled(false);
+						} else {
+							ShowConfigureAction.this.setEnabled(enabled);
+						}
+					}
+				}
+			});
+		}
+
+		@Override
+		public void setEnabled(boolean enabled) {
+			this.enabled = enabled;
+			super.setEnabled(enabled);
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			WorkflowBundle workflowBundle = selectionManager.getSelectedWorkflowBundle();
+			DataflowSelectionModel dataFlowSelectionModel = selectionManager
+					.getDataflowSelectionModel(workflowBundle);
+			// Get selected port
+			Set<Object> selectedWFComponents = dataFlowSelectionModel.getSelection();
+			if (selectedWFComponents.size() > 0) {
+				Object component = selectedWFComponents.iterator().next();
+				if (component instanceof Processor) {
+					Action action = WorkflowView.getConfigureAction((Processor) component,
+							menuManager);
+					if (action != null) {
+						action.actionPerformed(e);
+					}
+				} else if (component instanceof DataLink) {
+					DataLink dataLink = (DataLink) component;
+					if (dataLink.getMergePosition() != null) {
+						List<DataLink> datalinks = scufl2Tools.datalinksTo(dataLink.getSendsTo());
+						MergeConfigurationView mergeConfigurationView = new MergeConfigurationView(
+								datalinks, editManager, selectionManager);
+						mergeConfigurationView.setLocationRelativeTo(null);
+						mergeConfigurationView.setVisible(true);
+					}
+				} else if (component instanceof InputWorkflowPort) {
+					InputWorkflowPort port = (InputWorkflowPort) component;
+					new EditDataflowInputPortAction(port.getParent(), port, null, editManager,
+							selectionManager).actionPerformed(e);
+				} else if (component instanceof OutputWorkflowPort) {
+					OutputWorkflowPort port = (OutputWorkflowPort) component;
+					new EditDataflowOutputPortAction(port.getParent(), port, null, editManager,
+							selectionManager).actionPerformed(e);
+				}
+			}
+		}
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setMenuManager(MenuManager menuManager) {
+		this.menuManager = menuManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsContextualMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsContextualMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsContextualMenuAction.java
new file mode 100644
index 0000000..27a47de
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsContextualMenuAction.java
@@ -0,0 +1,65 @@
+/**********************************************************************
+ * 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.ui.menu.items.contextualviews;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.ui.Workbench;
+
+import org.apache.log4j.Logger;
+
+public class ShowDetailsContextualMenuAction extends AbstractContextualMenuAction {
+	private static final String SHOW_DETAILS = "Show details";
+	private String namedComponent = "contextualView";
+
+	private static Logger logger = Logger.getLogger(ShowDetailsContextualMenuAction.class);
+	private Workbench workbench;
+
+	public ShowDetailsContextualMenuAction() {
+		super(ConfigureSection.configureSection, 40);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled();
+		// FIXME: Should we list all the applicable types here?
+		// && getContextualSelection().getSelection() instanceof Processor;
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		return new AbstractAction(SHOW_DETAILS) {
+			public void actionPerformed(ActionEvent e) {
+				workbench.makeNamedComponentVisible(namedComponent);
+			}
+		};
+	}
+
+	public void setWorkbench(Workbench workbench) {
+		this.workbench = workbench;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsMenuAction.java
new file mode 100644
index 0000000..ae9fee3
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsMenuAction.java
@@ -0,0 +1,81 @@
+/**********************************************************************
+ * 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.ui.menu.items.contextualviews;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.KeyStroke;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.ui.menu.DesignOnlyAction;
+import net.sf.taverna.t2.workbench.ui.Workbench;
+
+public class ShowDetailsMenuAction extends AbstractMenuAction {
+	private static final URI SHOW_DETAILS_URI = URI
+	.create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuShowDetailsComponent");
+
+	private static final String SHOW_DETAILS = "Details";
+	private String namedComponent = "contextualView";
+
+	private Workbench workbench;
+
+ 	public ShowDetailsMenuAction() {
+		super(ShowConfigureMenuAction.GRAPH_DETAILS_MENU_SECTION, 20, SHOW_DETAILS_URI);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled();
+		// FIXME: Should we list all the applicable types here?
+		// && getContextualSelection().getSelection() instanceof Processor;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new ShowDetailsAction();
+	}
+
+	protected class ShowDetailsAction extends AbstractAction implements DesignOnlyAction {
+
+		ShowDetailsAction() {
+			super();
+			putValue(NAME, "Show details");
+			putValue(SHORT_DESCRIPTION, "Show details of selected component");
+			putValue(Action.ACCELERATOR_KEY,
+					KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK));
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			workbench.makeNamedComponentVisible(namedComponent);
+		}
+
+	}
+
+	public void setWorkbench(Workbench workbench) {
+		this.workbench = workbench;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowReportsContextualMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowReportsContextualMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowReportsContextualMenuAction.java
new file mode 100644
index 0000000..c9d7279
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowReportsContextualMenuAction.java
@@ -0,0 +1,103 @@
+/**********************************************************************
+ * 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.ui.menu.items.contextualviews;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+
+import net.sf.taverna.t2.lang.ui.icons.Icons;
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.report.ReportManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.Workbench;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.validation.Status;
+
+public class ShowReportsContextualMenuAction extends AbstractContextualMenuAction {
+
+	private static final String SHOW_REPORTS = "Show validation report";
+	private String namedComponent = "reportView";
+	private ReportManager reportManager;
+	private Workbench workbench;
+	private SelectionManager selectionManager;
+
+	@SuppressWarnings("unused")
+	private static Logger logger = Logger.getLogger(ShowReportsContextualMenuAction.class);
+
+	public ShowReportsContextualMenuAction() {
+		/** Right below ShowDetailsContextualMenuAction
+		 */
+		super(ConfigureSection.configureSection, 41);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled();
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		WorkflowBundle parent;
+		if (getContextualSelection().getParent() instanceof Workflow) {
+			parent = ((Workflow)getContextualSelection().getParent()).getParent();
+		} else {
+			parent = selectionManager.getSelectedWorkflowBundle();
+		}
+		Status status = Status.OK;
+		if (reportManager != null) {
+//			status = reportManager.getStatus(parent.getMainProfile(), (WorkflowBean) getContextualSelection().getSelection());
+		}
+
+		Icon icon = null;
+		if (status == Status.WARNING) {
+			icon = Icons.warningIcon;
+		} else if (status == Status.SEVERE) {
+			icon = Icons.severeIcon;
+		}
+
+		return new AbstractAction(SHOW_REPORTS, icon) {
+			public void actionPerformed(ActionEvent e) {
+				workbench.makeNamedComponentVisible(namedComponent);
+			}
+		};
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setReportManager(ReportManager reportManager) {
+		this.reportManager = reportManager;
+	}
+
+	public void setWorkbench(Workbench workbench) {
+		this.workbench = workbench;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/ConditionSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/ConditionSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/ConditionSection.java
new file mode 100644
index 0000000..3f67def
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/ConditionSection.java
@@ -0,0 +1,71 @@
+/**********************************************************************
+ * 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.ui.menu.items.controllink;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import uk.org.taverna.scufl2.api.core.BlockingControlLink;
+import uk.org.taverna.scufl2.api.core.ControlLink;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DefaultContextualMenu;
+
+public class ConditionSection extends AbstractMenuSection implements
+		ContextualMenuComponent {
+
+	private static final String CONTROL_LINK = "Control link: ";
+	public static final URI conditionSection = URI
+			.create("http://taverna.sf.net/2009/contextMenu/condition");
+	private ContextualSelection contextualSelection;
+
+	public ConditionSection() {
+		super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, conditionSection);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof BlockingControlLink;
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+		this.action = null;
+	}
+
+	@Override
+	protected Action createAction() {
+		BlockingControlLink controllink = (BlockingControlLink) getContextualSelection()
+				.getSelection();
+		String name = CONTROL_LINK + controllink.getBlock().getName()
+				+ " RUNS_AFTER " + controllink.getUntilFinished().getName();
+		return new DummyAction(name);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/RemoveConditionMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/RemoveConditionMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/RemoveConditionMenuAction.java
new file mode 100644
index 0000000..68e32e2
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/RemoveConditionMenuAction.java
@@ -0,0 +1,67 @@
+/**********************************************************************
+ * 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.ui.menu.items.controllink;
+
+import java.awt.Component;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.design.actions.RemoveConditionAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.ControlLink;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public class RemoveConditionMenuAction extends AbstractContextualMenuAction {
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public RemoveConditionMenuAction() {
+		super(ConditionSection.conditionSection, 10);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof ControlLink
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+	@Override
+	protected Action createAction() {
+		Workflow dataflow = (Workflow) getContextualSelection().getParent();
+		ControlLink controlLink = (ControlLink) getContextualSelection()
+				.getSelection();
+		Component component = getContextualSelection().getRelativeToComponent();
+		return new RemoveConditionAction(dataflow, controlLink, component, editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/LinkSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/LinkSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/LinkSection.java
new file mode 100644
index 0000000..aee08ea
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/LinkSection.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.ui.menu.items.datalink;
+
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import uk.org.taverna.scufl2.api.core.DataLink;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DefaultContextualMenu;
+
+public class LinkSection extends AbstractMenuSection implements
+		ContextualMenuComponent {
+
+	public static final URI linkSection = URI
+			.create("http://taverna.sf.net/2009/contextMenu/link");
+	private ContextualSelection contextualSelection;
+
+	public LinkSection() {
+		super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, linkSection);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof DataLink;
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+		this.action = null;
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		DataLink link = (DataLink) getContextualSelection().getSelection();
+		String name = "Data link: " + link.getReceivesFrom().getName() + " -> " + link.getSendsTo().getName();
+		return new AbstractAction(name) {
+			public void actionPerformed(ActionEvent e) {
+			}
+		};
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/RemoveLinkMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/RemoveLinkMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/RemoveLinkMenuAction.java
new file mode 100644
index 0000000..c672f99
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/RemoveLinkMenuAction.java
@@ -0,0 +1,66 @@
+/**********************************************************************
+ * 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.ui.menu.items.datalink;
+
+import java.awt.Component;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.design.actions.RemoveDatalinkAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public class RemoveLinkMenuAction extends AbstractContextualMenuAction {
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public RemoveLinkMenuAction() {
+		super(LinkSection.linkSection, 10);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof DataLink
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+	@Override
+	protected Action createAction() {
+		Workflow workflow = (Workflow) getContextualSelection().getParent();
+		DataLink datalink = (DataLink) getContextualSelection().getSelection();
+		Component component = getContextualSelection().getRelativeToComponent();
+		return new RemoveDatalinkAction(workflow, datalink, component, editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowInputPortMenuActions.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowInputPortMenuActions.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowInputPortMenuActions.java
new file mode 100644
index 0000000..15e8424
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowInputPortMenuActions.java
@@ -0,0 +1,42 @@
+/**********************************************************************
+ * 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.ui.menu.items.ports;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.items.activityport.AbstractConnectPortMenuActions;
+
+public class ConnectDataflowInputPortMenuActions extends
+		AbstractConnectPortMenuActions implements ContextualMenuComponent {
+
+	public ConnectDataflowInputPortMenuActions() {
+		super(WorkflowInputPortSection.inputPort, 20);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof InputWorkflowPort
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowOutputPortMenuActions.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowOutputPortMenuActions.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowOutputPortMenuActions.java
new file mode 100644
index 0000000..d99a361
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowOutputPortMenuActions.java
@@ -0,0 +1,42 @@
+/**********************************************************************
+ * 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.ui.menu.items.ports;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.items.activityport.AbstractConnectPortMenuActions;
+
+public class ConnectDataflowOutputPortMenuActions extends
+		AbstractConnectPortMenuActions implements ContextualMenuComponent {
+
+	public ConnectDataflowOutputPortMenuActions() {
+		super(WorkflowOutputPortSection.outputPort, 20);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof OutputWorkflowPort
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowInputPortMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowInputPortMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowInputPortMenuAction.java
new file mode 100644
index 0000000..77c25f5
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowInputPortMenuAction.java
@@ -0,0 +1,68 @@
+/**********************************************************************
+ * 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.ui.menu.items.ports;
+
+import java.awt.Component;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.design.actions.EditDataflowInputPortAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+
+public class EditDataflowInputPortMenuAction extends
+		AbstractContextualMenuAction {
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public EditDataflowInputPortMenuAction() {
+		super(WorkflowInputPortSection.inputPort, 10);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof InputWorkflowPort
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+	@Override
+	protected Action createAction() {
+		Workflow workflow = (Workflow) getContextualSelection().getParent();
+		InputWorkflowPort inport = (InputWorkflowPort) getContextualSelection()
+				.getSelection();
+		Component component = getContextualSelection().getRelativeToComponent();
+		return new EditDataflowInputPortAction(workflow, inport, component, editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowOutputPortMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowOutputPortMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowOutputPortMenuAction.java
new file mode 100644
index 0000000..0f406dd
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowOutputPortMenuAction.java
@@ -0,0 +1,68 @@
+/**********************************************************************
+ * 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.ui.menu.items.ports;
+
+import java.awt.Component;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.design.actions.EditDataflowOutputPortAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+
+public class EditDataflowOutputPortMenuAction extends
+		AbstractContextualMenuAction {
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public EditDataflowOutputPortMenuAction() {
+		super(WorkflowOutputPortSection.outputPort, 10);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof OutputWorkflowPort
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+	@Override
+	protected Action createAction() {
+		Workflow workflow = (Workflow) getContextualSelection().getParent();
+		OutputWorkflowPort outport = (OutputWorkflowPort) getContextualSelection()
+				.getSelection();
+		Component component = getContextualSelection().getRelativeToComponent();
+		return new EditDataflowOutputPortAction(workflow, outport, component, editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowInputPortMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowInputPortMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowInputPortMenuAction.java
new file mode 100644
index 0000000..f5e2fc1
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowInputPortMenuAction.java
@@ -0,0 +1,68 @@
+/**********************************************************************
+ * 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.ui.menu.items.ports;
+
+import java.awt.Component;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.design.actions.RemoveDataflowInputPortAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+
+public class RemoveDataflowInputPortMenuAction extends
+		AbstractContextualMenuAction {
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public RemoveDataflowInputPortMenuAction() {
+		super(WorkflowInputPortSection.inputPort, 10);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof InputWorkflowPort
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+	@Override
+	protected Action createAction() {
+		Workflow workflow = (Workflow) getContextualSelection().getParent();
+		InputWorkflowPort inport = (InputWorkflowPort) getContextualSelection()
+				.getSelection();
+		Component component = getContextualSelection().getRelativeToComponent();
+		return new RemoveDataflowInputPortAction(workflow, inport, component, editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowOutputPortMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowOutputPortMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowOutputPortMenuAction.java
new file mode 100644
index 0000000..da775a5
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowOutputPortMenuAction.java
@@ -0,0 +1,68 @@
+/**********************************************************************
+ * 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.ui.menu.items.ports;
+
+import java.awt.Component;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.design.actions.RemoveDataflowOutputPortAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+
+public class RemoveDataflowOutputPortMenuAction extends
+		AbstractContextualMenuAction {
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public RemoveDataflowOutputPortMenuAction() {
+		super(WorkflowOutputPortSection.outputPort, 10);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof OutputWorkflowPort
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+	@Override
+	protected Action createAction() {
+		Workflow workflow = (Workflow) getContextualSelection().getParent();
+		OutputWorkflowPort outport = (OutputWorkflowPort) getContextualSelection()
+				.getSelection();
+		Component component = getContextualSelection().getRelativeToComponent();
+		return new RemoveDataflowOutputPortAction(workflow, outport, component, editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowInputPortSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowInputPortSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowInputPortSection.java
new file mode 100644
index 0000000..1a0d8ef
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowInputPortSection.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.ui.menu.items.ports;
+
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DefaultContextualMenu;
+
+public class WorkflowInputPortSection extends AbstractMenuSection implements
+		ContextualMenuComponent {
+
+	public static final URI inputPort = URI
+			.create("http://taverna.sf.net/2009/contextMenu/inputPort");
+	private ContextualSelection contextualSelection;
+
+	public WorkflowInputPortSection() {
+		super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, inputPort);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof InputWorkflowPort;
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+		this.action = null;
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		InputWorkflowPort proc = (InputWorkflowPort) getContextualSelection().getSelection();
+		String name = "Workflow input port: " + proc.getName();
+		return new AbstractAction(name) {
+			public void actionPerformed(ActionEvent e) {
+			}
+		};
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowOutputPortSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowOutputPortSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowOutputPortSection.java
new file mode 100644
index 0000000..e387f9e
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowOutputPortSection.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.ui.menu.items.ports;
+
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DefaultContextualMenu;
+
+public class WorkflowOutputPortSection extends AbstractMenuSection implements
+		ContextualMenuComponent {
+
+	public static final URI outputPort = URI
+			.create("http://taverna.sf.net/2009/contextMenu/outputPort");
+	private ContextualSelection contextualSelection;
+
+	public WorkflowOutputPortSection() {
+		super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, outputPort);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof OutputWorkflowPort;
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+		this.action = null;
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		OutputWorkflowPort proc = (OutputWorkflowPort) getContextualSelection().getSelection();
+		String name = "Workflow output port: " + proc.getName();
+		return new AbstractAction(name) {
+			public void actionPerformed(ActionEvent e) {
+			}
+		};
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ConditionMenuActions.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ConditionMenuActions.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ConditionMenuActions.java
new file mode 100644
index 0000000..a3e569b
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ConditionMenuActions.java
@@ -0,0 +1,118 @@
+/**********************************************************************
+ * 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.ui.menu.items.processor;
+
+import java.awt.Component;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.menu.AbstractMenuCustom;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureSection;
+import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager;
+import net.sf.taverna.t2.workbench.design.actions.AddConditionAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public class ConditionMenuActions extends AbstractMenuCustom implements
+		ContextualMenuComponent {
+
+	private ContextualSelection contextualSelection;
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+	private ActivityIconManager activityIconManager;
+	private Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	public ConditionMenuActions() {
+		super(ConfigureSection.configureSection, 80 );
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof Processor
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+		this.customComponent = null;
+	}
+
+	@Override
+	protected Component createCustomComponent() {
+
+		Workflow workflow = (Workflow) getContextualSelection().getParent();
+		Processor processor = (Processor) getContextualSelection()
+				.getSelection();
+		Component component = getContextualSelection().getRelativeToComponent();
+
+		List<AddConditionAction> conditions = getAddConditionActions(workflow,
+				processor, component);
+		if (conditions.isEmpty()) {
+			return null;
+		}
+		JMenu conditionMenu = new JMenu("Run after");
+		conditionMenu.setIcon(WorkbenchIcons.controlLinkIcon);
+		conditionMenu.add(new ShadedLabel("Services:", ShadedLabel.ORANGE));
+		conditionMenu.addSeparator();
+		for (AddConditionAction addConditionAction : conditions) {
+			conditionMenu.add(new JMenuItem(addConditionAction));
+		}
+		return conditionMenu;
+	}
+
+	protected List<AddConditionAction> getAddConditionActions(
+			Workflow workflow, Processor targetProcessor, Component component) {
+		List<AddConditionAction> actions = new ArrayList<AddConditionAction>();
+		for (Processor processor : scufl2Tools.possibleUpStreamProcessors(workflow, targetProcessor)) {
+			actions.add(new AddConditionAction(workflow, processor,
+					targetProcessor, component, editManager, selectionManager, activityIconManager));
+		}
+		return actions;
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setActivityIconManager(ActivityIconManager activityIconManager) {
+		this.activityIconManager = activityIconManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ProcessorSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ProcessorSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ProcessorSection.java
new file mode 100644
index 0000000..b2bde61
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ProcessorSection.java
@@ -0,0 +1,58 @@
+/**********************************************************************
+ * 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.ui.menu.items.processor;
+
+import java.net.URI;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.items.contextualviews.EditSection;
+
+public class ProcessorSection extends AbstractMenuSection implements
+		ContextualMenuComponent {
+
+	public static final URI processorSection = URI
+			.create("http://taverna.sf.net/2009/contextMenu/processor");
+	private ContextualSelection contextualSelection;
+
+	public ProcessorSection() {
+		super(EditSection.editSection, 200, processorSection);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof Processor;
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+		this.action = null;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RemoveProcessorMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RemoveProcessorMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RemoveProcessorMenuAction.java
new file mode 100644
index 0000000..e9c8fb4
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RemoveProcessorMenuAction.java
@@ -0,0 +1,67 @@
+/**********************************************************************
+ * 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.ui.menu.items.processor;
+
+import java.awt.Component;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.workbench.design.actions.RemoveProcessorAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public class RemoveProcessorMenuAction extends AbstractContextualMenuAction {
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public RemoveProcessorMenuAction() {
+		super(ProcessorSection.processorSection, 100);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof Processor
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+	@Override
+	protected Action createAction() {
+		Workflow workflow = (Workflow) getContextualSelection().getParent();
+		Processor processor = (Processor) getContextualSelection()
+				.getSelection();
+		Component component = getContextualSelection().getRelativeToComponent();
+		return new RemoveProcessorAction(workflow, processor, component, editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RenameProcessorMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RenameProcessorMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RenameProcessorMenuAction.java
new file mode 100644
index 0000000..a077726
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RenameProcessorMenuAction.java
@@ -0,0 +1,68 @@
+/**********************************************************************
+ * 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.ui.menu.items.processor;
+
+import java.awt.Component;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureSection;
+import net.sf.taverna.t2.workbench.design.actions.RenameProcessorAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public class RenameProcessorMenuAction extends AbstractContextualMenuAction {
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public RenameProcessorMenuAction() {
+		super(ConfigureSection.configureSection, 60);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof Processor
+				&& getContextualSelection().getParent() instanceof Workflow;
+	}
+
+	@Override
+	protected Action createAction() {
+		Workflow workflow = (Workflow) getContextualSelection().getParent();
+		Processor processor = (Processor) getContextualSelection()
+				.getSelection();
+		Component component = getContextualSelection().getRelativeToComponent();
+		return new RenameProcessorAction(workflow, processor, component, editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateInputMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateInputMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateInputMenuAction.java
new file mode 100644
index 0000000..6a3b880
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateInputMenuAction.java
@@ -0,0 +1,62 @@
+/**********************************************************************
+ * 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.ui.menu.items.workflow;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.ui.menu.items.contextualviews.InsertSection;
+import net.sf.taverna.t2.workbench.design.actions.AddDataflowInputAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public class CreateInputMenuAction extends AbstractContextualMenuAction {
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public CreateInputMenuAction() {
+		super(InsertSection.insertSection, 10);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof Workflow;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new AddDataflowInputAction((Workflow) getContextualSelection()
+				.getSelection(), getContextualSelection()
+				.getRelativeToComponent(), editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateOutputMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateOutputMenuAction.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateOutputMenuAction.java
new file mode 100644
index 0000000..226258c
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateOutputMenuAction.java
@@ -0,0 +1,62 @@
+/**********************************************************************
+ * 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.ui.menu.items.workflow;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction;
+import net.sf.taverna.t2.ui.menu.items.contextualviews.InsertSection;
+import net.sf.taverna.t2.workbench.design.actions.AddDataflowOutputAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+public class CreateOutputMenuAction extends AbstractContextualMenuAction {
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public CreateOutputMenuAction() {
+		super(InsertSection.insertSection, 20);
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof Workflow;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new AddDataflowOutputAction((Workflow) getContextualSelection()
+				.getSelection(), getContextualSelection()
+				.getRelativeToComponent(), editManager, selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/WorkflowServiceTemplatesSection.java
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/WorkflowServiceTemplatesSection.java b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/WorkflowServiceTemplatesSection.java
new file mode 100644
index 0000000..d461b5e
--- /dev/null
+++ b/taverna-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/WorkflowServiceTemplatesSection.java
@@ -0,0 +1,76 @@
+/**********************************************************************
+ * 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.ui.menu.items.workflow;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DefaultContextualMenu;
+
+/**
+ * Menu section containing the actions to add service templates, i.e. activities
+ * than are not readily runnable but need to be configured first. The actual actions that
+ * go into this menu can be found in the ui modules for the activities.
+ *
+ * @author Alex Nenadic
+ *
+ */
+public class WorkflowServiceTemplatesSection extends AbstractMenuSection
+		implements ContextualMenuComponent {
+
+	private static final String SERVICE_TEMPLATES = "Service templates";
+	public static final URI serviceTemplatesSection = URI
+			.create("http://taverna.sf.net/2009/contextMenu/serviceTemplates");
+	private ContextualSelection contextualSelection;
+
+	public WorkflowServiceTemplatesSection() {
+		super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 30, serviceTemplatesSection);
+	}
+
+	public ContextualSelection getContextualSelection() {
+		return contextualSelection;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return super.isEnabled()
+				&& getContextualSelection().getSelection() instanceof Workflow;
+	}
+
+	public void setContextualSelection(ContextualSelection contextualSelection) {
+		this.contextualSelection = contextualSelection;
+	}
+
+	@Override
+	protected Action createAction() {
+		DummyAction action = new DummyAction(SERVICE_TEMPLATES);
+		// Set the colour for the section
+		action.putValue(AbstractMenuSection.SECTION_COLOR, ShadedLabel.ORANGE);
+		return action;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-menu-items/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..47f3e0e
--- /dev/null
+++ b/taverna-menu-items/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,46 @@
+net.sf.taverna.t2.ui.menu.items.activityport.ActivityInputPortSection
+net.sf.taverna.t2.ui.menu.items.activityport.SetConstantInputPortValueMenuAction
+net.sf.taverna.t2.ui.menu.items.activityport.ConnectInputPortMenuActions
+
+net.sf.taverna.t2.ui.menu.items.activityport.ActivityOutputPortSection
+net.sf.taverna.t2.ui.menu.items.activityport.ConnectOutputPortMenuActions
+
+net.sf.taverna.t2.ui.menu.items.controllink.ConditionSection
+net.sf.taverna.t2.ui.menu.items.controllink.RemoveConditionMenuAction
+
+net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureRunningContextualMenuSection
+net.sf.taverna.t2.ui.menu.items.contextualviews.ShowDetailsMenuAction
+net.sf.taverna.t2.ui.menu.items.contextualviews.ShowDetailsContextualMenuAction
+net.sf.taverna.t2.ui.menu.items.contextualviews.ShowConfigureMenuAction
+net.sf.taverna.t2.ui.menu.items.contextualviews.ShowReportsContextualMenuAction
+
+net.sf.taverna.t2.ui.menu.items.workflow.CreateInputMenuAction
+net.sf.taverna.t2.ui.menu.items.workflow.CreateOutputMenuAction
+net.sf.taverna.t2.ui.menu.items.contextualviews.InsertSection
+net.sf.taverna.t2.ui.menu.items.workflow.WorkflowServiceTemplatesSection
+net.sf.taverna.t2.ui.menu.items.contextualviews.EditSection
+net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureSection
+net.sf.taverna.t2.ui.menu.items.contextualviews.PasteMenuAction
+
+net.sf.taverna.t2.ui.menu.items.datalink.LinkSection
+net.sf.taverna.t2.ui.menu.items.datalink.RemoveLinkMenuAction
+
+net.sf.taverna.t2.ui.menu.items.merge.MergeSection
+net.sf.taverna.t2.ui.menu.items.merge.RemoveMergeMenuAction
+
+net.sf.taverna.t2.ui.menu.items.ports.ConnectDataflowInputPortMenuActions
+net.sf.taverna.t2.ui.menu.items.ports.ConnectDataflowOutputPortMenuActions
+net.sf.taverna.t2.ui.menu.items.ports.EditDataflowInputPortMenuAction
+net.sf.taverna.t2.ui.menu.items.ports.EditDataflowOutputPortMenuAction
+net.sf.taverna.t2.ui.menu.items.ports.RemoveDataflowInputPortMenuAction
+net.sf.taverna.t2.ui.menu.items.ports.RemoveDataflowOutputPortMenuAction
+net.sf.taverna.t2.ui.menu.items.ports.WorkflowInputPortSection
+net.sf.taverna.t2.ui.menu.items.ports.WorkflowOutputPortSection
+
+net.sf.taverna.t2.ui.menu.items.processor.ConditionMenuActions
+net.sf.taverna.t2.ui.menu.items.processor.ProcessorSection
+net.sf.taverna.t2.ui.menu.items.processor.RenameProcessorMenuAction
+net.sf.taverna.t2.ui.menu.items.processor.RemoveProcessorMenuAction
+
+net.sf.taverna.t2.ui.menu.items.annotated.AnnotatedConfigureMenuAction
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-items/src/main/resources/META-INF/spring/menu-items-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-items/src/main/resources/META-INF/spring/menu-items-context-osgi.xml b/taverna-menu-items/src/main/resources/META-INF/spring/menu-items-context-osgi.xml
new file mode 100644
index 0000000..b7ba9e4
--- /dev/null
+++ b/taverna-menu-items/src/main/resources/META-INF/spring/menu-items-context-osgi.xml
@@ -0,0 +1,57 @@
+<?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="ActivityInputPortSection" auto-export="interfaces" />
+	<service ref="ActivityOutputPortSection" auto-export="interfaces" />
+	<service ref="AnnotatedConfigureMenuAction" auto-export="interfaces" />
+	<service ref="ConditionMenuActions" auto-export="interfaces" />
+	<service ref="ConditionSection" auto-export="interfaces" />
+	<service ref="ConfigureRunningContextualMenuSection" auto-export="interfaces" />
+	<service ref="ConfigureSection" auto-export="interfaces" />
+	<service ref="ConnectDataflowInputPortMenuActions" auto-export="interfaces" />
+	<service ref="ConnectDataflowOutputPortMenuActions" auto-export="interfaces" />
+	<service ref="ConnectInputPortMenuActions" auto-export="interfaces" />
+	<service ref="ConnectOutputPortMenuActions" auto-export="interfaces" />
+	<service ref="CreateInputMenuAction" auto-export="interfaces" />
+	<service ref="CreateOutputMenuAction" auto-export="interfaces" />
+	<service ref="EditDataflowInputPortMenuAction" auto-export="interfaces" />
+	<service ref="EditDataflowOutputPortMenuAction" auto-export="interfaces" />
+	<service ref="EditSection" auto-export="interfaces" />
+	<service ref="InsertSection" auto-export="interfaces" />
+	<service ref="LinkSection" auto-export="interfaces" />
+	<service ref="PasteMenuAction" auto-export="interfaces" />
+	<service ref="ProcessorSection" auto-export="interfaces" />
+	<service ref="RemoveConditionMenuAction" auto-export="interfaces" />
+	<service ref="RemoveDataflowInputPortMenuAction" auto-export="interfaces" />
+	<service ref="RemoveDataflowOutputPortMenuAction" auto-export="interfaces" />
+	<service ref="RemoveLinkMenuAction" auto-export="interfaces" />
+	<service ref="RemoveProcessorMenuAction" auto-export="interfaces" />
+	<service ref="RenameProcessorMenuAction" auto-export="interfaces" />
+	<service ref="SetConstantInputPortValueMenuAction" auto-export="interfaces" />
+	<service ref="ShowConfigureMenuAction" auto-export="interfaces" />
+	<service ref="ShowDetailsContextualMenuAction" auto-export="interfaces" />
+	<service ref="ShowDetailsMenuAction" auto-export="interfaces" />
+	<service ref="ShowReportsContextualMenuAction" auto-export="interfaces" />
+	<service ref="WorkflowInputPortSection" auto-export="interfaces" />
+	<service ref="WorkflowOutputPortSection" auto-export="interfaces" />
+	<service ref="WorkflowServiceTemplatesSection" auto-export="interfaces" />
+
+	<reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" />
+	<reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" />
+	<reference id="menuManager" interface="net.sf.taverna.t2.ui.menu.MenuManager" />
+	<reference id="reportManager" interface="net.sf.taverna.t2.workbench.report.ReportManager" cardinality="0..1" />
+	<reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" />
+	<reference id="workbench" interface="net.sf.taverna.t2.workbench.ui.Workbench" cardinality="0..1" />
+	<reference id="workbenchConfiguration" interface="net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration" />
+	<reference id="activityIconManager" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconManager" />
+	<reference id="colourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" />
+	<reference id="serviceRegistry" interface="uk.org.taverna.commons.services.ServiceRegistry" />
+
+	<list id="annotationBeans" interface="net.sf.taverna.t2.annotation.AnnotationBeanSPI"/>
+
+</beans:beans>


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeJsonEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeJsonEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeJsonEdit.java
new file mode 100644
index 0000000..2c79ff3
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeJsonEdit.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Changes the JSON of a configuration.
+ * 
+ * @author David Withers
+ */
+public class ChangeJsonEdit extends AbstractEdit<Configuration> {
+	private JsonNode oldJson, newJson;
+
+	public ChangeJsonEdit(Configuration configuration, JsonNode newJson) {
+		super(configuration);
+		this.newJson = newJson;
+		newJson = configuration.getJson();
+	}
+
+	@Override
+	protected void doEditAction(Configuration configuration) {
+		configuration.setJson(newJson);
+	}
+
+	@Override
+	protected void undoEditAction(Configuration configuration) {
+		configuration.setJson(oldJson);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ClearIterationStrategyStackEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ClearIterationStrategyStackEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ClearIterationStrategyStackEdit.java
new file mode 100644
index 0000000..956857c
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ClearIterationStrategyStackEdit.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (C) 2011 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyStack;
+
+/**
+ * Removes all the iteration strategies from an iteration strategy stack.
+ * 
+ * @author David Withers
+ */
+public class ClearIterationStrategyStackEdit extends
+		AbstractEdit<IterationStrategyStack> {
+	private IterationStrategyStack oldIterationStrategyStack;
+
+	public ClearIterationStrategyStackEdit(
+			IterationStrategyStack iterationStrategyStack) {
+		super(iterationStrategyStack);
+	}
+
+	@Override
+	protected void doEditAction(IterationStrategyStack iterationStrategyStack) {
+		oldIterationStrategyStack = new IterationStrategyStack();
+		oldIterationStrategyStack.addAll(iterationStrategyStack);
+		iterationStrategyStack.clear();
+	}
+
+	@Override
+	public void undoEditAction(IterationStrategyStack iterationStrategyStack) {
+		iterationStrategyStack.addAll(oldIterationStrategyStack);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ConfigureEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ConfigureEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ConfigureEdit.java
new file mode 100644
index 0000000..f4274b0
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ConfigureEdit.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.common.Configurable;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+
+/**
+ * An Edit that configures a {@link Configurable} with a given
+ * {@link Configuration}.
+ * 
+ * @author David Withers
+ */
+public class ConfigureEdit<ConfigurableType extends Configurable> extends
+		AbstractEdit<ConfigurableType> {
+	private final Configuration oldConfiguration;
+	private final Configuration newConfiguration;
+
+	public ConfigureEdit(ConfigurableType configurable,
+			Configuration oldConfiguration, Configuration newConfiguration) {
+		super(configurable);
+		this.oldConfiguration = oldConfiguration;
+		this.newConfiguration = newConfiguration;
+	}
+
+	@Override
+	protected void doEditAction(ConfigurableType configurable) {
+		oldConfiguration.setConfigures(null);
+		newConfiguration.setConfigures(configurable);
+	}
+
+	@Override
+	protected void undoEditAction(ConfigurableType configurable) {
+		oldConfiguration.setConfigures(configurable);
+		newConfiguration.setConfigures(null);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityEdit.java
new file mode 100644
index 0000000..1350d88
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityEdit.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+
+/**
+ * Remove an Activity from a Processor.
+ *
+ * @author alanrw
+ */
+public class RemoveActivityEdit extends AbstractEdit<Processor> {
+	private Activity activityToRemove;
+	private ProcessorBinding removedProcessorBinding;
+
+	public RemoveActivityEdit(Processor processor, Activity activity) {
+		super(processor);
+		this.activityToRemove = activity;
+	}
+
+	@Override
+	protected void doEditAction(Processor processor) {
+		for (ProcessorBinding binding : scufl2Tools
+				.processorBindingsToActivity(activityToRemove))
+			if (binding.getBoundProcessor().equals(processor)) {
+				removedProcessorBinding = binding;
+				removedProcessorBinding.setParent(null);
+			}
+	}
+
+	@Override
+	protected void undoEditAction(Processor processor) {
+		removedProcessorBinding.setParent(activityToRemove.getParent());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityInputPortMappingEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityInputPortMappingEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityInputPortMappingEdit.java
new file mode 100644
index 0000000..ce058bf
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityInputPortMappingEdit.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.port.InputActivityPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding;
+
+public class RemoveActivityInputPortMappingEdit extends AbstractEdit<Activity> {
+	private final InputActivityPort inputActivityPort;
+	private ProcessorInputPortBinding removedPortBinding;
+	private ProcessorBinding processorBinding;
+
+	public RemoveActivityInputPortMappingEdit(Activity activity,
+			InputActivityPort inputActivityPort) {
+		super(activity);
+		this.inputActivityPort = inputActivityPort;
+	}
+
+	@Override
+	protected void doEditAction(Activity activity) {
+		removedPortBinding = scufl2Tools.processorPortBindingForPort(
+				inputActivityPort, activity.getParent());
+		processorBinding = removedPortBinding.getParent();
+		removedPortBinding.setParent(null);
+	}
+
+	@Override
+	protected void undoEditAction(Activity activity) {
+		removedPortBinding.setParent(processorBinding);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityOutputPortMappingEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityOutputPortMappingEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityOutputPortMappingEdit.java
new file mode 100644
index 0000000..8639dbd
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityOutputPortMappingEdit.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.port.OutputActivityPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding;
+
+public class RemoveActivityOutputPortMappingEdit extends AbstractEdit<Activity> {
+	private final OutputActivityPort outputActivityPort;
+	private ProcessorOutputPortBinding removedPortBinding;
+	private ProcessorBinding processorBinding;
+
+	public RemoveActivityOutputPortMappingEdit(Activity activity,
+			OutputActivityPort outputActivityPort) {
+		super(activity);
+		this.outputActivityPort = outputActivityPort;
+	}
+
+	@Override
+	protected void doEditAction(Activity activity) {
+		removedPortBinding = scufl2Tools.processorPortBindingForPort(
+				outputActivityPort, activity.getParent());
+		processorBinding = removedPortBinding.getParent();
+		removedPortBinding.setParent(null);
+	}
+
+	@Override
+	protected void undoEditAction(Activity activity) {
+		removedPortBinding.setParent(processorBinding);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveChildEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveChildEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveChildEdit.java
new file mode 100644
index 0000000..1b9d5d2
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveChildEdit.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.common.Child;
+import uk.org.taverna.scufl2.api.common.WorkflowBean;
+
+/**
+ * Removes a child from a parent.
+ * 
+ * @author David Withers
+ */
+public class RemoveChildEdit<T extends WorkflowBean> extends AbstractEdit<T> {
+	private Child<T> child;
+
+	public RemoveChildEdit(T parent, Child<T> child) {
+		super(parent);
+		this.child = child;
+	}
+
+	@Override
+	protected void doEditAction(T parent) {
+		child.setParent(null);
+	}
+
+	@Override
+	protected void undoEditAction(T parent) {
+		child.setParent(parent);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveDataLinkEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveDataLinkEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveDataLinkEdit.java
new file mode 100644
index 0000000..9f8d243
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveDataLinkEdit.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * 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.workflow.edits;
+
+import java.util.List;
+
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyNode;
+import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyParent;
+import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyTopNode;
+import uk.org.taverna.scufl2.api.iterationstrategy.PortNode;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.port.ReceiverPort;
+
+/**
+ * Remove a DataLink from a Workflow.
+ * <p>
+ * Handles setting the merge position of all dataLinks with the same receiver port.
+ *
+ * @author David Withers
+ */
+public class RemoveDataLinkEdit extends AbstractEdit<Workflow> {
+	private final DataLink dataLink;
+	private PortNode portNode;
+	private int portPosition;
+	private IterationStrategyTopNode parent;
+
+	public RemoveDataLinkEdit(Workflow workflow, DataLink dataLink) {
+		super(workflow);
+		this.dataLink = dataLink;
+	}
+
+	@Override
+	protected void doEditAction(Workflow workflow) {
+		dataLink.setParent(null);
+		ReceiverPort sink = dataLink.getSendsTo();
+		List<DataLink> datalinksTo = scufl2Tools.datalinksTo(sink);
+		if (datalinksTo.isEmpty()) {
+			if (sink instanceof InputProcessorPort) {
+				InputProcessorPort port = (InputProcessorPort) sink;
+				for (IterationStrategyTopNode topNode : port.getParent().getIterationStrategyStack()) {
+					portNode = findPortNode(topNode, port);
+					if (portNode != null) {
+						IterationStrategyParent parentNode = portNode.getParent();
+						if (parentNode instanceof IterationStrategyTopNode) {
+							parent = (IterationStrategyTopNode) parentNode;
+							portPosition = parent.indexOf(portNode);
+							parent.remove(portNode);
+						}
+						break;
+					}
+				}
+			}
+		} else if (datalinksTo.size() == 1) {
+			datalinksTo.get(0).setMergePosition(null);
+		} else {
+			for (int i = 0; i < datalinksTo.size(); i++)
+				datalinksTo.get(i).setMergePosition(i);
+		}
+	}
+
+	@Override
+	protected void undoEditAction(Workflow workflow) {
+		ReceiverPort sink = dataLink.getSendsTo();
+		List<DataLink> datalinksTo = scufl2Tools.datalinksTo(sink);
+		if (dataLink.getMergePosition() != null)
+			for (int i = dataLink.getMergePosition(); i < datalinksTo.size(); i++)
+				datalinksTo.get(i).setMergePosition(i + 1);
+		if (portNode != null) {
+			parent.add(portPosition, portNode);
+			portNode.setParent(parent);
+		}
+		dataLink.setParent(workflow);
+	}
+
+	private PortNode findPortNode(IterationStrategyTopNode topNode,
+			InputProcessorPort port) {
+		for (IterationStrategyNode node : topNode) {
+			if (node instanceof PortNode) {
+				PortNode portNode = (PortNode) node;
+				if (port.equals(portNode.getInputProcessorPort()))
+					return portNode;
+			} else if (node instanceof IterationStrategyTopNode) {
+				IterationStrategyTopNode iterationStrategyTopNode = (IterationStrategyTopNode) node;
+				PortNode result = findPortNode(iterationStrategyTopNode, port);
+				if (result != null)
+					return result;
+			}
+		}
+		return null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorInputPortEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorInputPortEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorInputPortEdit.java
new file mode 100644
index 0000000..497f700
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorInputPortEdit.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+
+public class RemoveProcessorInputPortEdit extends RemoveChildEdit<Processor> {
+	public RemoveProcessorInputPortEdit(Processor processor,
+			InputProcessorPort port) {
+		super(processor, port);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorOutputPortEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorOutputPortEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorOutputPortEdit.java
new file mode 100644
index 0000000..72a1238
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorOutputPortEdit.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+
+public class RemoveProcessorOutputPortEdit extends RemoveChildEdit<Processor> {
+	public RemoveProcessorOutputPortEdit(Processor processor,
+			OutputProcessorPort port) {
+		super(processor, port);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowInputPortEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowInputPortEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowInputPortEdit.java
new file mode 100644
index 0000000..a6ac8e4
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowInputPortEdit.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (C) 2013 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.workflow.edits;
+
+import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW;
+
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputActivityPort;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Removes an input port from a workflow.
+ *
+ * @author David Withers
+ */
+public class RemoveWorkflowInputPortEdit extends AbstractEdit<Workflow> {
+	private final InputWorkflowPort port;
+	private final CompoundEdit nestedPortEdit = new CompoundEdit();
+
+	public RemoveWorkflowInputPortEdit(Workflow workflow, InputWorkflowPort port) {
+		super(workflow);
+		this.port = port;
+		WorkflowBundle workflowBundle = workflow.getParent();
+		if (workflowBundle != null)
+			for (Profile profile : workflowBundle.getProfiles())
+				for (Activity activity : profile.getActivities())
+					if (activity.getType().equals(NESTED_WORKFLOW))
+						for (Configuration c : scufl2Tools.configurationsFor(
+								activity, profile))
+							defineEditsForConfiguration(workflow, port,
+									workflowBundle, activity, c);
+	}
+
+	private void defineEditsForConfiguration(Workflow workflow,
+			InputWorkflowPort port, WorkflowBundle workflowBundle,
+			Activity activity, Configuration c) {
+		List<Edit<?>> edits = nestedPortEdit.getChildEdits();
+		JsonNode nested = c.getJson().get("nestedWorkflow");
+		Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName(
+				nested.asText());
+		if (nestedWorkflow != workflow)
+			return;
+
+		InputActivityPort activityPort = activity.getInputPorts().getByName(
+				port.getName());
+		edits.add(new RemoveChildEdit<>(activity, activityPort));
+
+		for (ProcessorBinding binding : scufl2Tools
+				.processorBindingsToActivity(activity)) {
+			Processor processor = binding.getBoundProcessor();
+			for (ProcessorInputPortBinding portBinding : binding
+					.getInputPortBindings())
+				if (portBinding.getBoundActivityPort() == activityPort) {
+					InputProcessorPort processorPort = portBinding
+							.getBoundProcessorPort();
+					edits.add(new RemoveProcessorInputPortEdit(processor,
+							processorPort));
+					edits.add(new RemoveChildEdit<>(binding, portBinding));
+				}
+		}
+	}
+
+	@Override
+	protected void doEditAction(Workflow workflow) throws EditException {
+		port.setParent(null);
+		nestedPortEdit.doEdit();
+	}
+
+	@Override
+	protected void undoEditAction(Workflow workflow) {
+		port.setParent(workflow);
+		nestedPortEdit.undo();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowOutputPortEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowOutputPortEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowOutputPortEdit.java
new file mode 100644
index 0000000..2c71da6
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowOutputPortEdit.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (C) 2013 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.workflow.edits;
+
+import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW;
+
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.OutputActivityPort;
+import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Removes an output port from a workflow.
+ *
+ * @author David Withers
+ */
+public class RemoveWorkflowOutputPortEdit extends AbstractEdit<Workflow> {
+	private final OutputWorkflowPort port;
+	private final CompoundEdit nestedPortEdit = new CompoundEdit();
+
+	public RemoveWorkflowOutputPortEdit(Workflow workflow,
+			OutputWorkflowPort port) {
+		super(workflow);
+		this.port = port;
+		WorkflowBundle workflowBundle = workflow.getParent();
+		if (workflowBundle != null)
+			for (Profile profile : workflowBundle.getProfiles())
+				for (Activity activity : profile.getActivities())
+					if (activity.getType().equals(NESTED_WORKFLOW))
+						for (Configuration c : scufl2Tools.configurationsFor(
+								activity, profile))
+							defineEditsForConfiguration(workflow, port,
+									workflowBundle, activity, c);
+	}
+
+	private void defineEditsForConfiguration(Workflow workflow,
+			OutputWorkflowPort port, WorkflowBundle workflowBundle,
+			Activity activity, Configuration c) {
+		List<Edit<?>> edits = nestedPortEdit.getChildEdits();
+		JsonNode nested = c.getJson().get("nestedWorkflow");
+		Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName(
+				nested.asText());
+		if (nestedWorkflow != workflow)
+			return;
+
+		OutputActivityPort activityPort = activity.getOutputPorts().getByName(
+				port.getName());
+		edits.add(new RemoveChildEdit<>(activity, activityPort));
+		for (ProcessorBinding processorBinding : scufl2Tools
+				.processorBindingsToActivity(activity))
+			for (ProcessorOutputPortBinding portBinding : processorBinding
+					.getOutputPortBindings())
+				if (portBinding.getBoundActivityPort() == activityPort) {
+					OutputProcessorPort processorPort = portBinding
+							.getBoundProcessorPort();
+					edits.add(new RemoveProcessorOutputPortEdit(
+							processorBinding.getBoundProcessor(), processorPort));
+					edits.add(new RemoveChildEdit<>(processorBinding,
+							portBinding));
+				}
+	}
+
+	@Override
+	protected void doEditAction(Workflow workflow) throws EditException {
+		port.setParent(null);
+		nestedPortEdit.doEdit();
+	}
+
+	@Override
+	protected void undoEditAction(Workflow workflow) {
+		port.setParent(workflow);
+		nestedPortEdit.undo();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RenameEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RenameEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RenameEdit.java
new file mode 100644
index 0000000..beb4913
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RenameEdit.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.workflow.edits;
+
+import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.Named;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.ActivityPort;
+import uk.org.taverna.scufl2.api.port.InputPort;
+import uk.org.taverna.scufl2.api.port.ProcessorPort;
+import uk.org.taverna.scufl2.api.port.WorkflowPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding;
+import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Renames a Named WorkflowBean.
+ *
+ * @author David Withers
+ */
+public class RenameEdit<T extends Named> extends AbstractEdit<T> {
+	private String oldName, newName;
+
+	public RenameEdit(T named, String newName) {
+		super(named);
+		this.newName = newName;
+		oldName = named.getName();
+	}
+
+	@Override
+	protected void doEditAction(T named) {
+		named.setName(newName);
+		if (named instanceof WorkflowPort)
+			checkNestedPortNames((WorkflowPort) named, oldName, newName);
+	}
+
+	@Override
+	protected void undoEditAction(T named) {
+		named.setName(oldName);
+		if (named instanceof WorkflowPort)
+			checkNestedPortNames((WorkflowPort) named, newName, oldName);
+	}
+
+	private void checkNestedPortNames(WorkflowPort workflowPort, String oldName, String newName) {
+		Workflow workflow = workflowPort.getParent();
+		if (workflow == null)
+			return;
+		WorkflowBundle workflowBundle = workflow.getParent();
+		if (workflowBundle == null)
+			return;
+		for (Profile profile : workflowBundle.getProfiles())
+			for (Activity activity : profile.getActivities())
+				if (activity.getType().equals(NESTED_WORKFLOW))
+					for (Configuration c : scufl2Tools.configurationsFor(activity, profile))
+						changeActivityPortName(workflowPort, oldName,
+								newName, workflow, workflowBundle, activity, c);
+	}
+
+	private void changeActivityPortName(WorkflowPort workflowPort,
+			String oldName, String newName, Workflow workflow,
+			WorkflowBundle workflowBundle, Activity activity, Configuration c) {
+		JsonNode nested = c.getJson().get("nestedWorkflow");
+		Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName(
+				nested.asText());
+		if (nestedWorkflow != workflow)
+			return;
+
+		ActivityPort activityPort;
+		if (workflowPort instanceof InputPort) {
+			activityPort = activity.getInputPorts().getByName(oldName);
+			changeProcessorInputPortName(oldName, newName, activity,
+					activityPort);
+		} else {
+			activityPort = activity.getOutputPorts().getByName(oldName);
+			changeProcessorOutputPortName(oldName, newName, activity,
+					activityPort);
+		}
+		activityPort.setName(newName);
+	}
+
+	private void changeProcessorInputPortName(String oldName, String newName,
+			Activity activity, ActivityPort activityPort) {
+		bindings: for (ProcessorBinding binding : scufl2Tools
+				.processorBindingsToActivity(activity))
+			for (ProcessorInputPortBinding portBinding : binding
+					.getInputPortBindings())
+				if (portBinding.getBoundActivityPort() == activityPort) {
+					ProcessorPort processorPort = portBinding
+							.getBoundProcessorPort();
+					if (processorPort.getName().equals(oldName)) {
+						processorPort.setName(newName);
+						continue bindings;
+					}
+				}
+	}
+
+	private void changeProcessorOutputPortName(String oldName, String newName,
+			Activity activity, ActivityPort activityPort) {
+		bindings: for (ProcessorBinding binding : scufl2Tools
+				.processorBindingsToActivity(activity))
+			for (ProcessorOutputPortBinding portBinding : binding
+					.getOutputPortBindings())
+				if (portBinding.getBoundActivityPort() == activityPort) {
+					ProcessorPort processorPort = portBinding
+							.getBoundProcessorPort();
+					if (processorPort.getName().equals(oldName)) {
+						processorPort.setName(newName);
+						continue bindings;
+					}
+				}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ReorderMergePositionsEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ReorderMergePositionsEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ReorderMergePositionsEdit.java
new file mode 100644
index 0000000..49caa32
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ReorderMergePositionsEdit.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.workflow.edits;
+
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.edits.EditException;
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.port.ReceiverPort;
+
+/**
+ * Change datalink merge positions based on ordered list of data links.
+ * 
+ * @author David Withers
+ * @author Stian Soiland-Reyes
+ */
+public class ReorderMergePositionsEdit extends AbstractEdit<ReceiverPort> {
+	private List<DataLink> newMergePositions;
+	private final List<DataLink> oldMergePositions;
+
+	public ReorderMergePositionsEdit(List<DataLink> dataLinks,
+			List<DataLink> newMergePositions) {
+		super(dataLinks.get(0).getSendsTo());
+		this.oldMergePositions = dataLinks;
+		this.newMergePositions = newMergePositions;
+	}
+
+	@Override
+	protected void doEditAction(ReceiverPort subject) throws EditException {
+		for (int i = 0; i < newMergePositions.size(); i++)
+			newMergePositions.get(i).setMergePosition(i);
+	}
+
+	@Override
+	protected void undoEditAction(ReceiverPort subject) {
+		for (int i = 0; i < oldMergePositions.size(); i++)
+			oldMergePositions.get(i).setMergePosition(i);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/SetIterationStrategyStackEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/SetIterationStrategyStackEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/SetIterationStrategyStackEdit.java
new file mode 100644
index 0000000..e16b95f
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/SetIterationStrategyStackEdit.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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.workflow.edits;
+
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyStack;
+
+/**
+ * Set the iteration strategy
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class SetIterationStrategyStackEdit extends AbstractEdit<Processor> {
+	private final IterationStrategyStack iterationStrategyStack;
+	private IterationStrategyStack oldStrategyStack;
+
+	public SetIterationStrategyStackEdit(Processor processor,
+			IterationStrategyStack iterationStrategyStack) {
+		super(processor);
+		this.iterationStrategyStack = iterationStrategyStack;
+	}
+
+	@Override
+	protected void doEditAction(Processor processor) {
+		oldStrategyStack = processor.getIterationStrategyStack();
+		processor.setIterationStrategyStack(iterationStrategyStack);
+	}
+
+	@Override
+	protected void undoEditAction(Processor processor) {
+		processor.setIterationStrategyStack(oldStrategyStack);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/UpdateDataflowInternalIdentifierEdit.java
----------------------------------------------------------------------
diff --git a/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/UpdateDataflowInternalIdentifierEdit.java b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/UpdateDataflowInternalIdentifierEdit.java
new file mode 100644
index 0000000..f246d60
--- /dev/null
+++ b/taverna-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/UpdateDataflowInternalIdentifierEdit.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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.workflow.edits;
+
+import java.net.URI;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+public class UpdateDataflowInternalIdentifierEdit extends
+		AbstractEdit<WorkflowBundle> {
+	private URI newId;
+	private URI oldId;
+
+	public UpdateDataflowInternalIdentifierEdit(WorkflowBundle dataflow,
+			URI newId) {
+		super(dataflow);
+		this.newId = newId;
+		this.oldId = dataflow.getGlobalBaseURI();
+	}
+
+	@Override
+	protected void doEditAction(WorkflowBundle dataflow) {
+		dataflow.setGlobalBaseURI(newId);
+	}
+
+	@Override
+	protected void undoEditAction(WorkflowBundle dataflow) {
+		dataflow.setGlobalBaseURI(oldId);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/pom.xml b/taverna-edits-impl/pom.xml
new file mode 100644
index 0000000..98b9026
--- /dev/null
+++ b/taverna-edits-impl/pom.xml
@@ -0,0 +1,65 @@
+<?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>taverna-edits-impl</artifactId>
+	<packaging>bundle</packaging>
+	<name>Edits implementation</name>
+	<description>
+		Implementation for doing workflow edits and undo.
+	</description>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-edits-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-menu-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-selection-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-ui</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+			<version>${taverna.language.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/EditManagerImpl.java
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/EditManagerImpl.java b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/EditManagerImpl.java
new file mode 100644
index 0000000..f97d36c
--- /dev/null
+++ b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/EditManagerImpl.java
@@ -0,0 +1,285 @@
+/*******************************************************************************
+ * 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.edits.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.sf.taverna.t2.lang.observer.MultiCaster;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * Implementation of {@link EditManager}.
+ *
+ * @author Stian Soiland-Reyes
+ */
+public class EditManagerImpl implements EditManager {
+	private static Logger logger = Logger.getLogger(EditManagerImpl.class);
+
+	private MultiCaster<EditManagerEvent> multiCaster = new MultiCaster<>(this);
+	private Map<WorkflowBundle, DataflowEdits> editsForDataflow = new HashMap<>();
+
+	@Override
+	public void addObserver(Observer<EditManagerEvent> observer) {
+		multiCaster.addObserver(observer);
+	}
+
+	@Override
+	public boolean canRedoDataflowEdit(WorkflowBundle dataflow) {
+		DataflowEdits edits = getEditsForDataflow(dataflow);
+		return edits.canRedo();
+	}
+
+	@Override
+	public boolean canUndoDataflowEdit(WorkflowBundle dataflow) {
+		DataflowEdits edits = getEditsForDataflow(dataflow);
+		return edits.canUndo();
+	}
+
+	@Override
+	public void doDataflowEdit(WorkflowBundle dataflow, Edit<?> edit)
+			throws EditException {
+		// We do the edit before we notify the observers
+		DataflowEdits edits = getEditsForDataflow(dataflow);
+		synchronized (edits) {
+			// Make sure the edits are in the order they were performed
+			edit.doEdit();
+			edits.addEdit(edit);
+		}
+		multiCaster.notify(new DataflowEditEvent(dataflow, edit));
+	}
+
+	@Override
+	public List<Observer<EditManagerEvent>> getObservers() {
+		return multiCaster.getObservers();
+	}
+
+	@Override
+	public void redoDataflowEdit(WorkflowBundle dataflow) throws EditException {
+		DataflowEdits edits = getEditsForDataflow(dataflow);
+		Edit<?> edit;
+		synchronized (edits) {
+			if (!edits.canRedo())
+				return;
+			edit = edits.getLastUndo();
+			edit.doEdit();
+			edits.addRedo(edit);
+		}
+		multiCaster.notify(new DataFlowRedoEvent(dataflow, edit));
+	}
+
+	@Override
+	public void removeObserver(Observer<EditManagerEvent> observer) {
+		multiCaster.removeObserver(observer);
+	}
+
+	@Override
+	public void undoDataflowEdit(WorkflowBundle dataflow) {
+		DataflowEdits edits = getEditsForDataflow(dataflow);
+		Edit<?> edit;
+		synchronized (edits) {
+			if (!edits.canUndo())
+				return;
+			edit = edits.getLastEdit();
+			edit.undo();
+			edits.addUndo(edit);
+		}
+		logger.info("Undoing an edit");
+		multiCaster.notify(new DataFlowUndoEvent(dataflow, edit));
+	}
+
+	/**
+	 * Get the set of edits for a given dataflow, creating if neccessary.
+	 *
+	 * @param dataflow
+	 *            Dataflow the edits relate to
+	 * @return A {@link DataflowEdits} instance to keep edits for the given
+	 *         dataflow
+	 */
+	protected synchronized DataflowEdits getEditsForDataflow(WorkflowBundle dataflow) {
+		DataflowEdits edits = editsForDataflow.get(dataflow);
+		if (edits == null) {
+			edits = new DataflowEdits();
+			editsForDataflow.put(dataflow, edits);
+		}
+		return edits;
+	}
+
+	/**
+	 * A set of edits and undoes for a {@link Dataflow}
+	 *
+	 * @author Stian Soiland-Reyes
+	 *
+	 */
+	public class DataflowEdits {
+		/**
+		 * List of edits that have been performed and can be undone.
+		 */
+		private List<Edit<?>> edits = new ArrayList<>();
+		/**
+		 * List of edits that have been undone and can be redone
+		 */
+		private List<Edit<?>> undoes = new ArrayList<>();
+
+		/**
+		 * Add an {@link Edit} that has been done by the EditManager.
+		 * <p>
+		 * This can later be retrieved using {@link #getLastEdit()}. After
+		 * calling this {@link #canRedo()} will be false.
+		 *
+		 * @param edit
+		 *            {@link Edit} that has been undone
+		 */
+		public synchronized void addEdit(Edit<?> edit) {
+			addEditOrRedo(edit, false);
+		}
+
+		/**
+		 * Add an {@link Edit} that has been redone by the EditManager.
+		 * <p>
+		 * The {@link Edit} must be the same as the last undo returned through
+		 * {@link #getLastUndo()}.
+		 * <p>
+		 * This method works like {@link #addEdit(Edit)} except that instead of
+		 * removing all possible redoes, only the given {@link Edit} is removed.
+		 *
+		 * @param edit
+		 *            {@link Edit} that has been redone
+		 */
+		public synchronized void addRedo(Edit<?> edit) {
+			addEditOrRedo(edit, true);
+		}
+
+		/**
+		 * Add an {@link Edit} that has been undone by the EditManager.
+		 * <p>
+		 * After calling this method {@link #canRedo()} will be true, and the
+		 * edit can be retrieved using {@link #getLastUndo()}.
+		 * </p>
+		 * <p>
+		 * The {@link Edit} must be the last edit returned from
+		 * {@link #getLastEdit()}, after calling this method
+		 * {@link #getLastEdit()} will return the previous edit or
+		 * {@link #canUndo()} will be false if there are no more edits.
+		 *
+		 * @param edit
+		 *            {@link Edit} that has been undone
+		 */
+		public synchronized void addUndo(Edit<?> edit) {
+			int lastIndex = edits.size() - 1;
+			if (lastIndex < 0 || !edits.get(lastIndex).equals(edit))
+				throw new IllegalArgumentException("Can't undo unknown edit "
+						+ edit);
+			undoes.add(edit);
+			edits.remove(lastIndex);
+		}
+
+		/**
+		 * True if there are undone events that can be redone.
+		 *
+		 * @return <code>true</code> if there are undone events
+		 */
+		public boolean canRedo() {
+			return !undoes.isEmpty();
+		}
+
+		/**
+		 * True if there are edits that can be undone and later added with
+		 * {@link #addUndo(Edit)}.
+		 *
+		 * @return <code>true</code> if there are edits that can be undone
+		 */
+		public boolean canUndo() {
+			return !edits.isEmpty();
+		}
+
+		/**
+		 * Get the last edit that can be undone. This edit was the last one to
+		 * be added with {@link #addEdit(Edit)} or {@link #addRedo(Edit)}.
+		 *
+		 * @return The last added {@link Edit}
+		 * @throws IllegalStateException
+		 *             If there are no more edits (Check with {@link #canUndo()}
+		 *             first)
+		 *
+		 */
+		public synchronized Edit<?> getLastEdit() throws IllegalStateException {
+			if (edits.isEmpty())
+				throw new IllegalStateException("No more edits");
+			int lastEdit = edits.size() - 1;
+			return edits.get(lastEdit);
+		}
+
+		/**
+		 * Get the last edit that can be redone. This edit was the last one to
+		 * be added with {@link #addUndo(Edit)}.
+		 *
+		 * @return The last undone {@link Edit}
+		 * @throws IllegalStateException
+		 *             If there are no more edits (Check with {@link #canRedo()}
+		 *             first)
+		 *
+		 */
+		public synchronized Edit<?> getLastUndo() throws IllegalStateException {
+			if (undoes.isEmpty())
+				throw new IllegalStateException("No more undoes");
+			int lastUndo = undoes.size() - 1;
+			return undoes.get(lastUndo);
+		}
+
+		/**
+		 * Add an edit or redo. Common functionallity called by
+		 * {@link #addEdit(Edit)} and {@link #addRedo(Edit)}.
+		 *
+		 * @see #addEdit(Edit)
+		 * @see #addRedo(Edit)
+		 * @param edit
+		 *            The {@link Edit} to add
+		 * @param isRedo
+		 *            True if this is a redo
+		 */
+		protected void addEditOrRedo(Edit<?> edit, boolean isRedo) {
+			edits.add(edit);
+			if (undoes.isEmpty())
+				return;
+			if (isRedo) {
+				// It's a redo, remove only the last one
+				int lastUndoIndex = undoes.size() - 1;
+				Edit<?> lastUndo = undoes.get(lastUndoIndex);
+				if (!edit.equals(lastUndo))
+					throw new IllegalArgumentException(
+							"Can only redo last undo");
+				undoes.remove(lastUndoIndex);
+			} else
+				// It's a new edit, remove all redos
+				undoes.clear();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/AbstractUndoAction.java
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/AbstractUndoAction.java b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/AbstractUndoAction.java
new file mode 100644
index 0000000..97d14a6
--- /dev/null
+++ b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/AbstractUndoAction.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.edits.impl.menu;
+
+import static java.awt.Toolkit.getDefaultToolkit;
+import static java.awt.event.KeyEvent.VK_Y;
+import static java.awt.event.KeyEvent.VK_Z;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.redoIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.undoIcon;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.PerspectiveSelectionEvent;
+import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public abstract class AbstractUndoAction extends AbstractAction {
+	protected EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public AbstractUndoAction(String label, EditManager editManager) {
+		super(label);
+		this.editManager = editManager;
+		if (label.equals("Undo")) {
+			this.putValue(SMALL_ICON, undoIcon);
+			this.putValue(SHORT_DESCRIPTION, "Undo an action");
+			putValue(
+					ACCELERATOR_KEY,
+					getKeyStroke(VK_Z, getDefaultToolkit()
+							.getMenuShortcutKeyMask()));
+		} else if (label.equals("Redo")) {
+			this.putValue(SMALL_ICON, redoIcon);
+			this.putValue(SHORT_DESCRIPTION, "Redo an action");
+			putValue(
+					ACCELERATOR_KEY,
+					getKeyStroke(VK_Y, getDefaultToolkit()
+							.getMenuShortcutKeyMask()));
+		}
+		editManager.addObserver(new EditManagerObserver());
+		updateStatus();
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		WorkflowBundle workflowBundle = getCurrentDataflow();
+		if (workflowBundle != null)
+			performUndoOrRedo(workflowBundle);
+	}
+
+	/**
+	 * Check if action should be enabled or disabled and update its status.
+	 */
+	public void updateStatus() {
+		WorkflowBundle workflowBundle = getCurrentDataflow();
+		if (workflowBundle == null)
+			setEnabled(false);
+		setEnabled(isActive(workflowBundle));
+	}
+
+	/**
+	 * Retrieve the current dataflow from the {@link ModelMap}, or
+	 * <code>null</code> if no workflow is active.
+	 * 
+	 * @return The current {@link Dataflow}
+	 */
+	protected WorkflowBundle getCurrentDataflow() {
+		if (selectionManager == null)
+			return null;
+		return selectionManager.getSelectedWorkflowBundle();
+	}
+
+	/**
+	 * Return <code>true</code> if the action should be enabled when the given
+	 * {@link Dataflow} is the current, ie. if it's undoable or redoable.
+	 * 
+	 * @param dataflow
+	 *            Current {@link Dataflow}
+	 * @return <code>true</code> if the action should be enabled.
+	 */
+	protected abstract boolean isActive(WorkflowBundle workflowBundle);
+
+	/**
+	 * Called by {@link #actionPerformed(ActionEvent)} when the current dataflow
+	 * is not <code>null</code>.
+	 * 
+	 * @param dataflow
+	 *            {@link Dataflow} on which to undo or redo
+	 */
+	protected abstract void performUndoOrRedo(WorkflowBundle workflowBundle);
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+		if (selectionManager != null)
+			selectionManager.addObserver(new SelectionManagerObserver());
+	}
+
+	/**
+	 * Update the status if there's been an edit done on the current workflow.
+	 * 
+	 */
+	protected class EditManagerObserver implements Observer<EditManagerEvent> {
+		@Override
+		public void notify(Observable<EditManagerEvent> sender,
+				EditManagerEvent message) throws Exception {
+			if (!(message instanceof AbstractDataflowEditEvent))
+				return;
+			AbstractDataflowEditEvent dataflowEdit = (AbstractDataflowEditEvent) message;
+			if (dataflowEdit.getDataFlow().equals(dataflowEdit.getDataFlow()))
+				// It's an edit that could effect our undoability
+				updateStatus();
+		}
+	}
+
+	private final class SelectionManagerObserver extends
+			SwingAwareObserver<SelectionManagerEvent> {
+		private static final String DESIGN_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.design.DesignPerspective";
+
+		@Override
+		public void notifySwing(Observable<SelectionManagerEvent> sender,
+				SelectionManagerEvent message) {
+			if (message instanceof WorkflowBundleSelectionEvent)
+				updateStatus();
+			else if (message instanceof PerspectiveSelectionEvent) {
+				PerspectiveSelectionEvent perspectiveSelectionEvent = (PerspectiveSelectionEvent) message;
+				if (DESIGN_PERSPECTIVE_ID.equals(perspectiveSelectionEvent
+						.getSelectedPerspective().getID()))
+					updateStatus();
+				else
+					setEnabled(false);
+			}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/RedoMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/RedoMenuAction.java b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/RedoMenuAction.java
new file mode 100644
index 0000000..2abc139
--- /dev/null
+++ b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/RedoMenuAction.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * 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.edits.impl.menu;
+
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuSection.UNDO_SECTION_URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * Redo the previous {@link Edit} done on the current workflow using the
+ * {@link EditManager}.
+ *
+ * @author Stian Soiland-Reyes
+ */
+public class RedoMenuAction extends AbstractMenuAction {
+	private static Logger logger = Logger.getLogger(RedoMenuAction.class);
+	private final EditManager editManager;
+	private SelectionManager selectionManager;
+	private AbstractUndoAction undoAction;
+
+	public RedoMenuAction(EditManager editManager) {
+		super(UNDO_SECTION_URI, 20);
+		this.editManager = editManager;
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		undoAction = new AbstractUndoAction("Redo", editManager) {
+			@Override
+			protected boolean isActive(WorkflowBundle workflowBundle) {
+				return editManager.canRedoDataflowEdit(workflowBundle);
+			}
+
+			@Override
+			protected void performUndoOrRedo(WorkflowBundle workflowBundle) {
+				try {
+					editManager.redoDataflowEdit(workflowBundle);
+				} catch (EditException | RuntimeException e) {
+					logger.warn("Could not redo for " + workflowBundle, e);
+					showMessageDialog(null, "Could not redo for workflow "
+							+ workflowBundle + ":\n" + e, "Could not redo",
+							ERROR_MESSAGE);
+				}
+			}
+		};
+		undoAction.setSelectionManager(selectionManager);
+		return undoAction;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+		if (undoAction != null)
+			undoAction.setSelectionManager(selectionManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuAction.java b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuAction.java
new file mode 100644
index 0000000..e1242b3
--- /dev/null
+++ b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuAction.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * 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.edits.impl.menu;
+
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuSection.UNDO_SECTION_URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * Undo the last {@link Edit} done on the current workflow using the
+ * {@link EditManager}.
+ * 
+ * @author Stian Soiland-Reyes
+ * 
+ */
+public class UndoMenuAction extends AbstractMenuAction {
+	private static Logger logger = Logger.getLogger(UndoMenuAction.class);
+	private final EditManager editManager;
+	private SelectionManager selectionManager;
+	private AbstractUndoAction undoAction;
+
+	public UndoMenuAction(EditManager editManager) {
+		super(UNDO_SECTION_URI, 10);
+		this.editManager = editManager;
+	}
+
+	@SuppressWarnings("serial")
+	@Override
+	protected Action createAction() {
+		undoAction = new AbstractUndoAction("Undo", editManager) {
+			@Override
+			protected boolean isActive(WorkflowBundle workflowBundle) {
+				return editManager.canUndoDataflowEdit(workflowBundle);
+			}
+
+			@Override
+			protected void performUndoOrRedo(WorkflowBundle workflowBundle) {
+				try {
+					editManager.undoDataflowEdit(workflowBundle);
+				} catch (RuntimeException e) {
+					logger.warn("Could not undo for " + workflowBundle, e);
+					showMessageDialog(null, "Could not undo for workflow "
+							+ workflowBundle + ":\n" + e, "Could not undo",
+							ERROR_MESSAGE);
+				}
+			}
+		};
+		undoAction.setSelectionManager(selectionManager);
+		return undoAction;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+		if (undoAction != null)
+			undoAction.setSelectionManager(selectionManager);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuSection.java b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuSection.java
new file mode 100644
index 0000000..b83a650
--- /dev/null
+++ b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuSection.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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.edits.impl.menu;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * A section of the Edit menu that contains {@link UndoMenuSection undo} and
+ * {@link RedoMenuAction redo}.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class UndoMenuSection extends AbstractMenuSection {
+	public static final URI UNDO_SECTION_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/edits#undoSection");
+	public static final URI EDIT_MENU_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#edit");
+
+	public UndoMenuSection() {
+		super(EDIT_MENU_URI, 10, UNDO_SECTION_URI);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/EditToolbarSection.java
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/EditToolbarSection.java b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/EditToolbarSection.java
new file mode 100644
index 0000000..9eea85a
--- /dev/null
+++ b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/EditToolbarSection.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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.edits.impl.toolbar;
+
+import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+public class EditToolbarSection extends AbstractMenuSection {
+	public static final URI EDIT_TOOLBAR_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#editToolbarSection");
+
+	public EditToolbarSection() {
+		super(DEFAULT_TOOL_BAR, 60, EDIT_TOOLBAR_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/RedoToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/RedoToolbarAction.java b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/RedoToolbarAction.java
new file mode 100644
index 0000000..09c0058
--- /dev/null
+++ b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/RedoToolbarAction.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.edits.impl.toolbar;
+
+import static net.sf.taverna.t2.workbench.edits.impl.toolbar.EditToolbarSection.EDIT_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.impl.menu.RedoMenuAction;
+
+public class RedoToolbarAction extends AbstractMenuAction {
+	private static final URI EDIT_TOOLBAR_REDO_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#editToolbarRedo");
+	private final RedoMenuAction redoMenuAction;
+
+	public RedoToolbarAction(RedoMenuAction redoMenuAction) {
+		super(EDIT_TOOLBAR_SECTION, 20, EDIT_TOOLBAR_REDO_URI);
+		this.redoMenuAction = redoMenuAction;
+	}
+
+	@Override
+	protected Action createAction() {
+		return redoMenuAction.getAction();
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/UndoToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/UndoToolbarAction.java b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/UndoToolbarAction.java
new file mode 100644
index 0000000..8e31ed3
--- /dev/null
+++ b/taverna-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/UndoToolbarAction.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.edits.impl.toolbar;
+
+import static net.sf.taverna.t2.workbench.edits.impl.toolbar.EditToolbarSection.EDIT_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuAction;
+
+public class UndoToolbarAction extends AbstractMenuAction {
+	private static final URI EDIT_TOOLBAR_UNDO_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#editToolbarUndo");
+	private final UndoMenuAction undoMenuAction;
+
+	public UndoToolbarAction(UndoMenuAction undoMenuAction) {
+		super(EDIT_TOOLBAR_SECTION, 10, EDIT_TOOLBAR_UNDO_URI);
+		this.undoMenuAction = undoMenuAction;
+	}
+
+	@Override
+	protected Action createAction() {
+		return undoMenuAction.getAction();
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffSidebarPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffSidebarPanel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffSidebarPanel.java
new file mode 100644
index 0000000..79ef03c
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffSidebarPanel.java
@@ -0,0 +1,359 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.User;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Util;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs, Emmanuel Tagarira, Jiten Bhagat
+ */
+public class MyStuffSidebarPanel extends JPanel implements ActionListener {
+  private final MainComponent pluginMainComponent;
+  private final MyExperimentClient myExperimentClient;
+  private final Logger logger;
+
+  // main components of the SidebarPanel
+  private final JPanel jpMyProfileBox;
+  private final JPanel jpMyFriendsBox;
+  private final JPanel jpMyGroupsBox;
+  private final JPanel jpMyFavouritesBox;
+  private final JPanel jpMyTagsBox;
+  private JButton bLogout;
+  protected JButton bRefreshMyStuff;
+  private JButton bUpload;
+
+  // icons which are used in several places in the sidebar
+  private final ImageIcon iconUser;
+  private final ImageIcon iconLogout;
+  private final FileManager fileManager;
+
+  public MyStuffSidebarPanel(MainComponent component, MyExperimentClient client, Logger logger, FileManager fileManager) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the
+	// parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+	this.fileManager = fileManager;
+
+	// prepare icons
+	iconUser = new ImageIcon(MyExperimentPerspective.getLocalIconURL(Resource.USER));
+	iconLogout = new ImageIcon(MyExperimentPerspective.getLocalResourceURL("logout_icon"));
+
+	// add elements of the sidebar
+	this.setLayout(new GridBagLayout());
+	GridBagConstraints gbConstraints = new GridBagConstraints();
+	gbConstraints.anchor = GridBagConstraints.NORTHWEST;
+	gbConstraints.fill = GridBagConstraints.HORIZONTAL;
+	gbConstraints.weightx = 1;
+	gbConstraints.gridx = 0;
+
+	gbConstraints.gridy = 0;
+	jpMyProfileBox = createMyProfileBox();
+	this.add(jpMyProfileBox, gbConstraints);
+
+	gbConstraints.gridy = 1;
+	jpMyFriendsBox = createMyFriendsBox();
+	this.add(jpMyFriendsBox, gbConstraints);
+
+	gbConstraints.gridy = 2;
+	jpMyGroupsBox = createMyGroupsBox();
+	this.add(jpMyGroupsBox, gbConstraints);
+
+	gbConstraints.gridy = 3;
+	jpMyFavouritesBox = createMyFavouritesBox();
+	repopulateFavouritesBox();
+	this.add(jpMyFavouritesBox, gbConstraints);
+
+	gbConstraints.gridy = 4;
+	jpMyTagsBox = createMyTagsBox();
+	this.add(jpMyTagsBox, gbConstraints);
+
+	// report that this component has been loaded
+	pluginMainComponent.getMyStuffTab().cdlComponentLoadingDone.countDown();
+  }
+
+  // creates a JPanel displaying the currently logged in user, logout button,
+  // etc
+  private JPanel createMyProfileBox() {
+	// panel containing name and avatar
+	JPanel jpAvatar = new JPanel();
+	jpAvatar.setLayout(new BoxLayout(jpAvatar, BoxLayout.X_AXIS));
+
+	User currentUser = this.myExperimentClient.getCurrentUser();
+	ImageIcon userAvatar = currentUser.getAvatar();
+	JLabel jlUserAvatar = new JLabel("No Profile Picture Found");
+	if (userAvatar != null)
+	  jlUserAvatar = new JLabel(Util.getResizedImageIcon(userAvatar, 80, 80));
+
+	jlUserAvatar.setAlignmentX(LEFT_ALIGNMENT);
+	jpAvatar.add(jlUserAvatar);
+
+	String name = "<html>";
+	for (int x = 0; x < currentUser.getName().split(" ").length; x++)
+	  name += currentUser.getName().split(" ")[x] + "<br>";
+	name += "</html>";
+
+	JClickableLabel jclUserName = new JClickableLabel(name, "preview:"
+		+ Resource.USER + ":" + currentUser.getURI(), pluginMainComponent.getPreviewBrowser(), this.iconUser);
+	jpAvatar.add(jclUserName);
+
+	// panel containing everything in the profile box
+	JPanel jpEverythingInProfileBox = new JPanel();
+	jpEverythingInProfileBox.setMaximumSize(new Dimension(1024, 0));
+	jpEverythingInProfileBox.setLayout(new BoxLayout(jpEverythingInProfileBox, BoxLayout.X_AXIS));
+
+	jpEverythingInProfileBox.add(jpAvatar);
+
+	// action buttons
+	bLogout = new JButton("Logout", iconLogout);
+	bLogout.addActionListener(this);
+
+	bRefreshMyStuff = new JButton("Refresh", WorkbenchIcons.refreshIcon);
+	bRefreshMyStuff.addActionListener(this.pluginMainComponent.getMyStuffTab());
+
+	bUpload = new JButton("Upload Workflow", WorkbenchIcons.upArrowIcon);
+	bUpload.addActionListener(this);
+
+	// panel for the buttons
+	JPanel jpButtons = new JPanel();
+	jpButtons.setLayout(new GridBagLayout());
+	GridBagConstraints gbc = new GridBagConstraints();
+	gbc.gridwidth = 1;
+	gbc.gridy = 0;
+	gbc.anchor = GridBagConstraints.NORTH;
+	gbc.fill = GridBagConstraints.BOTH;
+	gbc.gridx = 0;
+
+	jpButtons.add(bUpload, gbc);
+	gbc.gridy++;
+	jpButtons.add(bRefreshMyStuff, gbc);
+	gbc.gridy++;
+	jpButtons.add(bLogout, gbc);
+
+	jpEverythingInProfileBox.add(jpButtons);
+
+	jpEverythingInProfileBox.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " My Profile "), BorderFactory.createEmptyBorder(1, 8, 8, 5)));
+
+	return (jpEverythingInProfileBox);
+  }
+
+  // creates a JPanel that displays a list of all friends of the current user
+  private JPanel createMyFriendsBox() {
+	JPanel jpFriends = new JPanel();
+	jpFriends.setLayout(new BoxLayout(jpFriends, BoxLayout.Y_AXIS));
+
+	// iterate through all friends and add all to the panel
+	Iterator<HashMap<String, String>> iFriends = this.myExperimentClient.getCurrentUser().getFriends().iterator();
+	if (iFriends.hasNext()) {
+	  while (iFriends.hasNext()) {
+		HashMap<String, String> hmCurFriend = iFriends.next();
+		jpFriends.add(new JClickableLabel(hmCurFriend.get("name"), "preview:"
+			+ Resource.USER + ":" + hmCurFriend.get("uri"), pluginMainComponent.getPreviewBrowser(), this.iconUser));
+	  }
+	} else {
+	  // known not to have any friends
+	  JLabel lNone = new JLabel("None");
+	  lNone.setFont(lNone.getFont().deriveFont(Font.ITALIC));
+	  lNone.setForeground(Color.GRAY);
+	  jpFriends.add(lNone);
+	}
+
+	jpFriends.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " My Friends "), BorderFactory.createEmptyBorder(1, 8, 8, 5)));
+
+	return (jpFriends);
+  }
+
+  // generates a JPanel that displays a list of groups for current user
+  // when they are logged in
+  private JPanel createMyGroupsBox() {
+	JPanel jpGroups = new JPanel();
+
+	jpGroups.setLayout(new BoxLayout(jpGroups, BoxLayout.Y_AXIS));
+
+	// prepare the icon for groups
+	ImageIcon iconGroup = new ImageIcon(MyExperimentPerspective.getLocalIconURL(Resource.GROUP));
+
+	// iterate through all groups and add all to the panel
+	Iterator<HashMap<String, String>> iGroups = this.myExperimentClient.getCurrentUser().getGroups().iterator();
+	if (iGroups.hasNext()) {
+	  while (iGroups.hasNext()) {
+		HashMap<String, String> hmCurGroup = iGroups.next();
+		jpGroups.add(new JClickableLabel(hmCurGroup.get("name"), "preview:"
+			+ Resource.GROUP + ":" + hmCurGroup.get("uri"), pluginMainComponent.getPreviewBrowser(), iconGroup));
+	  }
+	} else {
+	  // known not to have any groups
+	  JLabel lNone = new JLabel("None");
+	  lNone.setFont(lNone.getFont().deriveFont(Font.ITALIC));
+	  lNone.setForeground(Color.GRAY);
+	  jpGroups.add(lNone);
+	}
+
+	jpGroups.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " My Groups "), BorderFactory.createEmptyBorder(1, 8, 8, 5)));
+
+	return (jpGroups);
+  }
+
+  // generates a JPanel that displays a list of favourite items for current user
+  // when they are logged in
+  private JPanel createMyFavouritesBox() {
+	JPanel jpFavourites = new JPanel();
+
+	jpFavourites.setLayout(new BoxLayout(jpFavourites, BoxLayout.Y_AXIS));
+	jpFavourites.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " My Favourites "), BorderFactory.createEmptyBorder(1, 8, 8, 5)));
+
+	return (jpFavourites);
+  }
+
+  public void repopulateFavouritesBox() {
+	this.jpMyFavouritesBox.removeAll();
+
+	// iterate through all favourites and add all to the panel
+	Iterator<Resource> iFavourites = this.myExperimentClient.getCurrentUser().getFavourites().iterator();
+	if (iFavourites.hasNext()) {
+	  while (iFavourites.hasNext()) {
+		Resource rFavourite = iFavourites.next();
+		this.jpMyFavouritesBox.add(new JClickableLabel(rFavourite.getTitle(), "preview:"
+			+ rFavourite.getItemType() + ":" + rFavourite.getURI(), pluginMainComponent.getPreviewBrowser(), new ImageIcon(MyExperimentPerspective.getLocalIconURL(rFavourite.getItemType()))));
+	  }
+	} else {
+	  // known not to have any favourites
+	  JLabel lNone = new JLabel("None");
+	  lNone.setFont(lNone.getFont().deriveFont(Font.ITALIC));
+	  lNone.setForeground(Color.GRAY);
+	  this.jpMyFavouritesBox.add(lNone);
+	}
+  }
+
+  // creates a Panel that shows all tags of the current user
+  private JPanel createMyTagsBox() {
+	JPanel jpTags = new JPanel();
+	jpTags.setLayout(new BoxLayout(jpTags, BoxLayout.Y_AXIS));
+
+	// prepare the icon for tags
+	ImageIcon iconTag = new ImageIcon(MyExperimentPerspective.getLocalIconURL(Resource.TAG));
+
+	// iterate through all tags and add all to the panel
+	Iterator<HashMap<String, String>> iTags = this.myExperimentClient.getCurrentUser().getTags().iterator();
+	if (iTags.hasNext()) {
+	  while (iTags.hasNext()) {
+		String strCurTag = iTags.next().get("name");
+		jpTags.add(new JClickableLabel(strCurTag, "tag:" + strCurTag, pluginMainComponent.getPreviewBrowser(), iconTag));
+	  }
+	} else {
+	  // known not to have any tags
+	  JLabel lNone = new JLabel("None");
+	  lNone.setFont(lNone.getFont().deriveFont(Font.ITALIC));
+	  lNone.setForeground(Color.GRAY);
+	  jpTags.add(lNone);
+	}
+
+	jpTags.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " My Tags "), BorderFactory.createEmptyBorder(1, 8, 8, 5)));
+
+	return (jpTags);
+  }
+
+  public JPanel getMyProfileBox() {
+	return (this.jpMyProfileBox);
+  }
+
+  // listener of button clicks in the sidebar
+  public void actionPerformed(ActionEvent e) {
+	if (e.getSource().equals(bLogout)) {
+	  // logout button was clicked
+
+	  try {
+		// "forget" login details
+		this.myExperimentClient.doLogout();
+	  } catch (Exception ex) {
+		logger.error("Error while trying to logout from myExperiment, exception:\n"
+			+ ex);
+	  }
+
+	  // repaint "myStuff" tab to display the login box again
+	  this.pluginMainComponent.getStatusBar().setStatus(this.pluginMainComponent.getMyStuffTab().getClass().getName(), "Logging out");
+	  this.pluginMainComponent.getMyStuffTab().createAndInitialiseInnerComponents();
+	  this.pluginMainComponent.getMyStuffTab().revalidate();
+	  this.pluginMainComponent.getMyStuffTab().repaint();
+	  this.pluginMainComponent.getStatusBar().setStatus(this.pluginMainComponent.getMyStuffTab().getClass().getName(), null);
+	  this.pluginMainComponent.getStatusBar().setCurrentUser(null);
+
+	  // remove "My Tags" from the tags browser tab and rerun last searches (tag
+	  // & keyword)
+	  // so that any "private" search results won't get shown anymore
+	  this.pluginMainComponent.getTagBrowserTab().setMyTagsShown(false);
+	  this.pluginMainComponent.getTagBrowserTab().getTagSearchResultPanel().clear();
+	  this.pluginMainComponent.getTagBrowserTab().rerunLastTagSearch();
+
+	  this.pluginMainComponent.getSearchTab().getSearchResultPanel().clear();
+	  this.pluginMainComponent.getSearchTab().rerunLastSearch();
+
+	  // TODO: also, update another tabs, so that they don't display any 'private' content?
+	} else if (e.getSource().equals(this.bUpload)) {
+	  JFrame containingFrame = (JFrame) SwingUtilities.windowForComponent(this);
+	  //	  UploadWorkflowDialog uploadWorkflowDialog = new UploadWorkflowDialog(containingFrame);
+
+	  //	  File workflowFile = null;
+	  //	  JFileChooser jfsSelectFile = new JFileChooser();
+	  //	  if (jfsSelectFile.showOpenDialog(this) == JFileChooser.APPROVE_OPTION)
+	  //		workflowFile = jfsSelectFile.getSelectedFile();
+	  //	  else
+	  //		return;
+
+	  UploadWorkflowDialog uploadWorkflowDialog = new UploadWorkflowDialog(containingFrame, true, fileManager);
+
+	  if (uploadWorkflowDialog.launchUploadDialogAndPostIfRequired())
+		// true was returned so  refresh the whole of the mystuff content panel
+		this.actionPerformed(new ActionEvent(this.bRefreshMyStuff, 0, ""));
+
+	}
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffTabContentPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffTabContentPanel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffTabContentPanel.java
new file mode 100644
index 0000000..56e2dcf
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/MyStuffTabContentPanel.java
@@ -0,0 +1,342 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.util.concurrent.CountDownLatch;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.workbench.file.FileManager;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs, Emmanuel Tagarira, Jiten Bhagat
+ */
+public class MyStuffTabContentPanel extends JPanel implements ActionListener, KeyListener {
+  private final MainComponent pluginMainComponent;
+  private final MyExperimentClient myExperimentClient;
+  private final Logger logger;
+
+  // components that should be accessible from anywhere in this class
+  private JButton bLogin;
+  private JCheckBox cbLoginAutomatically;
+  private MyStuffSidebarPanel jpSidebar;
+  public JSplitPane spMyStuff;
+
+  // synchronisation latch to let main thread know that all component-creation
+  // threads have been finished
+  protected CountDownLatch cdlComponentLoadingDone;
+  int NUMBER_OF_SUBCOMPONENTS = 2;
+
+  // "return to" type of thing, so that after a certain action has been done,
+  // it is possible to switch to another tab in this tabbed view
+  protected JComponent cTabContentComponentToSwitchToAfterLogin = null;
+  private final FileManager fileManager;
+
+  public MyStuffTabContentPanel(MainComponent component, MyExperimentClient client, Logger logger, FileManager fileManager) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the
+	// parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+	this.fileManager = fileManager;
+  }
+
+  public void createAndInitialiseInnerComponents() {
+	// if there are any components, these will be removed
+	this.removeAll();
+	cdlComponentLoadingDone = new CountDownLatch(NUMBER_OF_SUBCOMPONENTS);
+
+	// based on the current status (logged in / anonymous user), decide which
+	// components to create and display
+	if (this.myExperimentClient.isLoggedIn()) {
+	  jpSidebar = new MyStuffSidebarPanel(pluginMainComponent, myExperimentClient, logger, fileManager);
+	  JPanel jpSidebarContainer = new JPanel();
+	  jpSidebarContainer.setLayout(new BorderLayout());
+	  jpSidebarContainer.add(jpSidebar, BorderLayout.NORTH);
+	  JScrollPane spSidebar = new JScrollPane(jpSidebarContainer);
+	  spSidebar.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+	  spSidebar.setMinimumSize(new Dimension(jpSidebar.getMyProfileBox().getPreferredSize().width + 30, 0)); // +30
+	  // --> 10 for padding and 10 for vertical scroll bar + 10 extra current
+	  // user is logged in to myExperiment, display all personal data
+
+	  spMyStuff = new JSplitPane();
+	  spMyStuff.setLeftComponent(spSidebar);
+	  spMyStuff.setRightComponent(new MyStuffContributionsPanel(pluginMainComponent, myExperimentClient, logger));
+	  this.pluginMainComponent.getStatusBar().setCurrentUser(myExperimentClient.getCurrentUser().getName());
+
+	  // set proportional sizes of the two panes as 30/70 percents of the total
+	  // width of the SplitPane
+	  // this can only be done after the SplitPane is made visible - hence the
+	  // need for the listener below
+	  pluginMainComponent.addComponentListener(new ComponentAdapter() {
+		@Override
+		public void componentShown(ComponentEvent e) {
+		  javax.swing.JOptionPane.showMessageDialog(null, "component shown");
+		  // NB! This is only needed for use with test class, not when Taverna
+		  // calls perspective!!
+		  // the SplitPane wouldn't have loaded yet - wait until it does
+		  try {
+			Thread.sleep(50);
+		  } // 50ms is a tiny delay -- acceptable
+		  catch (Exception ex) { /* do nothing */}
+
+		  // set the proportions in the SplitPane
+		  spMyStuff.setDividerLocation(400);
+		}
+	  });
+
+	  // make sure that both panes will grow/shrink at the same rate if the
+	  // size of the whole SplitPane is changed by resizing the window
+	  spMyStuff.setResizeWeight(0.3);
+	  spMyStuff.setOneTouchExpandable(true);
+	  spMyStuff.setDividerLocation(400);
+	  spMyStuff.setDoubleBuffered(true);
+
+	  // spMyStuff will be the only component in the Panel
+	  this.setLayout(new BorderLayout());
+	  this.add(spMyStuff);
+
+	  // wait until two of the components finish loading and set the status to 'ready'
+	  // (done in a new thread so that this doesn't freeze the plugin window)
+	  new Thread("Waiting for myStuff data to load") {
+		@Override
+		public void run() {
+		  try {
+			cdlComponentLoadingDone.await();
+			pluginMainComponent.getStatusBar().setStatus(this.getClass().getName(), null);
+		  } catch (InterruptedException ex) { /* do nothing for now */
+		  }
+		}
+	  }.start();
+	} else { // NOT logged in
+	  // reset status in case of unsuccessful autologin
+	  this.pluginMainComponent.getStatusBar().setStatus(this.getClass().getName(), null);
+
+	  // user isn't logged in, display login box only
+	  JPanel jpLoginBoxContainer = new JPanel();
+	  jpLoginBoxContainer.setLayout(new GridBagLayout());
+	  GridBagConstraints c = new GridBagConstraints();
+	  jpLoginBoxContainer.add(createLoginBox(), c);
+
+	  // put everything together (welcome banner + login box)
+	  this.setLayout(new BorderLayout());
+	  this.add(new ShadedLabel("Welcome to the myExperiment plugin. Please note that you can still use other tabs even "
+		  + "if you don't have a user profile yet!", ShadedLabel.BLUE), BorderLayout.NORTH);
+	  this.add(jpLoginBoxContainer, BorderLayout.CENTER);
+	}
+  }
+
+  // generates JPanel containing a login box
+  private JPanel createLoginBox() {
+	JPanel jpLoginBox = new JPanel();
+	jpLoginBox.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(), BorderFactory.createEmptyBorder(10, 10, 10, 10)));
+
+	jpLoginBox.setLayout(new GridBagLayout());
+	GridBagConstraints c = new GridBagConstraints();
+
+	// label "Login to myExp"
+	c.gridwidth = GridBagConstraints.REMAINDER;
+	c.insets = new Insets(0, 0, 15, 0);
+	c.gridx = 0;
+	c.gridy = 0;
+	JLabel jlHeader = new JLabel("<html><b>Log in to myExperiment</b></html>");
+	jlHeader.setFont(jlHeader.getFont().deriveFont((float) 13.0));
+	jpLoginBox.add(jlHeader, c);
+
+	// set values
+	c.weightx = 1;
+	c.gridwidth = 1;
+	c.anchor = GridBagConstraints.LINE_START;
+	c.insets = new Insets(0, 0, 3, 0);
+	c.ipadx = 10;
+
+	// autologin checkbox and label
+	c.gridy++;
+	c.insets = new Insets(0, 0, 0, 3);
+	cbLoginAutomatically = new JCheckBox("Log in automatically (next time)");
+	cbLoginAutomatically.setBorder(BorderFactory.createEmptyBorder()); // makes sure that this is aligned with text fields above
+	cbLoginAutomatically.addActionListener(this);
+	cbLoginAutomatically.addKeyListener(this);
+	jpLoginBox.add(cbLoginAutomatically, c);
+
+	// login button
+	c.gridy++;
+	c.gridx = 0;
+	c.anchor = GridBagConstraints.CENTER;
+	c.gridwidth = GridBagConstraints.REMAINDER;
+	c.fill = GridBagConstraints.HORIZONTAL;
+	c.insets = new Insets(10, 0, 0, 0);
+	bLogin = new JButton("Login", new ImageIcon(MyExperimentPerspective.getLocalResourceURL("login_icon")));
+	bLogin.setDefaultCapable(true);
+	bLogin.addKeyListener(this);
+	bLogin.addActionListener(this);
+	jpLoginBox.add(bLogin, c);
+
+	// wrap contents into another panel to allow for some extra border around the contents
+	return (jpLoginBox);
+  }
+
+  public MyStuffSidebarPanel getSidebar() {
+	return (this.jpSidebar);
+  }
+
+  public void actionPerformed(ActionEvent e) {
+	if (e.getSource().equals(this.bLogin)) {
+	  // "Login" button clicked
+	  pluginMainComponent.getStatusBar().setStatus(this.getClass().getName(), "Logging in");
+
+	  // Make call to myExperiment API in a different thread
+	  // (then use SwingUtilities.invokeLater to update the UI when ready).
+	  new Thread("Login to myExperiment") {
+		@Override
+		public void run() {
+		  logger.debug("Logging in to myExperiment");
+
+		  try {
+			// do the actual "logging in"
+			boolean bLoginSuccessful = myExperimentClient.doLogin();
+
+			// check if need to store the login credentials and settings
+			if (bLoginSuccessful) {
+			  // store the settings anyway (for instance, to clear stored login/password
+			  // - when the 'remember me' tick is not checked anymore);
+			  // however, need to check whether to store login details or not
+
+			  myExperimentClient.getSettings().put(MyExperimentClient.INI_AUTO_LOGIN, new Boolean(cbLoginAutomatically.isSelected()).toString());
+			  myExperimentClient.storeHistoryAndSettings();
+
+			  // if logging in was successful, set the status to the start of fetching the data
+			  pluginMainComponent.getStatusBar().setStatus(this.getClass().getName(), "Fetching user data");
+
+			SwingUtilities.invokeLater(new Runnable() {
+			  public void run() {
+				if (myExperimentClient.isLoggedIn()) {
+				  // login successful, change view to "logged in" one
+				  createAndInitialiseInnerComponents();
+
+				  // ..also, load user's tag cloud
+				  pluginMainComponent.getTagBrowserTab().setMyTagsShown(true);
+				  pluginMainComponent.getTagBrowserTab().getMyTagPanel().refresh();
+
+				  // ..also, refresh tag search results because these my include
+				  // much more than
+				  // during the previous search when the user was still not
+				  // logged-in
+				  pluginMainComponent.getTagBrowserTab().rerunLastTagSearch();
+
+				  // ..also, refresh the keyword search results (as more items
+				  // can now be found)
+				  pluginMainComponent.getSearchTab().rerunLastSearch();
+
+				  // if after logging it is needed to switch to other tab,
+				  // that is done now
+				  if (cTabContentComponentToSwitchToAfterLogin != null) {
+					pluginMainComponent.getMainTabs().setSelectedComponent(cTabContentComponentToSwitchToAfterLogin);
+					cTabContentComponentToSwitchToAfterLogin = null;
+				  }
+
+				  logger.debug("Logged in to myExperiment successfully");
+				} else {
+				  // couldn't login - display error message
+				  pluginMainComponent.getStatusBar().setStatus(this.getClass().getName(), null);
+				  javax.swing.JOptionPane.showMessageDialog(null, "Unable to login to myExperiment - please check your login details", "myExperiment Plugin - Couldn't Login", JOptionPane.ERROR_MESSAGE);
+				}
+			  }
+			});
+			}
+
+		  } catch (Exception ex) {
+			logger.error("Exception on attempt to login to myExperiment:\n", ex);
+		  }
+		}
+	  }.start();
+
+	} else if (e.getSource().equals(this.jpSidebar.bRefreshMyStuff)) {
+	  // this will re-fetch all user profile data and repopulate the whole of the 'My Stuff' tab
+	  pluginMainComponent.getStatusBar().setStatus(this.getClass().getName(), "Refreshing user data");
+
+	  new Thread("Refreshing myStuff tab data") {
+		@Override
+		public void run() {
+		  // re-fetch user data first
+		  myExperimentClient.setCurrentUser(myExperimentClient.fetchCurrentUser(myExperimentClient.getCurrentUser().getURI()));
+		  createAndInitialiseInnerComponents();
+		  revalidate();
+		}
+	  }.start();
+	}
+  }
+
+  // *** Callbacks for KeyListener interface ***
+  public void keyPressed(KeyEvent e) {
+	// ENTER pressed - check which element is the source and determine what
+	// acion is to be taken
+	if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+	  if (e.getSource().equals(this.cbLoginAutomatically)
+		  || e.getSource().equals(this.bLogin)) {
+		// ENTER pressed when focus was on the login button, one of checkboxes or the password field - do logging in
+		actionPerformed(new ActionEvent(this.bLogin, 0, ""));
+	  }
+	}
+  }
+
+  public void keyReleased(KeyEvent e) {
+	// do nothing
+  }
+
+  public void keyTyped(KeyEvent e) {
+	// do nothing
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/PluginPreferencesDialog.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/PluginPreferencesDialog.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/PluginPreferencesDialog.java
new file mode 100644
index 0000000..2ad8a42
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/PluginPreferencesDialog.java
@@ -0,0 +1,372 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.util.ArrayList;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Util;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs, Emmanuel Tagarira
+ */
+public class PluginPreferencesDialog extends HelpEnabledDialog implements ComponentListener, ActionListener {
+  // CONSTANTS
+
+  // components for accessing application's main elements
+  private final MainComponent pluginMainComponent;
+  private final MyExperimentClient myExperimentClient;
+  private final Logger logger;
+
+  // COMPONENTS
+  private JTextField tfMyExperimentURL;
+  private JComboBox cbDefaultLoggedInTab;
+  private JComboBox cbDefaultNotLoggedInTab;
+  private JCheckBox cbMyStuffWorkflows;
+  private JCheckBox cbMyStuffFiles;
+  private JCheckBox cbMyStuffPacks;
+  private JButton bSave;
+  private JButton bCancel;
+  private JClickableLabel jclClearPreviewHistory;
+  private JClickableLabel jclClearSearchHistory;
+  private JClickableLabel jclClearFavouriteSearches;
+
+  // DATA STORAGE
+  private final Component[] pluginTabComponents;
+  private final ArrayList<String> alPluginTabComponentNames;
+
+  public PluginPreferencesDialog(JFrame owner, MainComponent component, MyExperimentClient client, Logger logger) {
+	super(owner, "Plugin preferences", true);
+
+	// set main variables to ensure access to myExperiment, logger and the parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	// set options of the preview dialog box
+	this.addComponentListener(this);
+	//this.setIconImage(new ImageIcon(MyExperimentPerspective.getLocalResourceURL("myexp_icon")).getImage());
+
+	// prepare plugin tab names to display in the UI afterwards
+	this.alPluginTabComponentNames = new ArrayList<String>();
+	this.pluginTabComponents = this.pluginMainComponent.getMainTabs().getComponents();
+	for (int i = 0; i < this.pluginTabComponents.length; i++) {
+	  alPluginTabComponentNames.add(this.pluginMainComponent.getMainTabs().getTitleAt(i));
+	}
+
+	this.initialiseUI();
+
+	// this is not computation-intensive method, so no need to run in a new thread
+	this.initialiseData();
+  }
+
+  private void initialiseUI() {
+	// this constraints instance will be shared among all components in the window
+	GridBagConstraints c = new GridBagConstraints();
+
+	// create the myExperiment API address box
+	JPanel jpApiLocation = new JPanel();
+	jpApiLocation.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " myExperiment Location "), BorderFactory.createEmptyBorder(0, 5, 5, 5)));
+	jpApiLocation.setLayout(new GridBagLayout());
+
+	c.gridx = 0;
+	c.gridy = 0;
+	c.weightx = 1.0;
+	c.anchor = GridBagConstraints.WEST;
+	jpApiLocation.add(new JLabel("Base URL of myExperiment instance to connect to"), c);
+
+	c.gridy = 1;
+	c.fill = GridBagConstraints.HORIZONTAL;
+	this.tfMyExperimentURL = new JTextField();
+	this.tfMyExperimentURL.setToolTipText("<html>Here you can specify the base URL of the myExperiment "
+		+ "instance that you wish to connect to.<br>This allows the plugin to connect not only to the "
+		+ "<b>main myExperiment website</b> (default value:<br><b>http://www.myexperiment.org</b>) but "
+		+ "also to any other myExperiment instance that might<br>exist elsewhere.<br><br>It is recommended "
+		+ "that you only change this setting if you are certain in your actions.</html>");
+	jpApiLocation.add(this.tfMyExperimentURL, c);
+
+	// create startup tab choice box
+	JPanel jpStartupTabChoice = new JPanel();
+	jpStartupTabChoice.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " Plugin Start-up Settings "), BorderFactory.createEmptyBorder(0, 5, 5, 5)));
+	jpStartupTabChoice.setLayout(new GridBagLayout());
+
+	c.gridx = 0;
+	c.gridy = 0;
+	c.weightx = 0;
+	c.insets = new Insets(0, 0, 0, 10);
+	jpStartupTabChoice.add(new JLabel("Default startup tab for anonymous user"), c);
+
+	c.gridx = 1;
+	c.weightx = 1.0;
+	c.insets = new Insets(0, 0, 2, 0);
+	this.cbDefaultNotLoggedInTab = new JComboBox(this.alPluginTabComponentNames.toArray());
+	this.cbDefaultNotLoggedInTab.setToolTipText("<html>This tab will be automatically opened at plugin start up time if you are <b>not</b> logged id to myExperiment.</html>");
+	jpStartupTabChoice.add(this.cbDefaultNotLoggedInTab, c);
+
+	c.gridx = 0;
+	c.gridy = 1;
+	c.weightx = 0;
+	c.insets = new Insets(0, 0, 0, 10);
+	jpStartupTabChoice.add(new JLabel("Default startup tab after successful auto-login"), c);
+
+	c.gridx = 1;
+	c.weightx = 1.0;
+	c.insets = new Insets(2, 0, 0, 0);
+	this.cbDefaultLoggedInTab = new JComboBox(this.alPluginTabComponentNames.toArray());
+	this.cbDefaultLoggedInTab.setToolTipText("<html>This tab will be automatically opened at plugin start up time if you have chosen to use <b>auto logging in</b> to myExperiment.</html>");
+	jpStartupTabChoice.add(this.cbDefaultLoggedInTab, c);
+
+	// create 'my stuff' tab preference box
+	JPanel jpMyStuffPrefs = new JPanel();
+	jpMyStuffPrefs.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " 'My Stuff' Tab Settings "), BorderFactory.createEmptyBorder(0, 5, 5, 5)));
+	jpMyStuffPrefs.setLayout(new GridBagLayout());
+
+	c.gridx = 0;
+	c.gridy = 0;
+	c.weightx = 0;
+	c.insets = new Insets(0, 0, 0, 0);
+	jpMyStuffPrefs.add(new JLabel("Sections to show in this tab:"), c);
+
+	c.gridx = 1;
+	c.gridy = 0;
+	c.weightx = 1.0;
+	c.insets = new Insets(0, 10, 0, 0);
+	this.cbMyStuffWorkflows = new JCheckBox("My Workflows");
+	jpMyStuffPrefs.add(this.cbMyStuffWorkflows, c);
+
+	c.gridy = 1;
+	this.cbMyStuffFiles = new JCheckBox("My Files");
+	jpMyStuffPrefs.add(this.cbMyStuffFiles, c);
+
+	c.gridy = 2;
+	this.cbMyStuffPacks = new JCheckBox("My Packs");
+	jpMyStuffPrefs.add(this.cbMyStuffPacks, c);
+
+	// create privacy settings box
+	JPanel jpPrivacySettings = new JPanel();
+	jpPrivacySettings.setLayout(new BoxLayout(jpPrivacySettings, BoxLayout.Y_AXIS));
+	jpPrivacySettings.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " Privacy Settings "), BorderFactory.createEmptyBorder(0, 7, 5, 5)));
+
+	this.jclClearPreviewHistory = new JClickableLabel("Clear browsing history", "clear_preview_history", this);
+	jpPrivacySettings.add(this.jclClearPreviewHistory);
+
+	this.jclClearSearchHistory = new JClickableLabel("Clear search history", "clear_search_history", this);
+	this.jclClearSearchHistory.setBorder(BorderFactory.createEmptyBorder(3, 0, 3, 0));
+	jpPrivacySettings.add(this.jclClearSearchHistory);
+
+	this.jclClearFavouriteSearches = new JClickableLabel("Clear favourite searches", "clear_favourite_searches", this);
+	this.jclClearFavouriteSearches.setBorder(BorderFactory.createEmptyBorder(0, 0, 2, 0));
+	jpPrivacySettings.add(this.jclClearFavouriteSearches);
+
+	// create button panel
+	this.bSave = new JButton("Save");
+	this.bSave.addActionListener(this);
+
+	this.bCancel = new JButton("Cancel");
+	this.bCancel.addActionListener(this);
+
+	JPanel jpButtons = new JPanel();
+	jpButtons.setLayout(new GridBagLayout());
+
+	c.gridx = 0;
+	c.gridy = 0;
+	c.weightx = 0;
+	c.insets = new Insets(0, 0, 0, 2);
+	jpButtons.add(bSave, c);
+
+	c.gridx = 1;
+	c.insets = new Insets(0, 2, 0, 0);
+	jpButtons.add(bCancel, c);
+
+	// PUT EVERYTHING TOGETHER
+	this.setTitle("myExperiment Plugin Preferences");
+	BorderLayout layout = new BorderLayout();
+	JPanel jpEverything = new JPanel();
+	GridBagLayout jpEverythingLayout = new GridBagLayout();
+	jpEverything.setLayout(jpEverythingLayout);
+	this.getContentPane().setLayout(layout);
+
+	GridBagConstraints gbConstraints = new GridBagConstraints();
+	gbConstraints.fill = GridBagConstraints.BOTH;
+	gbConstraints.weightx = 1;
+	gbConstraints.gridx = 0;
+
+	gbConstraints.gridy = 0;
+	jpEverything.add(jpApiLocation, gbConstraints);
+
+	gbConstraints.gridy = 1;
+	jpEverything.add(jpStartupTabChoice, gbConstraints);
+
+	gbConstraints.gridy = 2;
+	jpEverything.add(jpMyStuffPrefs, gbConstraints);
+
+	gbConstraints.gridy = 3;
+	jpEverything.add(jpPrivacySettings, gbConstraints);
+
+	gbConstraints.gridy = 4;
+	jpEverything.add(jpButtons, gbConstraints);
+
+	this.add(jpEverything);
+	this.setResizable(false);
+
+	// pack() sets preferred size of the dialog box;
+	// after this, can set the minimum size to that value too
+	this.pack();
+	this.setMinimumSize(this.getPreferredSize());
+  }
+
+  private void initialiseData() {
+	// myExperiment Base URL
+	this.tfMyExperimentURL.setText(myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_BASE_URL));
+
+	// default tabs
+	this.cbDefaultNotLoggedInTab.setSelectedIndex(Integer.parseInt(myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_DEFAULT_ANONYMOUS_TAB)));
+	this.cbDefaultLoggedInTab.setSelectedIndex(Integer.parseInt(myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_DEFAULT_LOGGED_IN_TAB)));
+
+	// components of "My Stuff" tab
+	this.cbMyStuffWorkflows.setSelected(Boolean.parseBoolean(myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_MY_STUFF_WORKFLOWS)));
+	this.cbMyStuffFiles.setSelected(Boolean.parseBoolean(myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_MY_STUFF_FILES)));
+	this.cbMyStuffPacks.setSelected(Boolean.parseBoolean(myExperimentClient.getSettings().getProperty(MyExperimentClient.INI_MY_STUFF_PACKS)));
+  }
+
+  // *** Callbacks for ComponentListener interface ***
+
+  public void componentShown(ComponentEvent e) {
+	// every time the settings window is shown, make sure that the dialog box appears
+	// centered horizontally and vertically relatively to the main component
+	Util.centerComponentWithinAnother(this.pluginMainComponent, this);
+
+	// also, need to make sure that correct settings get shown
+	// (e.g. especially relevant when this window was last closed with 'cancel',
+	//  but some options were changed prior to that)
+	this.initialiseData();
+  }
+
+  public void componentHidden(ComponentEvent e) {
+	// do nothing
+  }
+
+  public void componentResized(ComponentEvent e) {
+	// do nothing
+  }
+
+  public void componentMoved(ComponentEvent e) {
+	// do nothing
+  }
+
+  // *** Callback for ActionListener interface ***
+
+  public void actionPerformed(ActionEvent e) {
+	if (e.getSource().equals(this.bSave)) {
+	  // check if myExperiment address is present
+	  String strNewMyExperimentURL = this.tfMyExperimentURL.getText().trim();
+	  if (strNewMyExperimentURL.length() == 0) {
+		javax.swing.JOptionPane.showMessageDialog(null, "Please specify a base URL of myExperiment instance that you wish to connect to", "Error", JOptionPane.WARNING_MESSAGE);
+		this.tfMyExperimentURL.requestFocusInWindow();
+		return;
+	  }
+
+	  // check if at least one of the checkboxes (for sections in 'My Stuff' tab) is selected
+	  if (!(this.cbMyStuffWorkflows.isSelected()
+		  || this.cbMyStuffFiles.isSelected() || this.cbMyStuffPacks.isSelected())) {
+		javax.swing.JOptionPane.showMessageDialog(null, "Please choose at least one section to display in 'My Stuff' tab", "Error", JOptionPane.WARNING_MESSAGE);
+		this.cbMyStuffWorkflows.requestFocusInWindow();
+		return;
+	  }
+
+	  // NB! changed myExperiment location will not take action until the next application restart
+	  if (!strNewMyExperimentURL.equals(myExperimentClient.getBaseURL())) {
+		// turn off auto-login
+		myExperimentClient.getSettings().put(MyExperimentClient.INI_AUTO_LOGIN, new Boolean(false).toString());
+
+		javax.swing.JOptionPane.showMessageDialog(null, "You have selected a new Base URL for myExperiment. "
+			+ "Your new setting has been saved,\nbut will not take effect until you restart Taverna.\n\n"
+			+ "Auto-login feature has been switched off for you to check the login details at the next launch.", "myExperiment Plugin - Info", JOptionPane.INFORMATION_MESSAGE);
+	  }
+
+	  // all values should be present - store these into Properties object
+	  myExperimentClient.getSettings().put(MyExperimentClient.INI_BASE_URL, strNewMyExperimentURL);
+	  myExperimentClient.getSettings().put(MyExperimentClient.INI_DEFAULT_ANONYMOUS_TAB, new Integer(cbDefaultNotLoggedInTab.getSelectedIndex()).toString());
+	  myExperimentClient.getSettings().put(MyExperimentClient.INI_DEFAULT_LOGGED_IN_TAB, new Integer(cbDefaultLoggedInTab.getSelectedIndex()).toString());
+	  myExperimentClient.getSettings().put(MyExperimentClient.INI_MY_STUFF_WORKFLOWS, new Boolean(cbMyStuffWorkflows.isSelected()).toString());
+	  myExperimentClient.getSettings().put(MyExperimentClient.INI_MY_STUFF_FILES, new Boolean(cbMyStuffFiles.isSelected()).toString());
+	  myExperimentClient.getSettings().put(MyExperimentClient.INI_MY_STUFF_PACKS, new Boolean(cbMyStuffPacks.isSelected()).toString());
+
+	  // close the window eventually
+	  setVisible(false);
+	} else if (e.getSource().equals(this.bCancel)) {
+	  // simply close the preferences window
+	  setVisible(false);
+	} else if (e.getSource().equals(this.jclClearPreviewHistory)) {
+	  // request user confirmation and clear browsing history (preview history)
+	  if (JOptionPane.showConfirmDialog(null, "This will delete the browsing history - the lists of previously previewed,\n"
+		  + "downloaded, opened and commented on items will be emptied.\n\nDo you want to proceed?", "myExperiment Plugin - Confirmation Required", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
+		pluginMainComponent.getPreviewBrowser().clearPreviewHistory();
+		pluginMainComponent.getHistoryBrowser().clearDownloadedItemsHistory();
+		pluginMainComponent.getHistoryBrowser().clearOpenedItemsHistory();
+		pluginMainComponent.getHistoryBrowser().clearCommentedOnItemsHistory();
+		pluginMainComponent.getHistoryBrowser().refreshHistoryBox(HistoryBrowserTabContentPanel.PREVIEWED_ITEMS_HISTORY);
+		pluginMainComponent.getHistoryBrowser().refreshHistoryBox(HistoryBrowserTabContentPanel.DOWNLOADED_ITEMS_HISTORY);
+		pluginMainComponent.getHistoryBrowser().refreshHistoryBox(HistoryBrowserTabContentPanel.OPENED_ITEMS_HISTORY);
+		pluginMainComponent.getHistoryBrowser().refreshHistoryBox(HistoryBrowserTabContentPanel.COMMENTED_ON_ITEMS_HISTORY);
+	  }
+	} else if (e.getSource().equals(this.jclClearSearchHistory)) {
+	  // request user confirmation and clear search history (tag search history + query search history)
+	  if (JOptionPane.showConfirmDialog(null, "This will delete both query and tag search history.\nDo you want to proceed?", "myExperiment Plugin - Confirmation Required", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
+		pluginMainComponent.getSearchTab().getSearchHistory().clear();
+		pluginMainComponent.getTagBrowserTab().getTagSearchHistory().clear();
+		pluginMainComponent.getSearchTab().updateSearchHistory();
+		pluginMainComponent.getHistoryBrowser().refreshSearchHistory();
+		pluginMainComponent.getHistoryBrowser().refreshTagSearchHistory();
+	  }
+	} else if (e.getSource().equals(this.jclClearFavouriteSearches)) {
+	  // request user confirmation and clear favourite searches
+	  if (JOptionPane.showConfirmDialog(null, "This will delete all your favourite search settings.\nDo you want to proceed?", "myExperiment Plugin - Confirmation Required", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
+		pluginMainComponent.getSearchTab().getSearchFavouritesList().clear();
+		pluginMainComponent.getSearchTab().updateFavouriteSearches();
+	  }
+	}
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/PluginStatusBar.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/PluginStatusBar.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/PluginStatusBar.java
new file mode 100644
index 0000000..0d68811
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/PluginStatusBar.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Util;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs, Emmanuel Tagarira
+ */
+public class PluginStatusBar extends JPanel implements ActionListener {
+  // CONSTANTS
+  private static final String STATUS_MESSAGE_READY = "Ready";
+
+  private MainComponent pluginMainComponent;
+  private MyExperimentClient myExperimentClient;
+  private Logger logger;
+
+  // all components that represent the status
+  private JLabel lSpinnerIcon;
+  private JLabel lStatusMsg;
+  private JLabel lCurrentUser;
+  //  private JButton bPreferences;
+
+  // collections to keep the statuses for different tabs
+  ArrayList<String> alTabClassNames;
+  ArrayList<String> alTabStatuses;
+
+  // spinner icons
+  ImageIcon iconSpinner;
+  ImageIcon iconSpinnerStopped;
+
+  public PluginStatusBar(MainComponent component, MyExperimentClient client, Logger logger) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the
+	// parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	// prepare status collections for different tabs
+	alTabClassNames = new ArrayList<String>();
+	alTabStatuses = new ArrayList<String>();
+
+	// load icons
+	this.iconSpinner = new ImageIcon(MyExperimentPerspective.getLocalResourceURL("spinner"));
+	this.iconSpinnerStopped = new ImageIcon(MyExperimentPerspective.getLocalResourceURL("spinner_stopped"));
+
+	// prepare main panel for the status bar
+	this.setLayout(new BorderLayout());
+	this.setBorder(BorderFactory.createEmptyBorder(1, 4, 1, 1)); // this will
+	// add a bit
+	// more spacing
+	// on the left
+	// - before the
+	// status
+	// message
+
+	// prepare status labels
+	this.lSpinnerIcon = new JLabel("", iconSpinnerStopped, SwingConstants.LEFT);
+	this.lStatusMsg = new JLabel("Ready");
+	this.lCurrentUser = new JLabel("Please log in to access your profile", SwingConstants.CENTER);
+
+	// 'Plugin Preferences' button
+	//	this.bPreferences = new JButton("Plugin Preferences", WorkbenchIcons.configureIcon);
+	//	this.bPreferences.addActionListener(this);
+
+	// put everything together
+	JPanel pWestStatusBarSection = new JPanel();
+	pWestStatusBarSection.add(lSpinnerIcon);
+	pWestStatusBarSection.add(lStatusMsg);
+
+	this.add(pWestStatusBarSection, BorderLayout.WEST);
+	this.add(this.lCurrentUser, BorderLayout.EAST);
+	//	this.add(this.bPreferences, BorderLayout.EAST);
+  }
+
+  // updates the current user name in the middle of the status bar
+  public void setCurrentUser(String strUsername) {
+	// if "null" or "" is submitted as a parameter, the status will be set to
+	// "Ready"
+	if (strUsername == null || strUsername.length() == 0)
+	  this.lCurrentUser.setText("Please log in to access your profile");
+	else
+	  this.lCurrentUser.setText("<html>Logged in as <b>" + strUsername
+		  + "</b></html>");
+  }
+
+  // sets the status message to the one that is relevant to the current tab
+  public void displayStatus(String strTabClassName) {
+	int iTabIdx = -1;
+	String strBaseClassName = Util.getBaseClassName(strTabClassName);
+
+	if ((iTabIdx = alTabClassNames.indexOf(strBaseClassName)) != -1) {
+	  // tab found - show its status message
+	  String strCurStatus = alTabStatuses.get(iTabIdx);
+	  this.lStatusMsg.setText(strCurStatus);
+	  startSpinner(!strCurStatus.equals(PluginStatusBar.STATUS_MESSAGE_READY));
+	} else {
+	  // tab not found - assume no actions are happening
+	  // (this will create the 'ready' status for the current tab,
+	  // then return to display it)
+	  setStatus(strBaseClassName, null);
+	}
+  }
+
+  // sets the status message for a particular tab;
+  // if this tab is currently active, the status will get displayed immediately
+  // (alternatively it will be displayed at the time when the tab becomes
+  // active)
+  public void setStatus(String strTabClassName, String strStatus) {
+	// PREPROCESSING - if "null" or "" is submitted as a parameter, the status
+	// will be set to "Ready"
+	if (strStatus == null || strStatus.length() == 0)
+	  strStatus = PluginStatusBar.STATUS_MESSAGE_READY;
+	String strBaseClassName = Util.getBaseClassName(strTabClassName);
+
+	// STORING the status it in the collection
+	int iTabIdx = -1;
+	if ((iTabIdx = alTabClassNames.indexOf(strBaseClassName)) != -1) {
+	  // only a change of status, already dealt with this tab before
+	  alTabStatuses.set(iTabIdx, strStatus);
+	} else {
+	  // never worked with this tab before, add new one
+	  alTabClassNames.add(strBaseClassName);
+	  alTabStatuses.add(strStatus);
+	}
+
+	// display the new status if the updated status is on the active tab
+	if (isTabActive(strBaseClassName))
+	  displayStatus(strBaseClassName);
+  }
+
+  // helper to start / stop the spinner in the status bar that
+  // indicates that some action is currently in progress
+  // (action will be displayed by lStatusMsg label)
+  public void startSpinner(boolean bStart) {
+	this.lSpinnerIcon.setIcon(bStart ? this.iconSpinner : this.iconSpinnerStopped);
+  }
+
+  // Determine whether the tab in the parameter is currently active in the main
+  // tabbed pane.
+  private boolean isTabActive(String strTabClassName) {
+	// get the current active tab (this is a normal class name of the main tab
+	// content component)
+	String strCurSelectedTabClassName = this.pluginMainComponent.getMainTabs().getSelectedComponent().getClass().getName();
+
+	// get the real class name to match
+	String strBaseClassName = Util.getBaseClassName(strTabClassName);
+
+	// compare the two class names
+	return (strBaseClassName.equals(strCurSelectedTabClassName));
+  }
+
+  public void actionPerformed(ActionEvent e) {
+	//	if (e.getSource().equals(this.bPreferences)) {
+	//	  // open preferences dialog box
+	//	  pluginMainComponent.getPreferencesDialog().setVisible(true);
+	//	}
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourceListPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourceListPanel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourceListPanel.java
new file mode 100644
index 0000000..d0b00a3
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourceListPanel.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.net.URI;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs, Emmanuel Tagarira, Jiten Bhagat
+ */
+public class ResourceListPanel extends JPanel implements HyperlinkListener {
+  // CONSTANTS
+  public final static int DESCRIPTION_TRUNCATE_LENGTH_FOR_SHORT_LIST_VIEW = 150;
+
+  public final static int THUMBNAIL_WIDTH_FOR_SHORT_LIST_VIEW = 60;
+  public final static int THUMBNAIL_HEIGHT_FOR_SHORT_LIST_VIEW = 45;
+
+  public final static int THUMBNAIL_WIDTH_FOR_FULL_LIST_VIEW = 90;
+  public final static int THUMBNAIL_HEIGHT_FOR_FULL_LIST_VIEW = 90;
+
+  private MainComponent pluginMainComponent;
+  private MyExperimentClient myExperimentClient;
+  private Logger logger;
+
+  private JPanel listPanel;
+  private GridBagConstraints gbConstraints;
+
+  private List<Resource> listItems;
+
+  // some of the components will not be shown in the item list if
+  // it's not of full size
+  private boolean bFullSizeItemsList = true;
+
+  public ResourceListPanel(MainComponent component, MyExperimentClient client, Logger logger) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the
+	// parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	this.initialiseUI();
+  }
+
+  public boolean isFullSizeItemsList() {
+	return this.bFullSizeItemsList;
+  }
+
+  public void setFullSizeItemsList(boolean bFullSizeItemsList) {
+	this.bFullSizeItemsList = bFullSizeItemsList;
+  }
+
+  public void hyperlinkUpdate(HyperlinkEvent e) {
+	try {
+	  if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+		String strAction = e.getDescription().toString();
+
+		if (strAction.startsWith("preview:")) {
+		  this.pluginMainComponent.getPreviewBrowser().preview(strAction);
+		} else {
+		    Desktop.getDesktop().browse(new URI(strAction));
+		}
+	  }
+	} catch (Exception ex) {
+	  logger.error("Error occurred whilst clicking a hyperlink", ex);
+	}
+  }
+
+  public void setListItems(List<Resource> items) {
+	this.listItems = items;
+
+	this.repopulate();
+  }
+
+  public void clear() {
+	this.listPanel.removeAll();
+	this.invalidate();
+  }
+
+  public void refresh() {
+	if (this.listItems != null) {
+	  this.repopulate();
+	}
+  }
+
+  public void repopulate() {
+	if (this.listItems != null) {
+	  this.clear();
+
+	  if (this.listItems.size() > 0) {
+		Resource res = null;
+		for (int i = 0; i < this.listItems.size(); i++) {
+		  try {
+			// this will make the layout manager to push all extra space in
+			// Y-axis
+			// to go to the last element in the panel; essentially, this will
+			// push
+			// all list items to the top of the list view panel
+			if (i == listItems.size() - 1)
+			  gbConstraints.weighty = 1.0;
+
+			res = this.listItems.get(i);
+			this.listPanel.add(res.createListViewPanel(bFullSizeItemsList, pluginMainComponent, this, logger), gbConstraints);
+			logger.debug("Added entry in resource list panel for the resource (Type: "
+				+ res.getItemTypeName() + ", URI: " + res.getURI() + ")");
+		  } catch (Exception e) {
+			logger.error("Failed to add item entry to ResourceListPanel (Item Type : "
+				+ res.getItemTypeName() + ", URI: " + res.getURI() + ")", e);
+		  }
+		}
+	  } else {
+		// no items in the list
+		JLabel lNone = new JLabel("None");
+		lNone.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
+		lNone.setForeground(Color.GRAY);
+		lNone.setFont(lNone.getFont().deriveFont(Font.ITALIC));
+
+		gbConstraints.anchor = GridBagConstraints.WEST;
+		this.listPanel.add(lNone, gbConstraints);
+
+		this.listPanel.setPreferredSize(new Dimension(20, 40));
+		this.listPanel.setBackground(Color.WHITE);
+	  }
+	}
+
+	this.validate();
+	this.repaint();
+  }
+
+  private void initialiseUI() {
+	this.listPanel = new JPanel();
+	this.listPanel.setBorder(BorderFactory.createEmptyBorder());
+
+	this.listPanel.setLayout(new GridBagLayout());
+	this.gbConstraints = new GridBagConstraints();
+	this.gbConstraints.anchor = GridBagConstraints.NORTH;
+	this.gbConstraints.fill = GridBagConstraints.HORIZONTAL;
+	this.gbConstraints.gridx = GridBagConstraints.REMAINDER;
+	this.gbConstraints.gridy = GridBagConstraints.RELATIVE;
+	this.gbConstraints.weightx = 1.0;
+	this.gbConstraints.weighty = 0;
+
+	this.setLayout(new BorderLayout());
+	this.add(this.listPanel, BorderLayout.CENTER);
+  }
+}


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

Posted by st...@apache.org.
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/GraphElementTest.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphElementTest.java b/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphElementTest.java
new file mode 100644
index 0000000..8d6b7f8
--- /dev/null
+++ b/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphElementTest.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * 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 java.awt.Color;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphElement.LineStyle;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class GraphElementTest {
+
+	private GraphElement element;
+
+	private String id;
+
+	private String label;
+
+	private LineStyle lineStyle;
+
+	private Color color;
+
+	private Color fillColor;
+
+	private GraphElement parent;
+
+	private GraphController graphController;
+
+	@Before
+	public void setUp() throws Exception {
+		element = new GraphElement(graphController) {};
+		id = "element-id";
+		label = "element-label";
+		lineStyle = LineStyle.NONE;
+		color = Color.BLUE;
+		fillColor = Color.GREEN;
+		parent = new GraphNode(graphController);
+		element.setId(id);
+		element.setLabel(label);
+		element.setLineStyle(lineStyle);
+		element.setColor(color);
+		element.setFillColor(fillColor);
+		element.setParent(parent);
+	}
+
+	@Test
+	public void testGetParent() {
+		assertEquals(parent, element.getParent());
+	}
+
+	@Test
+	public void testSetParent() {
+		GraphNode newParent = new GraphNode(graphController);
+		element.setParent(newParent);
+		assertEquals(newParent, element.getParent());
+		element.setParent(null);
+		assertNull(element.getParent());
+	}
+
+	@Test
+	public void testGetLabel() {
+		assertEquals(label, element.getLabel());
+	}
+
+	@Test
+	public void testSetLabel() {
+		element.setLabel("new-label");
+		assertEquals("new-label", element.getLabel());
+		element.setLabel(null);
+		assertNull(element.getLabel());
+	}
+
+	@Test
+	public void testGetId() {
+		assertEquals(id, element.getId());
+	}
+
+	@Test
+	public void testSetId() {
+		element.setId("new-id");
+		assertEquals("new-id", element.getId());
+		element.setId(null);
+		assertNull(element.getId());
+	}
+
+	@Test
+	public void testGetColor() {
+		assertEquals(color, element.getColor());
+	}
+
+	@Test
+	public void testSetColor() {
+		element.setColor(Color.RED);
+		assertEquals(Color.RED, element.getColor());
+		element.setColor(null);
+		assertNull(element.getColor());
+	}
+
+	@Test
+	public void testGetFillColor() {
+		assertEquals(fillColor, element.getFillColor());
+	}
+
+	@Test
+	public void testSetFillColor() {
+		element.setFillColor(Color.RED);
+		assertEquals(Color.RED, element.getFillColor());
+		element.setFillColor(null);
+		assertNull(element.getFillColor());
+	}
+
+	@Test
+	public void testGetLineStyle() {
+		assertEquals(lineStyle, element.getLineStyle());
+	}
+
+	@Test
+	public void testSetLineStyle() {
+		element.setLineStyle(LineStyle.DOTTED);
+		assertEquals(LineStyle.DOTTED, element.getLineStyle());
+		element.setLineStyle(null);
+		assertNull(element.getLineStyle());
+	}
+
+}

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/GraphNodeTest.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphNodeTest.java b/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphNodeTest.java
new file mode 100644
index 0000000..c5bcd6c
--- /dev/null
+++ b/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphNodeTest.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * 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.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.awt.Dimension;
+
+import net.sf.taverna.t2.workbench.models.graph.GraphShapeElement.Shape;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class GraphNodeTest {
+
+	private GraphNode node;
+
+	private Shape shape;
+
+	private Dimension size;
+
+	private Graph graph;
+
+	private boolean expanded;
+
+	private GraphController graphController;
+
+	@Before
+	public void setUp() throws Exception {
+		shape = Shape.HOUSE;
+		size = new Dimension(1, 2);
+		graph = new Graph(graphController);
+		expanded = false;
+		node = new GraphNode(graphController);
+		node.setShape(shape);
+		node.setSize(size);
+		node.setGraph(graph);
+		node.setExpanded(expanded);
+	}
+
+	@Test
+	public void testNode() {
+		assertNotNull(new GraphNode(graphController));
+	}
+
+	@Test
+	public void testAddSinkNode() {
+		GraphNode newNode = new GraphNode(graphController);
+		node.addSinkNode(newNode);
+		assertEquals(1, node.getSinkNodes().size());
+		assertTrue(node.getSinkNodes().contains(newNode));
+		assertEquals(node, newNode.getParent());
+	}
+
+	@Test
+	public void testAddSourceNode() {
+		GraphNode newNode = new GraphNode(graphController);
+		node.addSourceNode(newNode);
+		assertEquals(1, node.getSourceNodes().size());
+		assertTrue(node.getSourceNodes().contains(newNode));
+		assertEquals(node, newNode.getParent());
+	}
+
+	@Test
+	public void testGetGraph() {
+		assertEquals(graph, node.getGraph());
+	}
+
+	@Test
+	public void testGetHeight() {
+		assertEquals(size.height, node.getHeight(), 0);
+	}
+
+	@Test
+	public void testGetShape() {
+		assertEquals(shape, node.getShape());
+	}
+
+	@Test
+	public void testGetSinkNodes() {
+		assertNotNull(node.getSinkNodes());
+		assertEquals(0, node.getSinkNodes().size());
+	}
+
+	@Test
+	public void testGetSize() {
+		assertEquals(size, node.getSize());
+	}
+
+	@Test
+	public void testGetSourceNodes() {
+		assertNotNull(node.getSourceNodes());
+		assertEquals(0, node.getSourceNodes().size());
+	}
+
+	@Test
+	public void testGetWidth() {
+		assertEquals(size.width, node.getWidth(), 0);
+	}
+
+	@Test
+	public void testIsExpanded() {
+		assertEquals(expanded, node.isExpanded());
+	}
+
+	@Test
+	public void testRemoveSinkNode() {
+		GraphNode newNode = new GraphNode(graphController);
+		assertFalse(node.removeSinkNode(newNode));
+		node.addSinkNode(newNode);
+		assertTrue(node.removeSinkNode(newNode));
+		assertFalse(node.getSinkNodes().contains(newNode));
+	}
+
+	@Test
+	public void testRemoveSourceNode() {
+		GraphNode newNode = new GraphNode(graphController);
+		assertFalse(node.removeSourceNode(newNode));
+		node.addSourceNode(newNode);
+		assertTrue(node.removeSourceNode(newNode));
+		assertFalse(node.getSourceNodes().contains(newNode));
+	}
+
+	@Test
+	public void testSetExpanded() {
+		node.setExpanded(true);
+		assertEquals(true, node.isExpanded());
+		node.setExpanded(false);
+		assertEquals(false, node.isExpanded());
+	}
+
+	@Test
+	public void testSetGraph() {
+		Graph newGraph = new Graph(graphController);
+		node.setGraph(newGraph);
+		assertEquals(newGraph, node.getGraph());
+		node.setGraph(null);
+		assertNull(node.getGraph());
+	}
+
+	@Test
+	public void testSetShape() {
+		node.setShape(Shape.INVTRIANGLE);
+		assertEquals(Shape.INVTRIANGLE, node.getShape());
+		node.setShape(Shape.TRIANGLE);
+		assertEquals(Shape.TRIANGLE, node.getShape());
+	}
+
+	@Test
+	public void testSetSize() {
+		node.setSize(new Dimension(23, 6));
+		assertEquals(new Dimension(23, 6), node.getSize());
+		node.setSize(new Dimension(14, 4));
+		assertEquals(new Dimension(14, 4), node.getSize());
+	}
+
+}

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/GraphTest.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphTest.java b/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphTest.java
new file mode 100644
index 0000000..44a5aaf
--- /dev/null
+++ b/taverna-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphTest.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * 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.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import net.sf.taverna.t2.workbench.models.graph.GraphEdge;
+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.Graph.Alignment;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class GraphTest {
+
+	private Graph graph;
+
+	private Alignment alignment;
+
+	private GraphController graphController;
+
+	@Before
+	public void setUp() throws Exception {
+		alignment = Alignment.VERTICAL;
+		graph = new Graph(graphController);
+	}
+
+	@Test
+	public void testGraph() {
+		assertNotNull(new Graph(graphController));
+	}
+
+	@Test
+	public void testAddEdge() {
+		GraphEdge newEdge = new GraphEdge(graphController);
+		graph.addEdge(newEdge);
+		assertEquals(1, graph.getEdges().size());
+		assertTrue(graph.getEdges().contains(newEdge));
+	}
+
+	@Test
+	public void testAddNode() {
+		GraphNode newNode = new GraphNode(graphController);
+		graph.addNode(newNode);
+		assertEquals(1, graph.getNodes().size());
+		assertTrue(graph.getNodes().contains(newNode));
+		assertEquals(graph, newNode.getParent());
+	}
+
+	@Test
+	public void testAddSubgraph() {
+		Graph newGraph = new Graph(graphController);
+		graph.addSubgraph(newGraph);
+		assertEquals(1, graph.getSubgraphs().size());
+		assertTrue(graph.getSubgraphs().contains(newGraph));
+		assertEquals(graph, newGraph.getParent());
+	}
+
+	@Test
+	public void testGetAlignment() {
+		assertEquals(alignment, graph.getAlignment());
+	}
+
+	@Test
+	public void testGetEdges() {
+		assertNotNull(graph.getNodes());
+		assertEquals(0, graph.getNodes().size());
+	}
+
+	@Test
+	public void testGetNodes() {
+		assertNotNull(graph.getEdges());
+		assertEquals(0, graph.getEdges().size());
+	}
+
+	@Test
+	public void testGetSubgraphs() {
+		assertNotNull(graph.getSubgraphs());
+		assertEquals(0, graph.getSubgraphs().size());
+	}
+
+	@Test
+	public void testRemoveEdge() {
+		GraphEdge newEdge = new GraphEdge(graphController);
+		assertFalse(graph.removeEdge(newEdge));
+		graph.addEdge(newEdge);
+		assertTrue(graph.removeEdge(newEdge));
+		assertFalse(graph.getNodes().contains(newEdge));
+	}
+
+	@Test
+	public void testRemoveNode() {
+		GraphNode newNode = new GraphNode(graphController);
+		assertFalse(graph.removeNode(newNode));
+		graph.addNode(newNode);
+		assertTrue(graph.removeNode(newNode));
+		assertFalse(graph.getNodes().contains(newNode));
+	}
+
+	@Test
+	public void testRemoveSubgraph() {
+		Graph newGraph = new Graph(graphController);
+		assertFalse(graph.removeSubgraph(newGraph));
+		graph.addSubgraph(newGraph);
+		assertTrue(graph.removeSubgraph(newGraph));
+		assertFalse(graph.getSubgraphs().contains(newGraph));
+	}
+
+	@Test
+	public void testSetAlignment() {
+		graph.setAlignment(Alignment.VERTICAL);
+		assertEquals(Alignment.VERTICAL, graph.getAlignment());
+		graph.setAlignment(Alignment.HORIZONTAL);
+		assertEquals(Alignment.HORIZONTAL, graph.getAlignment());
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-model/src/test/resources/nested_iteration.xml
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/test/resources/nested_iteration.xml b/taverna-graph-model/src/test/resources/nested_iteration.xml
new file mode 100644
index 0000000..3675361
--- /dev/null
+++ b/taverna-graph-model/src/test/resources/nested_iteration.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<s:scufl xmlns:s="http://org.embl.ebi.escience/xscufl/0.1alpha" version="0.2" log="0">
+  <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:c7016fc0-c2f4-4171-b6f1-430f408f4822" author="" title="nested_iteration" />
+  <s:processor name="constant" boring="true">
+    <s:stringconstant>constant</s:stringconstant>
+  </s:processor>
+  <s:processor name="generate_list">
+    <s:defaults>
+      <s:default name="prefix">prefix</s:default>
+    </s:defaults>
+    <s:beanshell>
+      <s:scriptvalue>list = new ArrayList();
+for (int i = 0; i &lt; 20; i++) {
+ list.add(prefix + i);
+}</s:scriptvalue>
+      <s:beanshellinputlist>
+        <s:beanshellinput s:syntactictype="'text/plain'">prefix</s:beanshellinput>
+      </s:beanshellinputlist>
+      <s:beanshelloutputlist>
+        <s:beanshelloutput s:syntactictype="l('text/plain')">list</s:beanshelloutput>
+      </s:beanshelloutputlist>
+      <s:dependencies s:classloader="iteration" />
+    </s:beanshell>
+  </s:processor>
+  <s:processor name="merge">
+    <s:workflow>
+      <s:scufl version="0.2" log="0">
+        <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:3368fb8d-ecc7-4fcd-b511-6ace84b13c81" author="" title="Untitled workflow #24" />
+        <s:processor name="Nested_Workflow">
+          <s:workflow>
+            <s:scufl version="0.2" log="0">
+              <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:75b99c76-7a76-4d3c-8d39-8c48df3355ad" author="" title="Untitled workflow #36" />
+              <s:processor name="concat">
+                <s:beanshell>
+                  <s:scriptvalue>Thread.sleep(200);
+out = in1 + in2;</s:scriptvalue>
+                  <s:beanshellinputlist>
+                    <s:beanshellinput s:syntactictype="'text/plain'">in1</s:beanshellinput>
+                    <s:beanshellinput s:syntactictype="'text/plain'">in2</s:beanshellinput>
+                  </s:beanshellinputlist>
+                  <s:beanshelloutputlist>
+                    <s:beanshelloutput s:syntactictype="'text/plain'">out</s:beanshelloutput>
+                  </s:beanshelloutputlist>
+                  <s:dependencies s:classloader="iteration" />
+                </s:beanshell>
+              </s:processor>
+              <s:link source="in1" sink="concat:in1" />
+              <s:link source="in2" sink="concat:in2" />
+              <s:link source="concat:out" sink="out" />
+              <s:source name="in1" />
+              <s:source name="in2" />
+              <s:sink name="out" />
+            </s:scufl>
+          </s:workflow>
+        </s:processor>
+        <s:link source="in1" sink="Nested_Workflow:in1" />
+        <s:link source="in2" sink="Nested_Workflow:in2" />
+        <s:link source="Nested_Workflow:out" sink="out" />
+        <s:source name="in1" />
+        <s:source name="in2" />
+        <s:sink name="out" />
+      </s:scufl>
+    </s:workflow>
+    <s:mergemode input="in2" mode="merge" />
+  </s:processor>
+  <s:link source="constant:value" sink="constant" />
+  <s:link source="constant:value" sink="merge:in1" />
+  <s:link source="generate_list:list" sink="list" />
+  <s:link source="generate_list:list" sink="merge:in2" />
+  <s:link source="generate_list:list" sink="merge:in2" />
+  <s:link source="merge:out" sink="concat" />
+  <s:source name="input" />
+  <s:sink name="concat">
+    <s:metadata>
+      <s:mimeTypes>
+        <s:mimeType>'text/plain'</s:mimeType>
+      </s:mimeTypes>
+    </s:metadata>
+  </s:sink>
+  <s:sink name="list">
+    <s:metadata>
+      <s:mimeTypes>
+        <s:mimeType>l('text/plain')</s:mimeType>
+      </s:mimeTypes>
+    </s:metadata>
+  </s:sink>
+  <s:sink name="constant">
+    <s:metadata>
+      <s:mimeTypes>
+        <s:mimeType>'text/plain'</s:mimeType>
+      </s:mimeTypes>
+    </s:metadata>
+  </s:sink>
+  <s:coordination name="constant_BLOCKON_generate_list">
+    <s:condition>
+      <s:state>Completed</s:state>
+      <s:target>generate_list</s:target>
+    </s:condition>
+    <s:action>
+      <s:target>constant</s:target>
+      <s:statechange>
+        <s:from>Scheduled</s:from>
+        <s:to>Running</s:to>
+      </s:statechange>
+    </s:action>
+  </s:coordination>
+  <s:coordination name="merge_BLOCKON_generate_list">
+    <s:condition>
+      <s:state>Completed</s:state>
+      <s:target>generate_list</s:target>
+    </s:condition>
+    <s:action>
+      <s:target>merge</s:target>
+      <s:statechange>
+        <s:from>Scheduled</s:from>
+        <s:to>Running</s:to>
+      </s:statechange>
+    </s:action>
+  </s:coordination>
+</s:scufl>
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-graph-view/pom.xml b/taverna-graph-view/pom.xml
new file mode 100644
index 0000000..56e0587
--- /dev/null
+++ b/taverna-graph-view/pom.xml
@@ -0,0 +1,102 @@
+<?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>graph-view</artifactId>
+	<packaging>taverna-bundle</packaging>
+	<name>Apache Taverna Graph View</name>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-file-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-menu-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-design-ui</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-workflow-view</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-graph-model</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-observer</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-ui</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-io</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-services-api</artifactId>
+			<version>${taverna.osgi.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+			<version>${taverna.language.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.batik</groupId>
+			<artifactId>batik-osgi</artifactId>
+			<version>${batik.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>${log4j.version}</version>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/AutoScrollInteractor.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/AutoScrollInteractor.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/AutoScrollInteractor.java
new file mode 100644
index 0000000..65a4aa5
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/AutoScrollInteractor.java
@@ -0,0 +1,181 @@
+package net.sf.taverna.t2.workbench.views.graph;
+
+import static java.awt.event.InputEvent.BUTTON1_DOWN_MASK;
+import static java.awt.event.InputEvent.BUTTON1_MASK;
+import static java.awt.event.MouseEvent.BUTTON1;
+import static java.awt.event.MouseEvent.MOUSE_DRAGGED;
+import static java.awt.event.MouseEvent.MOUSE_PRESSED;
+import static java.lang.System.currentTimeMillis;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.awt.geom.AffineTransform;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.apache.batik.swing.JSVGCanvas;
+import org.apache.batik.swing.gvt.InteractorAdapter;
+import org.apache.batik.swing.gvt.JGVTComponent;
+
+/**
+ * An interactor that scrolls the canvas view if the mouse is dragged to the
+ * edge of the canvas.
+ * 
+ * @author David Withers
+ */
+public class AutoScrollInteractor extends InteractorAdapter {
+	/**
+	 * Defines the border around the canvas in which the auto scroll will become
+	 * active.
+	 */
+	private static final int BORDER = 25;
+	/**
+	 * The interval, in milliseconds, between scroll events.
+	 */
+	private static final long SCROLL_INTERVAL = 100;
+
+	private JSVGCanvas svgCanvas;
+	private Dimension canvasSize;
+	private int scrollX;
+	private int scrollY;
+	private int mouseX;
+	private int mouseY;
+
+	/**
+	 * Component used to identify mouse events generated by this class
+	 */
+	private Component eventIdentifier = new Component() {
+		private static final long serialVersionUID = -295542754718804222L;
+	};
+
+	private static Timer timer = new Timer("GraphAutoScrollTimer", true);
+
+	private TimerTask task;
+
+	/**
+	 * Whether the interactor has finished.
+	 */
+	protected boolean finished = true;
+
+	public AutoScrollInteractor(JSVGCanvas svgCanvas) {
+		this.svgCanvas = svgCanvas;
+	}
+
+	@Override
+	public boolean startInteraction(InputEvent ie) {
+		int mods = ie.getModifiers();
+		if (ie.getID() == MOUSE_PRESSED && (mods & BUTTON1_MASK) != 0) {
+			AffineTransform transform = svgCanvas.getRenderingTransform();
+			// check if we're zoomed in
+			if (transform.getScaleX() > 1d || transform.getScaleY() > 1d) {
+				canvasSize = svgCanvas.getSize();
+				return true;
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public boolean endInteraction() {
+		return finished;
+	}
+
+	@Override
+	public void mousePressed(final MouseEvent e) {
+		if (startInteraction(e)) {
+			finished = false;
+			task = new TimerTask() {
+				@Override
+				public void run() {
+					scrollTimerCallback(e);
+				}
+			};
+			timer.schedule(task, 0, SCROLL_INTERVAL);
+		}
+	}
+
+	/**
+	 * Dispatches a mouse drag event that updates the mouse location by the
+	 * amount that the canvas has been scrolled.
+	 * 
+	 * @param dragX
+	 * @param dragY
+	 */
+	private void dispatchDragEvent(double dragX, double dragY) {
+		int x = (int) (mouseX + dragX);
+		int y = (int) (mouseY + dragY);
+		MouseEvent mouseDragEvent = new MouseEvent(eventIdentifier,
+				MOUSE_DRAGGED, currentTimeMillis(), BUTTON1_DOWN_MASK, x, y, 1,
+				false, BUTTON1);
+		svgCanvas.dispatchEvent(mouseDragEvent);
+	}
+
+	@Override
+	public void mouseReleased(MouseEvent e) {
+		if (!finished) {
+			finished = true;
+			scrollX = 0;
+			scrollY = 0;
+			if (task != null)
+				task.cancel();
+		}
+	}
+
+	@Override
+	public void mouseDragged(MouseEvent e) {
+		// ignore events generated by this class
+		if (!finished && e.getSource() != eventIdentifier) {
+			mouseX = e.getX();
+			mouseY = e.getY();
+			int minX = BORDER;
+			int maxX = canvasSize.width - BORDER;
+			int minY = BORDER;
+			int maxY = canvasSize.height - BORDER;
+
+			scrollX = (mouseX < minX) ? (minX - mouseX)
+					: (mouseX > maxX) ? (maxX - mouseX) : 0;
+			scrollY = (mouseY < minY) ? (minY - mouseY)
+					: (mouseY > maxY) ? (maxY - mouseY) : 0;
+		}
+	}
+
+	private void scrollTimerCallback(MouseEvent e) {
+		double x = scrollX;
+		double y = scrollY;
+		if (x == 0 && y == 0)
+			return;
+
+		JGVTComponent c = (JGVTComponent) e.getSource();
+		AffineTransform rt = (AffineTransform) c.getRenderingTransform()
+				.clone();
+		double currentTranslateX = rt.getTranslateX();
+		double currentTranslateY = rt.getTranslateY();
+		// the tranlation that will show the east edge
+		double maxTranslateX = -((canvasSize.width * rt.getScaleX()) - canvasSize.width);
+		// the translation that will show the south
+		double maxTranslateY = -((canvasSize.height * rt.getScaleY()) - canvasSize.height);
+
+		if (x > 0 && currentTranslateX + x > 0)
+			// scroll left && not at west edge
+			x = -currentTranslateX;
+		else if (x < 0 && currentTranslateX + x < maxTranslateX)
+			// scroll right && not at east edge
+			x = maxTranslateX - currentTranslateX;
+
+		if (y > 0 && currentTranslateY + y > 0)
+			// scroll up && not at north edge
+			y = -currentTranslateY;
+		else if (y < 0 && currentTranslateY + y < maxTranslateY)
+			// scroll down && not at south edge
+			y = maxTranslateY - currentTranslateY;
+
+		if (x != 0d || y != 0d) {
+			AffineTransform at = AffineTransform.getTranslateInstance(x, y);
+			rt.preConcatenate(at);
+			c.setRenderingTransform(rt);
+			dispatchDragEvent(x, y);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponent.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponent.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponent.java
new file mode 100644
index 0000000..55bcf3f
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponent.java
@@ -0,0 +1,548 @@
+/*******************************************************************************
+ * 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.views.graph;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static javax.swing.Action.SHORT_DESCRIPTION;
+import static javax.swing.Action.SMALL_ICON;
+import static javax.swing.BoxLayout.PAGE_AXIS;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.allportIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.blobIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.expandNestedIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.horizontalIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.noportIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.refreshIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.verticalIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomInIcon;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomOutIcon;
+import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ALIGNMENT;
+import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ANIMATION_ENABLED;
+import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ANIMATION_SPEED;
+import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.PORT_STYLE;
+import static org.apache.batik.swing.svg.AbstractJSVGComponent.ALWAYS_DYNAMIC;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JToggleButton;
+import javax.swing.JToolBar;
+import javax.swing.Timer;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment;
+import net.sf.taverna.t2.workbench.models.graph.GraphController;
+import net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle;
+import net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphController;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowSelectionEvent;
+import net.sf.taverna.t2.workbench.ui.dndhandler.ServiceTransferHandler;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+import net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration;
+import net.sf.taverna.t2.workbench.views.graph.menu.ResetDiagramAction;
+import net.sf.taverna.t2.workbench.views.graph.menu.ZoomInAction;
+import net.sf.taverna.t2.workbench.views.graph.menu.ZoomOutAction;
+
+import org.apache.batik.swing.JSVGCanvas;
+import org.apache.batik.swing.JSVGScrollPane;
+import org.apache.batik.swing.gvt.GVTTreeRendererAdapter;
+import org.apache.batik.swing.gvt.GVTTreeRendererEvent;
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.commons.services.ServiceRegistry;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * @author David Withers
+ * @author Alex Nenadic
+ * @author Tom Oinn
+ */
+public class GraphViewComponent extends JPanel implements UIComponentSPI {
+	private static final long serialVersionUID = 7404937056378331528L;
+	private static final Logger logger = Logger.getLogger(GraphViewComponent.class);
+
+	private Workflow workflow;
+	private SVGGraphController graphController;
+	private JPanel diagramPanel;
+
+	private Map<WorkflowBundle, Set<Workflow>> workflowsMap = new IdentityHashMap<>();
+
+	private Map<Workflow, SVGGraphController> graphControllerMap = new IdentityHashMap<>();
+	private Map<Workflow, JPanel> diagramPanelMap = new IdentityHashMap<>();
+	private Map<Workflow, Action[]> diagramActionsMap = new IdentityHashMap<>();
+
+	private Timer timer;
+
+	private CardLayout cardLayout;
+
+	private final ColourManager colourManager;
+	private final EditManager editManager;
+	private final MenuManager menuManager;
+	private final GraphViewConfiguration graphViewConfiguration;
+	private final WorkbenchConfiguration workbenchConfiguration;
+	private final SelectionManager selectionManager;
+	private final ServiceRegistry serviceRegistry;
+
+	public GraphViewComponent(ColourManager colourManager,
+			EditManager editManager, FileManager fileManager,
+			MenuManager menuManager,
+			GraphViewConfiguration graphViewConfiguration,
+			WorkbenchConfiguration workbenchConfiguration,
+			SelectionManager selectionManager, ServiceRegistry serviceRegistry) {
+		this.colourManager = colourManager;
+		this.editManager = editManager;
+		this.menuManager = menuManager;
+		this.graphViewConfiguration = graphViewConfiguration;
+		this.workbenchConfiguration = workbenchConfiguration;
+		this.selectionManager = selectionManager;
+		this.serviceRegistry = serviceRegistry;
+
+		cardLayout = new CardLayout();
+		setLayout(cardLayout);
+
+		ActionListener taskPerformer = new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				if (graphController != null)
+					graphController.redraw();
+				timer.stop();
+			}
+		};
+		timer = new Timer(100, taskPerformer);
+
+		addComponentListener(new ComponentAdapter() {
+			@Override
+			public void componentResized(ComponentEvent e) {
+				if (timer.isRunning())
+					timer.restart();
+				else
+					timer.start();
+			}
+		});
+
+		editManager.addObserver(new EditManagerObserver());
+		selectionManager.addObserver(new SelectionManagerObserver());
+		fileManager.addObserver(new FileManagerObserver());
+	}
+
+	@Override
+	protected void finalize() throws Throwable {
+		if (timer != null)
+			timer.stop();
+	}
+
+	@Override
+	public String getName() {
+		return "Graph View Component";
+	}
+
+	@Override
+	public ImageIcon getIcon() {
+		return null;
+	}
+
+	@Override
+	public void onDisplay() {
+	}
+
+	@Override
+	public void onDispose() {
+		if (timer != null)
+			timer.stop();
+	}
+
+	private JPanel createDiagramPanel(Workflow workflow) {
+		final JPanel diagramPanel = new JPanel(new BorderLayout());
+
+		// get the default diagram settings
+		Alignment alignment = Alignment.valueOf(graphViewConfiguration
+				.getProperty(ALIGNMENT));
+		PortStyle portStyle = PortStyle.valueOf(graphViewConfiguration
+				.getProperty(PORT_STYLE));
+		boolean animationEnabled = Boolean.parseBoolean(graphViewConfiguration
+				.getProperty(ANIMATION_ENABLED));
+		int animationSpeed = Integer.parseInt(graphViewConfiguration
+				.getProperty(ANIMATION_SPEED));
+
+		// create an SVG canvas
+		final JSVGCanvas svgCanvas = new JSVGCanvas(null, true, false);
+		svgCanvas.setEnableZoomInteractor(false);
+		svgCanvas.setEnableRotateInteractor(false);
+		svgCanvas.setDocumentState(ALWAYS_DYNAMIC);
+		svgCanvas.setTransferHandler(new ServiceTransferHandler(editManager,
+				menuManager, selectionManager, serviceRegistry));
+
+		AutoScrollInteractor asi = new AutoScrollInteractor(svgCanvas);
+		svgCanvas.addMouseListener(asi);
+		svgCanvas.addMouseMotionListener(asi);
+
+		final JSVGScrollPane svgScrollPane = new MySvgScrollPane(svgCanvas);
+
+		GVTTreeRendererAdapter gvtTreeRendererAdapter = new GVTTreeRendererAdapter() {
+			@Override
+			public void gvtRenderingCompleted(GVTTreeRendererEvent e) {
+				logger.info("Rendered svg");
+				svgScrollPane.reset();
+				diagramPanel.revalidate();
+			}
+		};
+		svgCanvas.addGVTTreeRendererListener(gvtTreeRendererAdapter);
+
+		// create a graph controller
+		SVGGraphController svgGraphController = new SVGGraphController(
+				workflow, selectionManager.getSelectedProfile(), false,
+				svgCanvas, alignment, portStyle, editManager, menuManager,
+				colourManager, workbenchConfiguration);
+		svgGraphController.setDataflowSelectionModel(selectionManager
+				.getDataflowSelectionModel(workflow.getParent()));
+		svgGraphController.setAnimationSpeed(animationEnabled ? animationSpeed
+				: 0);
+
+		graphControllerMap.put(workflow, svgGraphController);
+
+		// Toolbar with actions related to graph
+		JToolBar graphActionsToolbar = graphActionsToolbar(workflow,
+				svgGraphController, svgCanvas, alignment, portStyle);
+		graphActionsToolbar.setAlignmentX(LEFT_ALIGNMENT);
+		graphActionsToolbar.setFloatable(false);
+
+		// Panel to hold the toolbars
+		JPanel toolbarPanel = new JPanel();
+		toolbarPanel.setLayout(new BoxLayout(toolbarPanel, PAGE_AXIS));
+		toolbarPanel.add(graphActionsToolbar);
+
+		diagramPanel.add(toolbarPanel, NORTH);
+		diagramPanel.add(svgScrollPane, CENTER);
+
+		// JTextField workflowHierarchy = new JTextField(workflow.getName());
+		// diagramPanel.add(workflowHierarchy, BorderLayout.SOUTH);
+
+		return diagramPanel;
+	}
+
+	@SuppressWarnings("serial")
+	private JToolBar graphActionsToolbar(Workflow workflow,
+			final SVGGraphController graphController, JSVGCanvas svgCanvas,
+			Alignment alignment, PortStyle portStyle) {
+		JToolBar toolBar = new JToolBar();
+
+		JButton resetDiagramButton = new JButton();
+		resetDiagramButton.setBorder(new EmptyBorder(0, 2, 0, 2));
+		JButton zoomInButton = new JButton();
+		zoomInButton.setBorder(new EmptyBorder(0, 2, 0, 2));
+		JButton zoomOutButton = new JButton();
+		zoomOutButton.setBorder(new EmptyBorder(0, 2, 0, 2));
+
+		Action resetDiagramAction = svgCanvas.new ResetTransformAction();
+		ResetDiagramAction.setDesignAction(resetDiagramAction);
+		resetDiagramAction.putValue(SHORT_DESCRIPTION, "Reset Diagram");
+		resetDiagramAction.putValue(SMALL_ICON, refreshIcon);
+		resetDiagramButton.setAction(resetDiagramAction);
+
+		Action zoomInAction = svgCanvas.new ZoomAction(1.2);
+		ZoomInAction.setDesignAction(zoomInAction);
+		zoomInAction.putValue(SHORT_DESCRIPTION, "Zoom In");
+		zoomInAction.putValue(SMALL_ICON, zoomInIcon);
+		zoomInButton.setAction(zoomInAction);
+
+		Action zoomOutAction = svgCanvas.new ZoomAction(1 / 1.2);
+		ZoomOutAction.setDesignAction(zoomOutAction);
+		zoomOutAction.putValue(SHORT_DESCRIPTION, "Zoom Out");
+		zoomOutAction.putValue(SMALL_ICON, zoomOutIcon);
+		zoomOutButton.setAction(zoomOutAction);
+
+		diagramActionsMap.put(workflow, new Action[] { resetDiagramAction,
+				zoomInAction, zoomOutAction });
+
+		toolBar.add(resetDiagramButton);
+		toolBar.add(zoomInButton);
+		toolBar.add(zoomOutButton);
+
+		toolBar.addSeparator();
+
+		ButtonGroup nodeTypeGroup = new ButtonGroup();
+
+		JToggleButton noPorts = new JToggleButton();
+		JToggleButton allPorts = new JToggleButton();
+		JToggleButton blobs = new JToggleButton();
+		nodeTypeGroup.add(noPorts);
+		nodeTypeGroup.add(allPorts);
+		nodeTypeGroup.add(blobs);
+
+		if (portStyle.equals(PortStyle.NONE))
+			noPorts.setSelected(true);
+		else if (portStyle.equals(PortStyle.ALL))
+			allPorts.setSelected(true);
+		else
+			blobs.setSelected(true);
+
+		noPorts.setAction(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				graphController.setPortStyle(PortStyle.NONE);
+				graphController.redraw();
+			}
+		});
+		noPorts.getAction().putValue(SHORT_DESCRIPTION,
+				"Display no service ports");
+		noPorts.getAction().putValue(SMALL_ICON, noportIcon);
+		noPorts.setFocusPainted(false);
+
+		allPorts.setAction(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				graphController.setPortStyle(PortStyle.ALL);
+				graphController.redraw();
+			}
+		});
+		allPorts.getAction().putValue(SHORT_DESCRIPTION,
+				"Display all service ports");
+		allPorts.getAction().putValue(SMALL_ICON, allportIcon);
+		allPorts.setFocusPainted(false);
+
+		blobs.setAction(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				graphController.setPortStyle(PortStyle.BLOB);
+				graphController.redraw();
+			}
+		});
+		blobs.getAction().putValue(SHORT_DESCRIPTION,
+				"Display services as circles");
+		blobs.getAction().putValue(SMALL_ICON, blobIcon);
+		blobs.setFocusPainted(false);
+
+		toolBar.add(noPorts);
+		toolBar.add(allPorts);
+		toolBar.add(blobs);
+
+		toolBar.addSeparator();
+
+		ButtonGroup alignmentGroup = new ButtonGroup();
+
+		JToggleButton vertical = new JToggleButton();
+		JToggleButton horizontal = new JToggleButton();
+		alignmentGroup.add(vertical);
+		alignmentGroup.add(horizontal);
+
+		if (alignment.equals(Alignment.VERTICAL)) {
+			vertical.setSelected(true);
+		} else {
+			horizontal.setSelected(true);
+		}
+
+		vertical.setAction(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				graphController.setAlignment(Alignment.VERTICAL);
+				graphController.redraw();
+			}
+		});
+		vertical.getAction().putValue(SHORT_DESCRIPTION,
+				"Align services vertically");
+		vertical.getAction().putValue(SMALL_ICON, verticalIcon);
+		vertical.setFocusPainted(false);
+
+		horizontal.setAction(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				graphController.setAlignment(Alignment.HORIZONTAL);
+				graphController.redraw();
+			}
+
+		});
+		horizontal.getAction().putValue(SHORT_DESCRIPTION,
+				"Align services horizontally");
+		horizontal.getAction().putValue(SMALL_ICON, horizontalIcon);
+		horizontal.setFocusPainted(false);
+
+		toolBar.add(vertical);
+		toolBar.add(horizontal);
+
+		toolBar.addSeparator();
+
+		JToggleButton expandNested = new JToggleButton();
+		expandNested.setSelected(true);
+
+		expandNested.setAction(new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				graphController.setExpandNestedDataflows(!graphController
+						.expandNestedDataflows());
+				graphController.redraw();
+			}
+		});
+		expandNested.getAction().putValue(SHORT_DESCRIPTION,
+				"Expand Nested Workflows");
+		expandNested.getAction().putValue(SMALL_ICON, expandNestedIcon);
+		expandNested.setFocusPainted(false);
+		toolBar.add(expandNested);
+
+		return toolBar;
+	}
+
+	/**
+	 * Sets the Workflow to display in the graph view.
+	 *
+	 * @param workflow
+	 */
+	private void setWorkflow(Workflow workflow) {
+		this.workflow = workflow;
+		if (!diagramPanelMap.containsKey(workflow))
+			addWorkflow(workflow);
+		graphController = graphControllerMap.get(workflow);
+		diagramPanel = diagramPanelMap.get(workflow);
+		Action[] actions = diagramActionsMap.get(workflow);
+		if (actions != null && actions.length == 3) {
+			ResetDiagramAction.setDesignAction(actions[0]);
+			ZoomInAction.setDesignAction(actions[1]);
+			ZoomOutAction.setDesignAction(actions[2]);
+		}
+		cardLayout.show(this, String.valueOf(diagramPanel.hashCode()));
+		graphController.redraw();
+	}
+
+	private void addWorkflow(Workflow workflow) {
+		JPanel newDiagramPanel = createDiagramPanel(workflow);
+		add(newDiagramPanel, String.valueOf(newDiagramPanel.hashCode()));
+		diagramPanelMap.put(workflow, newDiagramPanel);
+		if (!workflowsMap.containsKey(workflow.getParent()))
+			workflowsMap.put(workflow.getParent(), new HashSet<Workflow>());
+		workflowsMap.get(workflow.getParent()).add(workflow);
+	}
+
+	private void removeWorkflow(Workflow workflow) {
+		JPanel panel = diagramPanelMap.remove(workflow);
+		if (panel != null)
+			remove(panel);
+		SVGGraphController removedController = graphControllerMap.remove(workflow);
+		if (removedController != null)
+			removedController.shutdown();
+		diagramActionsMap.remove(workflow);
+		Set<Workflow> workflows = workflowsMap.get(workflow.getParent());
+		if (workflows != null)
+			workflows.remove(workflow);
+	}
+
+	public GraphController getGraphController(Workflow workflow) {
+		return graphControllerMap.get(workflow);
+	}
+
+	private class EditManagerObserver extends
+			SwingAwareObserver<EditManagerEvent> {
+		@Override
+		public void notifySwing(Observable<EditManagerEvent> sender,
+				EditManagerEvent message) {
+			if (!(message instanceof AbstractDataflowEditEvent))
+				return;
+			AbstractDataflowEditEvent dataflowEditEvent = (AbstractDataflowEditEvent) message;
+			if (dataflowEditEvent.getDataFlow() != workflow.getParent())
+				return;
+			
+			boolean animationEnabled = Boolean
+					.parseBoolean(graphViewConfiguration
+							.getProperty(ANIMATION_ENABLED));
+			int animationSpeed = (animationEnabled ? Integer
+					.parseInt(graphViewConfiguration
+							.getProperty(ANIMATION_SPEED)) : 0);
+			boolean animationSettingChanged = (animationEnabled != (graphController
+					.getAnimationSpeed() != 0));
+
+			if (graphController.isDotMissing() || animationSettingChanged) {
+				removeWorkflow(workflow);
+				setWorkflow(workflow);
+			} else {
+				if (animationSpeed != graphController.getAnimationSpeed())
+					graphController.setAnimationSpeed(animationSpeed);
+				graphController.redraw();
+			}
+		}
+	}
+
+	private class FileManagerObserver extends SwingAwareObserver<FileManagerEvent> {
+		@Override
+		public void notifySwing(Observable<FileManagerEvent> sender, final FileManagerEvent message) {
+			if (!(message instanceof ClosedDataflowEvent))
+				return;
+			ClosedDataflowEvent closedDataflowEvent = (ClosedDataflowEvent) message;
+
+			WorkflowBundle workflowBundle = closedDataflowEvent.getDataflow();
+			if (workflowsMap.containsKey(workflowBundle))
+				for (Workflow workflow : workflowsMap.remove(workflowBundle))
+					removeWorkflow(workflow);
+		}
+	}
+
+	private class SelectionManagerObserver extends
+			SwingAwareObserver<SelectionManagerEvent> {
+		@Override
+		public void notifySwing(Observable<SelectionManagerEvent> sender,
+				SelectionManagerEvent message) {
+			if (message instanceof WorkflowSelectionEvent)
+				setWorkflow(selectionManager.getSelectedWorkflow());
+			else if (message instanceof WorkflowBundleSelectionEvent)
+				setWorkflow(selectionManager.getSelectedWorkflow());
+		}
+	}
+
+	private class MySvgScrollPane extends JSVGScrollPane {
+		private static final long serialVersionUID = -1539947450704269879L;
+
+		public MySvgScrollPane(JSVGCanvas canvas) {
+			super(canvas);
+		}
+
+		@Override
+		public void reset() {
+			super.resizeScrollBars();
+			super.reset();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponentFactory.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponentFactory.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponentFactory.java
new file mode 100644
index 0000000..85f2929
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponentFactory.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * 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.views.graph;
+
+import javax.swing.ImageIcon;
+
+import uk.org.taverna.commons.services.ServiceRegistry;
+
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration;
+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.zaria.UIComponentFactorySPI;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI;
+import net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration;
+
+/**
+ * @author David Withers
+ */
+public class GraphViewComponentFactory implements UIComponentFactorySPI {
+	private EditManager editManager;
+	private FileManager fileManager;
+	private MenuManager menuManager;
+	private SelectionManager selectionManager;
+	private ColourManager colourManager;
+	private WorkbenchConfiguration workbenchConfiguration;
+	private GraphViewConfiguration graphViewConfiguration;
+	private ServiceRegistry serviceRegistry;
+
+	@Override
+	public UIComponentSPI getComponent() {
+		return new GraphViewComponent(colourManager, editManager, fileManager,
+				menuManager, graphViewConfiguration, workbenchConfiguration,
+				selectionManager, serviceRegistry);
+	}
+
+	@Override
+	public ImageIcon getIcon() {
+		return null;
+	}
+
+	@Override
+	public String getName() {
+		return "Graph View";
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+	}
+
+	public void setMenuManager(MenuManager menuManager) {
+		this.menuManager = menuManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setColourManager(ColourManager colourManager) {
+		this.colourManager = colourManager;
+	}
+
+	public void setWorkbenchConfiguration(
+			WorkbenchConfiguration workbenchConfiguration) {
+		this.workbenchConfiguration = workbenchConfiguration;
+	}
+
+	public void setGraphViewConfiguration(
+			GraphViewConfiguration graphViewConfiguration) {
+		this.graphViewConfiguration = graphViewConfiguration;
+	}
+
+	public void setServiceRegistry(ServiceRegistry serviceRegistry) {
+		this.serviceRegistry = serviceRegistry;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFInputAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFInputAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFInputAction.java
new file mode 100644
index 0000000..8c16e4a
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFInputAction.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * 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.views.graph.actions;
+
+import static java.awt.event.InputEvent.ALT_DOWN_MASK;
+import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
+import static java.awt.event.KeyEvent.VK_I;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.inputIcon;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.ui.menu.DesignOnlyAction;
+import net.sf.taverna.t2.workbench.design.actions.AddDataflowInputAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * An action that adds a workflow input.
+ * 
+ * @author Alex Nenadic
+ * @author Alan R Williams
+ */
+@SuppressWarnings("serial")
+public class AddWFInputAction extends AbstractAction implements
+		DesignOnlyAction {
+	private final EditManager editManager;
+	private final SelectionManager selectionManager;
+
+	public AddWFInputAction(EditManager editManager,
+			SelectionManager selectionManager) {
+		super();
+		this.editManager = editManager;
+		this.selectionManager = selectionManager;
+		putValue(SMALL_ICON, inputIcon);
+		putValue(NAME, "Workflow input port");
+		putValue(SHORT_DESCRIPTION, "Workflow input port");
+		putValue(ACCELERATOR_KEY,
+				getKeyStroke(VK_I, SHIFT_DOWN_MASK | ALT_DOWN_MASK));
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		Workflow workflow = selectionManager.getSelectedWorkflow();
+		new AddDataflowInputAction(workflow, null, editManager,
+				selectionManager).actionPerformed(e);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFOutputAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFOutputAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFOutputAction.java
new file mode 100644
index 0000000..4027773
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFOutputAction.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * 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.views.graph.actions;
+
+import static java.awt.event.InputEvent.ALT_DOWN_MASK;
+import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
+import static java.awt.event.KeyEvent.VK_O;
+import static javax.swing.KeyStroke.getKeyStroke;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.ui.menu.DesignOnlyAction;
+import net.sf.taverna.t2.workbench.design.actions.AddDataflowOutputAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * An action that adds a workflow output.
+ * 
+ * @author Alex Nenadic
+ * @author Alan R Williams
+ */
+@SuppressWarnings("serial")
+public class AddWFOutputAction extends AbstractAction implements
+		DesignOnlyAction {
+	private final EditManager editManager;
+	private final SelectionManager selectionManager;
+
+	public AddWFOutputAction(EditManager editManager,
+			SelectionManager selectionManager) {
+		super();
+		this.editManager = editManager;
+		this.selectionManager = selectionManager;
+		putValue(SMALL_ICON, WorkbenchIcons.outputIcon);
+		putValue(NAME, "Workflow output port");
+		putValue(SHORT_DESCRIPTION, "Workflow output port");
+		putValue(ACCELERATOR_KEY,
+				getKeyStroke(VK_O, SHIFT_DOWN_MASK | ALT_DOWN_MASK));
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		Workflow workflow = selectionManager.getSelectedWorkflow();
+		new AddDataflowOutputAction(workflow, null, editManager,
+				selectionManager).actionPerformed(e);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/DeleteGraphComponentAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/DeleteGraphComponentAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/DeleteGraphComponentAction.java
new file mode 100644
index 0000000..86849b6
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/DeleteGraphComponentAction.java
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * 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.views.graph.actions;
+
+import static java.awt.event.KeyEvent.VK_DELETE;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon;
+
+import java.awt.event.ActionEvent;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.ui.menu.DesignOnlyAction;
+import net.sf.taverna.t2.workbench.design.actions.RemoveConditionAction;
+import net.sf.taverna.t2.workbench.design.actions.RemoveDataflowInputPortAction;
+import net.sf.taverna.t2.workbench.design.actions.RemoveDataflowOutputPortAction;
+import net.sf.taverna.t2.workbench.design.actions.RemoveDatalinkAction;
+import net.sf.taverna.t2.workbench.design.actions.RemoveProcessorAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage;
+import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.ControlLink;
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+
+/**
+ * An action that deletes the selected graph component.
+ *
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class DeleteGraphComponentAction extends AbstractAction implements DesignOnlyAction {
+	/** Current workflow's selection model event observer.*/
+	private Observer<DataflowSelectionMessage> workflowSelectionObserver = new DataflowSelectionObserver();
+
+	private final EditManager editManager;
+	private final SelectionManager selectionManager;
+
+	public DeleteGraphComponentAction(EditManager editManager, final SelectionManager selectionManager) {
+		super();
+		this.editManager = editManager;
+		this.selectionManager = selectionManager;
+		putValue(SMALL_ICON, deleteIcon);
+		putValue(NAME, "Delete");
+		putValue(SHORT_DESCRIPTION, "Delete selected component");
+		putValue(ACCELERATOR_KEY, getKeyStroke(VK_DELETE, 0));
+		setEnabled(false);
+
+		selectionManager.addObserver(new SelectionManagerObserver());
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		WorkflowBundle workflowBundle = selectionManager
+				.getSelectedWorkflowBundle();
+		DataflowSelectionModel dataFlowSelectionModel = selectionManager
+				.getDataflowSelectionModel(workflowBundle);
+		// Get all selected components
+		Set<Object> selectedWFComponents = dataFlowSelectionModel.getSelection();
+		for (Object selectedWFComponent : selectedWFComponents)
+			if (selectedWFComponent instanceof Processor) {
+				Processor processor = (Processor) selectedWFComponent;
+				new RemoveProcessorAction(processor.getParent(), processor,
+						null, editManager, selectionManager).actionPerformed(e);
+			} else if (selectedWFComponent instanceof DataLink) {
+				DataLink dataLink = (DataLink) selectedWFComponent;
+				new RemoveDatalinkAction(dataLink.getParent(), dataLink, null,
+						editManager, selectionManager).actionPerformed(e);
+			} else if (selectedWFComponent instanceof InputWorkflowPort) {
+				InputWorkflowPort port = (InputWorkflowPort) selectedWFComponent;
+				new RemoveDataflowInputPortAction(port.getParent(), port, null,
+						editManager, selectionManager).actionPerformed(e);
+			} else if (selectedWFComponent instanceof OutputWorkflowPort) {
+				OutputWorkflowPort port = (OutputWorkflowPort) selectedWFComponent;
+				new RemoveDataflowOutputPortAction(port.getParent(), port,
+						null, editManager, selectionManager).actionPerformed(e);
+			} else if (selectedWFComponent instanceof ControlLink) {
+				ControlLink controlLink = (ControlLink) selectedWFComponent;
+				new RemoveConditionAction(controlLink.getParent(), controlLink,
+						null, editManager, selectionManager).actionPerformed(e);
+			}
+	}
+
+	/**
+	 * Check if action should be enabled or disabled and update its status.
+	 */
+	public void updateStatus(WorkflowBundle selectionWorkflowBundle) {
+		if (selectionWorkflowBundle != null) {
+			DataflowSelectionModel selectionModel = selectionManager
+					.getDataflowSelectionModel(selectionWorkflowBundle);
+			Set<Object> selection = selectionModel.getSelection();
+			if (!selection.isEmpty()) {
+				// Take the first selected item - we only support single selections anyway
+				Object selected = selection.toArray()[0];
+				if ((selected instanceof Processor)
+						|| (selected instanceof InputWorkflowPort)
+						|| (selected instanceof OutputWorkflowPort)
+						|| (selected instanceof DataLink)
+						|| (selected instanceof ControlLink)) {
+					setEnabled(true);
+					return;
+				}
+			}
+		}
+		setEnabled(false);
+	}
+
+	/**
+	 * Observes events on workflow Selection Manager, i.e. when a workflow node
+	 * is selected in the graph view, and enables/disables this action
+	 * accordingly.
+	 */
+	private final class DataflowSelectionObserver extends
+			SwingAwareObserver<DataflowSelectionMessage> {
+		@Override
+		public void notifySwing(Observable<DataflowSelectionMessage> sender,
+				DataflowSelectionMessage message) {
+			updateStatus(selectionManager.getSelectedWorkflowBundle());
+		}
+	}
+
+	private final class SelectionManagerObserver extends
+			SwingAwareObserver<SelectionManagerEvent> {
+		@Override
+		public void notifySwing(Observable<SelectionManagerEvent> sender,
+				SelectionManagerEvent message) {
+			if (!(message instanceof WorkflowBundleSelectionEvent))
+				return;
+			WorkflowBundleSelectionEvent workflowBundleSelectionEvent = (WorkflowBundleSelectionEvent) message;
+			WorkflowBundle oldFlow = workflowBundleSelectionEvent
+					.getPreviouslySelectedWorkflowBundle();
+			WorkflowBundle newFlow = workflowBundleSelectionEvent
+					.getSelectedWorkflowBundle();
+
+			/*
+			 * Remove the workflow selection model listener from the previous
+			 * (if any) and add to the new workflow (if any)
+			 */
+			if (oldFlow != null)
+				selectionManager.getDataflowSelectionModel(oldFlow)
+						.removeObserver(workflowSelectionObserver);
+
+			// Update the buttons status as current dataflow has changed
+			updateStatus(newFlow);
+
+			if (newFlow != null)
+				selectionManager.getDataflowSelectionModel(newFlow)
+						.addObserver(workflowSelectionObserver);
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/RenameWFInputOutputProcessorAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/RenameWFInputOutputProcessorAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/RenameWFInputOutputProcessorAction.java
new file mode 100644
index 0000000..f56a0e0
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/RenameWFInputOutputProcessorAction.java
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * 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.views.graph.actions;
+
+import static java.awt.event.KeyEvent.VK_F2;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.KeyStroke.getKeyStroke;
+
+import java.awt.event.ActionEvent;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.ui.menu.DesignOnlyAction;
+import net.sf.taverna.t2.workbench.design.actions.EditDataflowInputPortAction;
+import net.sf.taverna.t2.workbench.design.actions.EditDataflowOutputPortAction;
+import net.sf.taverna.t2.workbench.design.actions.RenameProcessorAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage;
+import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+
+/**
+ * An action that allows user to rename workflow input, output or
+ * processor, in case one of these is currently selected in the Graph View.
+ *
+ * @author Alex Nenadic
+ */
+@SuppressWarnings("serial")
+public class RenameWFInputOutputProcessorAction extends AbstractAction implements DesignOnlyAction {
+	/** Current workflow's selection model event observer.*/
+	private Observer<DataflowSelectionMessage> workflowSelectionObserver = new DataflowSelectionObserver();
+
+	private final EditManager editManager;
+	private final SelectionManager selectionManager;
+
+	public RenameWFInputOutputProcessorAction(EditManager editManager,
+			final SelectionManager selectionManager) {
+		super();
+		this.editManager = editManager;
+		this.selectionManager = selectionManager;
+		putValue(SMALL_ICON, WorkbenchIcons.renameIcon);
+		putValue(NAME, "Rename");
+		putValue(SHORT_DESCRIPTION, "Rename inputs, outputs or services");
+		putValue(ACCELERATOR_KEY, getKeyStroke(VK_F2, 0));
+		setEnabled(false);
+
+		selectionManager.addObserver(new SelectionManagerObserver());
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		WorkflowBundle workflowBundle = selectionManager
+				.getSelectedWorkflowBundle();
+		DataflowSelectionModel dataFlowSelectionModel = selectionManager
+				.getDataflowSelectionModel(workflowBundle);
+		// Get selected port
+		Set<Object> selectedWFComponents = dataFlowSelectionModel
+				.getSelection();
+		if (selectedWFComponents.size() > 1) {
+			showMessageDialog(
+					null,
+					"Only one workflow component should be selected for this action.",
+					"Warning", WARNING_MESSAGE);
+		} else {
+			Object selectedWFComponent = selectedWFComponents.toArray()[0];
+			if (selectedWFComponent instanceof InputWorkflowPort) {
+				InputWorkflowPort port = (InputWorkflowPort) selectedWFComponent;
+				new EditDataflowInputPortAction(port.getParent(), port, null,
+						editManager, selectionManager).actionPerformed(e);
+			} else if (selectedWFComponent instanceof OutputWorkflowPort) {
+				OutputWorkflowPort port = (OutputWorkflowPort) selectedWFComponent;
+				new EditDataflowOutputPortAction(port.getParent(), port, null,
+						editManager, selectionManager).actionPerformed(e);
+			} else if (selectedWFComponent instanceof Processor) {
+				Processor processor = (Processor) selectedWFComponent;
+				new RenameProcessorAction(processor.getParent(), processor,
+						null, editManager, selectionManager).actionPerformed(e);
+			} else { // should not happen as the button will be disabled otherwise, but ...
+				showMessageDialog(
+						null,
+						"This action does not apply for the selected component.",
+						"Warning", WARNING_MESSAGE);
+			}
+		}
+	}
+
+	/**
+	 * Check if action should be enabled or disabled and update its status.
+	 */
+	public void updateStatus() {
+		WorkflowBundle workflowBundle = selectionManager
+				.getSelectedWorkflowBundle();
+		DataflowSelectionModel selectionModel = selectionManager
+				.getDataflowSelectionModel(workflowBundle);
+
+		// List of all selected objects in the graph view
+		Set<Object> selection = selectionModel.getSelection();
+
+		if (!selection.isEmpty()) {
+			// Take the first selected item - we only support single selections anyway
+			Object selected = selection.toArray()[0];
+			if ((selected instanceof Processor)
+					|| (selected instanceof InputWorkflowPort)
+					|| (selected instanceof OutputWorkflowPort)) {
+				setEnabled(true);
+				return;
+			}
+		}
+		setEnabled(false);
+	}
+
+	/**
+	 * Observes events on workflow Selection Manager, i.e. when a workflow node
+	 * is selected in the graph view, and enables/disables this action
+	 * accordingly.
+	 */
+	private final class DataflowSelectionObserver extends
+			SwingAwareObserver<DataflowSelectionMessage> {
+		@Override
+		public void notifySwing(Observable<DataflowSelectionMessage> sender,
+				DataflowSelectionMessage message) {
+			updateStatus();
+		}
+	}
+
+	private final class SelectionManagerObserver extends
+			SwingAwareObserver<SelectionManagerEvent> {
+		@Override
+		public void notifySwing(Observable<SelectionManagerEvent> sender,
+				SelectionManagerEvent message) {
+			if (!(message instanceof WorkflowBundleSelectionEvent))
+				return;
+			WorkflowBundleSelectionEvent workflowBundleSelectionEvent = (WorkflowBundleSelectionEvent) message;
+			WorkflowBundle oldFlow = workflowBundleSelectionEvent
+					.getPreviouslySelectedWorkflowBundle();
+			WorkflowBundle newFlow = workflowBundleSelectionEvent
+					.getSelectedWorkflowBundle();
+			// Update the buttons status as current dataflow has changed
+			updateStatus();
+
+			/*
+			 * Remove the workflow selection model listener from the previous
+			 * (if any) and add to the new workflow (if any)
+			 */
+			if (oldFlow != null)
+				selectionManager.getDataflowSelectionModel(oldFlow)
+						.removeObserver(workflowSelectionObserver);
+
+			if (newFlow != null)
+				selectionManager.getDataflowSelectionModel(newFlow)
+						.addObserver(workflowSelectionObserver);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfiguration.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfiguration.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfiguration.java
new file mode 100644
index 0000000..b1c3265
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfiguration.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (C) 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.views.graph.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import uk.org.taverna.configuration.AbstractConfigurable;
+import uk.org.taverna.configuration.ConfigurationManager;
+
+import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment;
+import net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle;
+
+/**
+ * Configuration for the GraphViewComponent.
+ * 
+ * @author David Withers
+ */
+public class GraphViewConfiguration extends AbstractConfigurable {
+	public static final String PORT_STYLE = "portStyle";
+	public static final String ALIGNMENT = "alignment";
+	public static final String ANIMATION_ENABLED = "animationEnabled";
+	public static final String ANIMATION_SPEED = "animationSpeed";
+
+	private Map<String, String> defaultPropertyMap;
+
+	public GraphViewConfiguration(ConfigurationManager configurationManager) {
+		super(configurationManager);
+	}
+
+	@Override
+	public String getCategory() {
+		return "general";
+	}
+
+	@Override
+	public Map<String, String> getDefaultPropertyMap() {
+		if (defaultPropertyMap == null) {
+			defaultPropertyMap = new HashMap<>();
+			defaultPropertyMap.put(PORT_STYLE, PortStyle.NONE.toString());
+			defaultPropertyMap.put(ALIGNMENT, Alignment.VERTICAL.toString());
+			defaultPropertyMap.put(ANIMATION_ENABLED, "false");
+			defaultPropertyMap.put(ANIMATION_SPEED, "800");
+		}
+		return defaultPropertyMap;
+	}
+
+	@Override
+	public String getDisplayName() {
+		return "Diagram";
+	}
+
+	@Override
+	public String getFilePrefix() {
+		return "Diagram";
+	}
+
+	@Override
+	public String getUUID() {
+		return "3686BA31-449F-4147-A8AC-0C3F63AFC68F";
+	}
+}


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

Posted by st...@apache.org.
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/DefaultGraphEventManager.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DefaultGraphEventManager.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DefaultGraphEventManager.java
new file mode 100644
index 0000000..d434e48
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DefaultGraphEventManager.java
@@ -0,0 +1,271 @@
+/*******************************************************************************
+ * 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 javax.swing.SwingUtilities.convertPointFromScreen;
+import static javax.swing.SwingUtilities.invokeLater;
+import static net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle.ALL;
+import static net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle.NONE;
+
+import java.awt.Component;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.net.URI;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+
+/**
+ * Manager for handling UI events on GraphElements.
+ *
+ * @author David Withers
+ */
+public class DefaultGraphEventManager implements GraphEventManager {
+	private static final URI NESTED_WORKFLOW_URI = URI
+			.create("http://ns.taverna.org.uk/2010/activity/nested-workflow");
+
+	private GraphController graphController;
+	private Component component;
+	private JPopupMenu menu;
+	private MenuManager menuManager;
+
+	private Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	/**
+	 * Constructs a new instance of GraphEventManager.
+	 *
+	 * @param graphController
+	 * @param component
+	 *            component to use when displaying popup menus
+	 */
+	public DefaultGraphEventManager(GraphController graphController, Component component,
+			MenuManager menuManager) {
+		this.graphController = graphController;
+		this.component = component;
+		this.menuManager = menuManager;
+	}
+
+	@Override
+	public void mouseClicked(final GraphElement graphElement, short button, boolean altKey,
+			boolean ctrlKey, boolean metaKey, final int x, final int y, int screenX, int screenY) {
+		Object dataflowObject = graphElement.getWorkflowBean();
+
+		// For both left and right click - add to selection model
+		if (graphController.getDataflowSelectionModel() != null)
+			graphController.getDataflowSelectionModel().addSelection(dataflowObject);
+
+		if ((button != 2) && !ctrlKey)return;
+
+		// If this was a right click - show a pop-up as well
+		if (dataflowObject == null)
+			menu = menuManager.createContextMenu(graphController.getWorkflow(),
+					graphController.getWorkflow(), component);
+		else {
+			menu = menuManager.createContextMenu(graphController.getWorkflow(),
+					dataflowObject, component);
+			if (dataflowObject instanceof Processor) {
+				final Processor processor = (Processor) dataflowObject;
+				ProcessorBinding processorBinding = scufl2Tools
+						.processorBindingForProcessor(processor,
+								graphController.getProfile());
+				final Activity activity = processorBinding.getBoundActivity();
+				if (menu == null)
+					menu = new JPopupMenu();
+				if (graphElement instanceof GraphNode) {
+					defineMenuForGraphElement(graphElement, x, y, processor,
+							activity);
+				} else if (graphElement instanceof Graph) {
+					defineMenuForGraphBackground(activity);
+				}
+			}
+		}
+
+		if (menu != null) {
+			final Point p = new Point(screenX, screenY);
+			convertPointFromScreen(p, component);
+			invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					menu.show(component, p.x, p.y);
+				}
+			});
+		}
+	}
+
+	@SuppressWarnings("serial")
+	private void defineMenuForGraphBackground(final Activity activity) {
+		if (activity.getType().equals(NESTED_WORKFLOW_URI)) {
+			menu.addSeparator();
+			menu.add(new JMenuItem(new AbstractAction("Hide nested workflow") {
+				@Override
+				public void actionPerformed(ActionEvent ev) {
+					graphController.setExpandNestedDataflow(activity, false);
+					graphController.redraw();
+				}
+			}));
+		}
+	}
+
+	@SuppressWarnings("serial")
+	private void defineMenuForGraphElement(final GraphElement graphElement,
+			final int x, final int y, final Processor processor,
+			final Activity activity) {
+		if (graphController.getPortStyle(processor).equals(NONE)) {
+			menu.addSeparator();
+			menu.add(new JMenuItem(new AbstractAction("Show ports") {
+				@Override
+				public void actionPerformed(ActionEvent ev) {
+					graphController.setPortStyle(processor, ALL);
+					graphController.redraw();
+				}
+			}));
+		} else if (graphController.getPortStyle(processor).equals(ALL)) {
+			menu.addSeparator();
+			menu.add(new JMenuItem(new AbstractAction("Hide ports") {
+				@Override
+				public void actionPerformed(ActionEvent arg0) {
+					graphController.setPortStyle(processor, NONE);
+					graphController.redraw();
+				}
+			}));
+		}
+
+		if (activity.getType().equals(NESTED_WORKFLOW_URI)) {
+			menu.addSeparator();
+			menu.add(new JMenuItem(new AbstractAction("Show nested workflow") {
+				@Override
+				public void actionPerformed(ActionEvent arg0) {
+					graphController.setExpandNestedDataflow(activity, true);
+					graphController.redraw();
+				}
+			}));
+		}
+
+		menu.addSeparator();
+
+		GraphNode graphNode = (GraphNode) graphElement;
+
+		List<GraphNode> sourceNodes = graphNode.getSourceNodes();
+		if (sourceNodes.size() == 1) {
+			final GraphNode sourceNode = sourceNodes.get(0);
+			if (sourceNode.getLabel() != null) {
+				menu.add(new JMenuItem(new AbstractAction("Link from output '"
+						+ sourceNode.getLabel() + "'") {
+					@Override
+					public void actionPerformed(ActionEvent arg0) {
+						graphController.startEdgeCreation(sourceNode,
+								new Point(x, y));
+					}
+				}));
+			}
+		} else if (sourceNodes.size() > 0) {
+			JMenu linkMenu = new JMenu("Link from output...");
+			menu.add(linkMenu);
+			for (final GraphNode sourceNode : sourceNodes) {
+				linkMenu.add(new JMenuItem(new AbstractAction(sourceNode
+						.getLabel()) {
+					@Override
+					public void actionPerformed(ActionEvent arg0) {
+						graphController.startEdgeCreation(sourceNode,
+								new Point(x, y));
+					}
+				}));
+			}
+		}
+
+		List<GraphNode> sinkNodes = graphNode.getSinkNodes();
+		if (sinkNodes.size() == 1) {
+			final GraphNode sinkNode = sinkNodes.get(0);
+			if (sinkNode.getLabel() != null) {
+				menu.add(new JMenuItem(new AbstractAction("Link to input '"
+						+ sinkNode.getLabel() + "'") {
+					@Override
+					public void actionPerformed(ActionEvent arg0) {
+						graphController.startEdgeCreation(sinkNode, new Point(
+								x, y));
+					}
+				}));
+			}
+		} else if (sinkNodes.size() > 0) {
+			JMenu linkMenu = new JMenu("Link to input...");
+			menu.add(linkMenu);
+			for (final GraphNode sinkNode : sinkNodes) {
+				linkMenu.add(new JMenuItem(new AbstractAction(sinkNode
+						.getLabel()) {
+					@Override
+					public void actionPerformed(ActionEvent arg0) {
+						graphController.startEdgeCreation(sinkNode, new Point(
+								x, y));
+					}
+				}));
+			}
+		}
+	}
+
+	@Override
+	public void mouseDown(GraphElement graphElement, short button,
+			boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y,
+			int screenX, int screenY) {
+		if (button == 0)
+			graphController.startEdgeCreation(graphElement, new Point(x, y));
+	}
+
+	@Override
+	public void mouseUp(GraphElement graphElement, short button,
+			boolean altKey, boolean ctrlKey, boolean metaKey, final int x,
+			final int y, int screenX, int screenY) {
+		if (button == 0)
+			graphController.stopEdgeCreation(graphElement, new Point(screenX,
+					screenY));
+	}
+
+	@Override
+	public void mouseMoved(GraphElement graphElement, short button,
+			boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y,
+			int screenX, int screenY) {
+		graphController.moveEdgeCreationTarget(graphElement, new Point(x, y));
+	}
+
+	@Override
+	public void mouseOver(GraphElement graphElement, short button,
+			boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y,
+			int screenX, int screenY) {
+		if (graphElement.getWorkflowBean() != null)
+			graphElement.setActive(true);
+	}
+
+	@Override
+	public void mouseOut(GraphElement graphElement, short button,
+			boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y,
+			int screenX, int screenY) {
+		if (graphElement.getWorkflowBean() != null)
+			graphElement.setActive(false);
+	}
+}

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/DotWriter.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DotWriter.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DotWriter.java
new file mode 100644
index 0000000..07cdbad
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DotWriter.java
@@ -0,0 +1,253 @@
+/*******************************************************************************
+ * 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 java.lang.String.format;
+import static net.sf.taverna.t2.workbench.models.graph.Graph.Alignment.HORIZONTAL;
+import static net.sf.taverna.t2.workbench.models.graph.Graph.Alignment.VERTICAL;
+import static net.sf.taverna.t2.workbench.models.graph.GraphElement.LineStyle.NONE;
+import static net.sf.taverna.t2.workbench.models.graph.GraphShapeElement.Shape.RECORD;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment;
+
+/**
+ * Writer for creating a graphical representation of a Graph in the DOT language.
+ * 
+ * @author David Withers
+ */
+public class DotWriter {
+	private static final String EOL = System.getProperty("line.separator");
+
+	private Writer writer;
+
+	/**
+	 * Constructs a new instance of DotWriter.
+	 *
+	 * @param writer
+	 */
+	public DotWriter(Writer writer) {
+		this.writer = writer;
+	}
+
+	/**
+	 * Writes a graphical representation of a Graph in the DOT language to a Writer.
+	 * 
+	 * @param graph
+	 * @throws IOException
+	 */
+	public void writeGraph(Graph graph) throws IOException {
+		writeLine("digraph \"" + graph.getId() + "\" {");
+
+		// Overall graph style
+		writeLine(" graph [");
+		writeLine("  bgcolor=\"" + getHexValue(graph.getFillColor()) + "\"");
+		writeLine("  color=\"black\"");
+		writeLine("  fontsize=\"10\"");
+		writeLine("  labeljust=\"left\"");
+		writeLine("  clusterrank=\"local\"");
+		writeLine("  ranksep=\"0.22\"");
+		writeLine("  nodesep=\"0.05\"");
+		// Set left to right view if alignment is horizontal
+		if (graph.getAlignment().equals(HORIZONTAL))
+			writeLine("  rankdir=\"LR\"");
+		writeLine(" ]");
+
+		// Overall node style
+		writeLine(" node [");
+		writeLine("  fontname=\"Helvetica\"");
+		writeLine("  fontsize=\"10\"");
+		writeLine("  fontcolor=\"black\"");
+		writeLine("  shape=\"record\"");
+		writeLine("  height=\"0\"");
+		writeLine("  width=\"0\"");
+		writeLine("  color=\"black\"");
+		writeLine("  fillcolor=\"lightgoldenrodyellow\"");
+		writeLine("  style=\"filled\"");
+		writeLine(" ];");
+
+		// Overall edge style
+		writeLine(" edge [");
+		writeLine("  fontname=\"Helvetica\"");
+		writeLine("  fontsize=\"8\"");
+		writeLine("  fontcolor=\"black\"");
+		writeLine("  color=\"black\"");
+		writeLine(" ];");
+
+		for (GraphNode node : graph.getNodes()) {
+			if (node.isExpanded())
+				writeSubGraph(node.getGraph(), " ");
+			else
+				writeNode(node, graph.getAlignment(), " ");
+		}
+
+		for (Graph subGraph : graph.getSubgraphs())
+			writeSubGraph(subGraph, " ");
+
+		for (GraphEdge edge : graph.getEdges())
+			writeEdges(edge, graph.getAlignment(), " ");
+
+		writeLine("}");
+	}
+
+	private void writeSubGraph(Graph graph, String indent) throws IOException {
+		writeLine(format("%ssubgraph \"cluster_%s\" {", indent, graph.getId()));
+		writeLine(format("%s rank=\"same\"", indent));
+
+		StringBuilder style = new StringBuilder();
+		if (graph.getFillColor() != null) {
+			writeLine(format("%s fillcolor=\"%s\"", indent,
+					getHexValue(graph.getFillColor())));
+			style.append("filled");
+		}
+		if (graph.getLineStyle() != null) {
+			style.append(style.length() == 0 ? "" : ",");
+			if (graph.getLineStyle().equals(NONE))
+				style.append("invis");
+			else
+				style.append(graph.getLineStyle().toString().toLowerCase());
+		}
+		writeLine(format("%s style=\"%s\"", indent, style));
+
+		if (graph.getLabel() != null)
+			writeLine(format("%s label=\"%s\"", indent, graph.getLabel()));
+
+		for(GraphNode node : graph.getNodes()) {
+			if (node.isExpanded())
+				writeSubGraph(node.getGraph(), indent + " ");
+			else
+				writeNode(node, graph.getAlignment(), indent + " ");
+		}
+
+		for (Graph subGraph : graph.getSubgraphs())
+			writeSubGraph(subGraph, indent + " ");
+
+		for (GraphEdge edge : graph.getEdges())
+			writeEdges(edge, graph.getAlignment(), indent + " ");
+
+		writeLine(indent + "}");
+	}
+
+	private void writeEdges(GraphEdge edge, Alignment alignment, String indent) throws IOException {
+		GraphNode source = edge.getSource();
+		GraphNode sink = edge.getSink();
+		String sourceId = "\"" + source.getId() + "\"";
+		String sinkId = "\"" + sink.getId() + "\"";
+		
+		if (source.getParent() instanceof GraphNode) {
+			GraphNode parent = (GraphNode) source.getParent();
+			sourceId = "\"" + parent.getId() + "\":" + sourceId;
+		}
+		if (sink.getParent() instanceof GraphNode) {
+			GraphNode parent = (GraphNode) sink.getParent();
+			sinkId = "\"" + parent.getId() + "\":" + sinkId;
+		}
+		/*
+		 * the compass point is required with newer versions of dot (e.g.
+		 * 2.26.3) but is not compatible with older versions (e.g. 1.3)
+		 */
+		if (alignment.equals(HORIZONTAL)) {
+			sourceId = sourceId + ":e";
+			sinkId = sinkId + ":w";
+		} else {
+			sourceId = sourceId + ":s";
+			sinkId = sinkId + ":n";			
+		}
+		writeLine(format("%s%s -> %s [", indent, sourceId, sinkId));
+		writeLine(format("%s arrowhead=\"%s\"", indent, edge
+				.getArrowHeadStyle().toString().toLowerCase()));
+		writeLine(format("%s, arrowtail=\"%s\"", indent, edge
+				.getArrowTailStyle().toString().toLowerCase()));
+		if (edge.getColor() != null)
+			writeLine(format("%s color=\"%s\"", indent,
+					getHexValue(edge.getColor())));
+		writeLine(format("%s]", indent));
+	}
+
+	private void writeNode(GraphNode node, Alignment alignment, String indent) throws IOException {
+		writeLine(format("%s\"%s\" [", indent, node.getId()));
+
+		StringBuilder style = new StringBuilder();
+		if (node.getFillColor() != null) {
+			writeLine(format("%s fillcolor=\"%s\"", indent,
+					getHexValue(node.getFillColor())));
+			style.append("filled");
+		}
+		if (node.getLineStyle() != null) {
+			style.append(style.length() == 0 ? "" : ",");
+			style.append(node.getLineStyle().toString().toLowerCase());
+		}
+		writeLine(format("%s style=\"%s\"", indent, style));
+
+		writeLine(format("%s shape=\"%s\"", indent, node.getShape().toString().toLowerCase()));
+		writeLine(format("%s width=\"%s\"", indent, node.getWidth() / 72f));
+		writeLine(format("%s height=\"%s\"", indent, node.getHeight() / 72f));
+
+		if (node.getShape().equals(RECORD)) {
+			StringBuilder labelString = new StringBuilder();
+			if (alignment.equals(VERTICAL)) {
+				labelString.append("{{");
+				addNodeLabels(node.getSinkNodes(), labelString);
+				labelString.append("}|").append(node.getLabel()).append("|{");
+				addNodeLabels(node.getSourceNodes(), labelString);
+				labelString.append("}}");
+			} else {
+				labelString.append(node.getLabel()).append("|{{");
+				addNodeLabels(node.getSinkNodes(), labelString);
+				labelString.append("}|{");
+				addNodeLabels(node.getSourceNodes(), labelString);
+				labelString.append("}}");
+			}
+			writeLine(format("%s label=\"%s\"", indent, labelString));
+		} else {
+			writeLine(format("%s label=\"%s\"", indent, node.getLabel()));
+		}
+
+		writeLine(format("%s];", indent));
+	}
+
+	private void addNodeLabels(List<GraphNode> nodes, StringBuilder labelString) {
+		String sep = "";
+		for (GraphNode node : nodes)
+			if (node.getLabel() != null) {
+				labelString.append(sep);
+				labelString.append("<");
+				labelString.append(node.getId());
+				labelString.append(">");
+				labelString.append(node.getLabel());
+				sep = "|";
+			}
+	}
+
+	private String getHexValue(Color color) {
+		return format("#%02x%02x%02x", color.getRed(), color.getGreen(),
+				color.getBlue());
+	}
+
+	private void writeLine(String line) throws IOException {
+		writer.write(line);
+		writer.write(EOL);
+	}
+}

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/Graph.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/Graph.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/Graph.java
new file mode 100644
index 0000000..0ff3852
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/Graph.java
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * 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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A graph model of a dataflow.
+ * 
+ * @author David Withers
+ */
+public class Graph extends GraphShapeElement {
+	public enum Alignment {
+		HORIZONTAL, VERTICAL
+	}
+
+	private List<GraphNode> nodes = new ArrayList<>();
+	private Set<GraphEdge> edges = new HashSet<>();
+	private Set<Graph> subgraphs = new HashSet<>();
+	private Alignment alignment = Alignment.VERTICAL;
+
+	/**
+	 * Constructs a Graph that uses the specified GraphEventManager to handle
+	 * any user generated events on GraphElements.
+	 * 
+	 * @param eventManager
+	 */
+	public Graph(GraphController graphController) {
+		super(graphController);
+	}
+
+	/**
+	 * Adds an edge to the Graph and sets its parent to be this Graph.
+	 * 
+	 * @param edge
+	 *            the edge to add
+	 */
+	public void addEdge(GraphEdge edge) {
+		edge.setParent(this);
+		edges.add(edge);
+	}
+
+	/**
+	 * Adds a node to the Graph and sets its parent to be this Graph.
+	 * 
+	 * @param node
+	 *            the node to add
+	 */
+	public void addNode(GraphNode node) {
+		node.setParent(this);
+		nodes.add(node);
+	}
+
+	/**
+	 * Adds a subgraph to the Graph and sets its parent to be this Graph.
+	 * 
+	 * @param subgraph
+	 *            the subgraph to add
+	 */
+	public void addSubgraph(Graph subgraph) {
+		subgraph.setParent(this);
+		subgraphs.add(subgraph);
+	}
+
+	/**
+	 * Returns the alignment of the Graph.
+	 * 
+	 * @return the alignment of the Graph
+	 */
+	public Alignment getAlignment() {
+		return alignment;
+	}
+
+	/**
+	 * Returns the edges contained in the Graph.
+	 * 
+	 * @return the edges contained in the Graph
+	 */
+	public Set<GraphEdge> getEdges() {
+		return Collections.unmodifiableSet(edges);
+	}
+
+	/**
+	 * Returns the nodes contained in the Graph.
+	 * 
+	 * @return the nodes contained in the Graph
+	 */
+	public List<GraphNode> getNodes() {
+		return Collections.unmodifiableList(nodes);
+	}
+
+	/**
+	 * Returns the subgraphs contained in the Graph.
+	 * 
+	 * @return the subgraphs contained in the Graph
+	 */
+	public Set<Graph> getSubgraphs() {
+		return Collections.unmodifiableSet(subgraphs);
+	}
+
+	/**
+	 * Removes an edge from the Graph.
+	 * 
+	 * @param edge
+	 *            the edge to remove
+	 * @return true if the edge is removed from the Graph
+	 */
+	public boolean removeEdge(GraphEdge edge) {
+		return edges.remove(edge);
+	}
+
+	/**
+	 * Removes a node from the Graph.
+	 * 
+	 * @param node
+	 *            the node to remove
+	 * @return true if the node is removed from the Graph
+	 */
+	public boolean removeNode(GraphNode node) {
+		return nodes.remove(node);
+	}
+
+	/**
+	 * Removes a subgraph from the Graph.
+	 * 
+	 * @param subgraph
+	 *            the subgraph to remove
+	 * @return true if the subgraph is removed from the Graph
+	 */
+	public boolean removeSubgraph(Graph subgraph) {
+		return subgraphs.remove(subgraph);
+	}
+
+	/**
+	 * Sets the alignment of the Graph.
+	 * 
+	 * @param alignment
+	 *            the new alignment
+	 */
+	public void setAlignment(Alignment alignment) {
+		this.alignment = alignment;
+	}
+}

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/GraphColorManager.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphColorManager.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphColorManager.java
new file mode 100644
index 0000000..1f44076
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphColorManager.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * 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 java.awt.Color;
+import java.lang.reflect.InvocationTargetException;
+
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+
+import org.apache.commons.beanutils.PropertyUtils;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+
+/**
+ * Manages the colour of elements in a graph.
+ *
+ * @author David Withers
+ * @author Start Owen
+ */
+public class GraphColorManager {
+	private static final String BEANSHELL = "http://ns.taverna.org.uk/2010/activity/beanshell";
+	private static final String LOCALWORKER = "http://ns.taverna.org.uk/2010/activity/localworker";
+
+	private static Color[] subGraphFillColors = new Color[] {
+			Color.decode("#ffffff"), Color.decode("#f0f8ff"),
+			Color.decode("#faebd7"), Color.decode("#f5f5dc") };
+
+	/**
+	 * Returns the colour associated with the Activity.
+	 *
+	 * For unknown activities Color.WHITE is returned.
+	 *
+	 * For {@link LocalworkerActivity} which have been user configured use the
+	 * BeanshellActivity colour
+	 *
+	 * @return the colour associated with the Activity
+	 */
+	public static Color getFillColor(Activity activity, ColourManager colourManager) {
+		try {
+			if (activity.getType().equals(LOCALWORKER)) {
+				// To avoid compile time dependency - read isAltered property as bean
+				if (Boolean.TRUE.equals(PropertyUtils.getProperty(activity, "altered"))) {
+					Color colour = colourManager.getPreferredColour(BEANSHELL);
+					return colour;
+				}
+			}
+		} catch (IllegalAccessException | InvocationTargetException
+				| NoSuchMethodException e) {
+		}
+		Color colour = colourManager.getPreferredColour(activity.getType().toASCIIString());
+		return colour;
+	}
+
+	public static Color getSubGraphFillColor(int depth) {
+		return subGraphFillColors[depth % subGraphFillColors.length];
+	}
+}

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/GraphController.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphController.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphController.java
new file mode 100644
index 0000000..0fb87a4
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphController.java
@@ -0,0 +1,1276 @@
+/*******************************************************************************
+ * 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 javax.swing.JOptionPane.PLAIN_MESSAGE;
+import static javax.swing.JOptionPane.showInputDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment;
+import net.sf.taverna.t2.workbench.models.graph.GraphEdge.ArrowStyle;
+import net.sf.taverna.t2.workbench.models.graph.GraphElement.LineStyle;
+import net.sf.taverna.t2.workbench.models.graph.GraphShapeElement.Shape;
+import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
+import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage;
+import net.sf.taverna.t2.workflow.edits.AddDataLinkEdit;
+import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.NamedSet;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.common.WorkflowBean;
+import uk.org.taverna.scufl2.api.core.BlockingControlLink;
+import uk.org.taverna.scufl2.api.core.ControlLink;
+import uk.org.taverna.scufl2.api.core.DataLink;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputActivityPort;
+import uk.org.taverna.scufl2.api.port.InputPort;
+import uk.org.taverna.scufl2.api.port.InputProcessorPort;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+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 uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+import uk.org.taverna.scufl2.api.port.Port;
+import uk.org.taverna.scufl2.api.port.ProcessorPort;
+import uk.org.taverna.scufl2.api.port.ReceiverPort;
+import uk.org.taverna.scufl2.api.port.SenderPort;
+import uk.org.taverna.scufl2.api.port.WorkflowPort;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+/**
+ * @author David Withers
+ */
+public abstract class GraphController implements
+		Observer<DataflowSelectionMessage> {
+	public enum PortStyle {
+		ALL {
+			@Override
+			Shape inputShape() {
+				return Shape.INVHOUSE;
+			}
+
+			@Override
+			Shape outputShape() {
+				return Shape.HOUSE;
+			}
+
+			@Override
+			Shape processorShape() {
+				return Shape.RECORD;
+			}
+		},
+		BOUND {
+			@Override
+			Shape inputShape() {
+				return Shape.INVHOUSE;
+			}
+
+			@Override
+			Shape outputShape() {
+				return Shape.HOUSE;
+			}
+
+			@Override
+			Shape processorShape() {
+				return Shape.RECORD;
+			}
+		},
+		NONE {
+			@Override
+			Shape inputShape() {
+				return Shape.BOX;
+			}
+
+			@Override
+			Shape outputShape() {
+				return Shape.BOX;
+			}
+
+			@Override
+			Shape processorShape() {
+				return Shape.BOX;
+			}
+		},
+		BLOB {
+			@Override
+			Shape inputShape() {
+				return Shape.CIRCLE;
+			}
+
+			@Override
+			Shape outputShape() {
+				return Shape.CIRCLE;
+			}
+
+			@Override
+			Shape processorShape() {
+				return Shape.CIRCLE;
+			}
+		};
+
+		abstract Shape inputShape();
+
+		abstract Shape outputShape();
+
+		abstract Shape processorShape();
+
+		Shape mergeShape() {
+			return Shape.CIRCLE;
+		}
+	}
+
+	private static Logger logger = Logger.getLogger(GraphController.class);
+
+	private Map<String, GraphElement> idToElement = new HashMap<>();
+	private Map<WorkflowBean, GraphElement> workflowToGraph = new HashMap<>();
+	private Map<Port, GraphNode> ports = new HashMap<>();
+	private Map<Graph, GraphNode> inputControls = new HashMap<>();
+	private Map<Graph, GraphNode> outputControls = new HashMap<>();
+	private Map<Port, Port> nestedWorkflowPorts = new HashMap<>();
+	private Map<WorkflowPort, ProcessorPort> workflowPortToProcessorPort = new HashMap<>();
+	private Map<Port, Processor> portToProcessor = new HashMap<>();
+
+	private EditManager editManager;
+	private final Workflow workflow;
+	private final Profile profile;
+	private DataflowSelectionModel dataflowSelectionModel;
+	private GraphEventManager graphEventManager;
+	private Component componentForPopups;
+
+	// graph settings
+	private PortStyle portStyle = PortStyle.NONE;
+	private Map<Processor, PortStyle> processorPortStyle = new HashMap<>();
+	private Alignment alignment = Alignment.VERTICAL;
+	private boolean expandNestedDataflows = true;
+	private Map<Activity, Boolean> dataflowExpansion = new HashMap<>();
+	protected Map<String, GraphElement> graphElementMap = new HashMap<>();
+	protected GraphElement edgeCreationSource, edgeCreationSink;
+	protected GraphEdge edgeMoveElement;
+	protected boolean edgeCreationFromSource = false;
+	protected boolean edgeCreationFromSink = false;
+	private Graph graph;
+	private boolean interactive;
+	private final ColourManager colourManager;
+
+	private Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	public GraphController(Workflow workflow, Profile profile,
+			boolean interactive, Component componentForPopups,
+			EditManager editManager, MenuManager menuManager,
+			ColourManager colourManager) {
+		this(workflow, profile, interactive, componentForPopups,
+				Alignment.VERTICAL, PortStyle.NONE, editManager, menuManager,
+				colourManager);
+	}
+
+	public GraphController(Workflow workflow, Profile profile,
+			boolean interactive, Component componentForPopups,
+			Alignment alignment, PortStyle portStyle, EditManager editManager,
+			MenuManager menuManager, ColourManager colourManager) {
+		this.workflow = workflow;
+		this.profile = profile;
+		this.interactive = interactive;
+		this.componentForPopups = componentForPopups;
+		this.alignment = alignment;
+		this.portStyle = portStyle;
+		this.editManager = editManager;
+		this.colourManager = colourManager;
+		this.graphEventManager = new DefaultGraphEventManager(this,
+				componentForPopups, menuManager);
+		graph = generateGraph();
+	}
+
+	public abstract Graph createGraph();
+
+	public abstract GraphNode createGraphNode();
+
+	public abstract GraphEdge createGraphEdge();
+
+	public void mapElement(String id, GraphElement element) {
+		idToElement.put(id, element);
+	}
+
+	public GraphElement getElement(String id) {
+		return idToElement.get(id);
+	}
+
+	public Graph getGraph() {
+		return graph;
+	}
+
+	public abstract void redraw();
+
+	/**
+	 * Generates a graph model of a dataflow.
+	 * 
+	 * @return
+	 */
+	public Graph generateGraph() {
+		workflowToGraph.clear();
+		ports.clear();
+		inputControls.clear();
+		outputControls.clear();
+		nestedWorkflowPorts.clear();
+		workflowPortToProcessorPort.clear();
+		graphElementMap.clear();
+		portToProcessor.clear();
+		return generateGraph(workflow, "", workflow.getName(), 0);
+	}
+
+	private Graph generateGraph(Workflow dataflow, String prefix, String name,
+			int depth) {
+		Graph graph = createGraph();
+		graph.setId(prefix + name);
+		graph.setAlignment(getAlignment());
+		if (getPortStyle().equals(PortStyle.BLOB) || depth == 0)
+			graph.setLabel("");
+		else
+			graph.setLabel(name);
+		graph.setFillColor(GraphColorManager.getSubGraphFillColor(depth));
+		if (depth == 0)
+			graph.setLineStyle(LineStyle.NONE);
+		else
+			graph.setLineStyle(LineStyle.SOLID);
+		graph.setColor(Color.BLACK);
+		graph.setShape(Shape.BOX);
+
+		if (depth == 0)
+			graph.setWorkflowBean(dataflow);
+		if (interactive)
+			graph.setWorkflowBean(dataflow);
+
+		// processors
+		for (Processor processor : dataflow.getProcessors())
+			graph.addNode(generateProcessorNode(processor, graph.getId(), depth));
+
+		// dataflow outputs
+		NamedSet<OutputWorkflowPort> outputPorts = dataflow.getOutputPorts();
+		if (outputPorts.size() > 0 || depth > 0)
+			graph.addSubgraph(generateOutputsGraph(outputPorts, graph.getId(),
+					graph, depth));
+
+		// dataflow inputs
+		NamedSet<InputWorkflowPort> inputPorts = dataflow.getInputPorts();
+		if (inputPorts.size() > 0 || depth > 0)
+			graph.addSubgraph(generateInputsGraph(inputPorts, graph.getId(),
+					graph, depth));
+
+		// datalinks
+		for (DataLink datalink : dataflow.getDataLinks()) {
+			GraphEdge edge = generateDataLinkEdge(datalink, depth);
+			if (edge != null)
+				graph.addEdge(edge);
+		}
+
+		// controlLinks
+		for (ControlLink controlLink : dataflow.getControlLinks())
+			if (controlLink instanceof BlockingControlLink) {
+				GraphEdge edge = generateControlLinkEdge(
+						(BlockingControlLink) controlLink, depth);
+				if (edge != null)
+					graph.addEdge(edge);
+			}
+
+		graphElementMap.put(graph.getId(), graph);
+		return graph;
+	}
+
+	public void transformGraph(Graph oldGraph, Graph newGraph) {
+		oldGraph.setAlignment(newGraph.getAlignment());
+		transformGraphElement(oldGraph, newGraph);
+		List<GraphEdge> oldEdges = new ArrayList<>(oldGraph.getEdges());
+		List<GraphEdge> newEdges = new ArrayList<>(newGraph.getEdges());
+		for (GraphEdge oldEdge : oldEdges) {
+			int index = newEdges.indexOf(oldEdge);
+			if (index >= 0) {
+				GraphEdge newEdge = newEdges.remove(index);
+				oldEdge.setPath(newEdge.getPath());
+				workflowToGraph.put(oldEdge.getWorkflowBean(), oldEdge);
+			} else
+				oldGraph.removeEdge(oldEdge);
+		}
+		List<GraphNode> newNodes = new ArrayList<>(newGraph.getNodes());
+		List<GraphNode> oldNodes = new ArrayList<>(oldGraph.getNodes());
+		for (GraphNode oldNode : oldNodes) {
+			int index = newNodes.indexOf(oldNode);
+			if (index >= 0) {
+				GraphNode newNode = newNodes.remove(index);
+				oldNode.setExpanded(newNode.isExpanded());
+				List<GraphNode> newSourceNodes = new ArrayList<>(
+						newNode.getSourceNodes());
+				List<GraphNode> oldSourceNodes = new ArrayList<>(
+						oldNode.getSourceNodes());
+				for (GraphNode oldSourceNode : oldSourceNodes) {
+					int sourceNodeIndex = newSourceNodes.indexOf(oldSourceNode);
+					if (sourceNodeIndex >= 0) {
+						GraphNode newSourceNode = newSourceNodes
+								.remove(sourceNodeIndex);
+						transformGraphElement(oldSourceNode, newSourceNode);
+					} else
+						oldNode.removeSourceNode(oldSourceNode);
+				}
+				for (GraphNode sourceNode : newSourceNodes)
+					oldNode.addSourceNode(sourceNode);
+				List<GraphNode> newSinkNodes = new ArrayList<>(
+						newNode.getSinkNodes());
+				List<GraphNode> oldSinkNodes = new ArrayList<>(
+						oldNode.getSinkNodes());
+				for (GraphNode oldSinkNode : oldSinkNodes) {
+					int sinkNodeIndex = newSinkNodes.indexOf(oldSinkNode);
+					if (sinkNodeIndex >= 0) {
+						GraphNode newSinkNode = newSinkNodes
+								.remove(sinkNodeIndex);
+						transformGraphElement(oldSinkNode, newSinkNode);
+					} else
+						oldNode.removeSinkNode(oldSinkNode);
+				}
+				for (GraphNode sinkNode : newSinkNodes)
+					oldNode.addSinkNode(sinkNode);
+				Graph oldSubGraph = oldNode.getGraph();
+				Graph newSubGraph = newNode.getGraph();
+				if (oldSubGraph != null && newSubGraph != null)
+					transformGraph(oldSubGraph, newSubGraph);
+				transformGraphElement(oldNode, newNode);
+			} else
+				oldGraph.removeNode(oldNode);
+		}
+		List<Graph> newSubGraphs = new ArrayList<>(newGraph.getSubgraphs());
+		List<Graph> oldSubGraphs = new ArrayList<>(oldGraph.getSubgraphs());
+		for (Graph oldSubGraph : oldSubGraphs) {
+			int index = newSubGraphs.indexOf(oldSubGraph);
+			if (index >= 0) {
+				Graph newSubGraph = newSubGraphs.remove(index);
+				transformGraph(oldSubGraph, newSubGraph);
+			} else
+				oldGraph.removeSubgraph(oldSubGraph);
+		}
+		for (GraphNode node : newNodes)
+			oldGraph.addNode(node);
+		for (Graph graph : newSubGraphs)
+			oldGraph.addSubgraph(graph);
+		for (GraphEdge newEdge : newEdges)
+			oldGraph.addEdge(newEdge);
+	}
+
+	public void transformGraphElement(GraphShapeElement oldGraphElement,
+			GraphShapeElement newGraphElement) {
+		oldGraphElement.setWorkflowBean(newGraphElement.getWorkflowBean());
+		oldGraphElement.setShape(newGraphElement.getShape());
+		oldGraphElement.setSize(newGraphElement.getSize());
+		oldGraphElement.setPosition(newGraphElement.getPosition());
+		oldGraphElement.setLabel(newGraphElement.getLabel());
+		oldGraphElement.setLabelPosition(newGraphElement.getLabelPosition());
+		oldGraphElement.setLineStyle(newGraphElement.getLineStyle());
+		oldGraphElement.setOpacity(newGraphElement.getOpacity());
+		oldGraphElement.setVisible(newGraphElement.isVisible());
+		oldGraphElement.setColor(newGraphElement.getColor());
+		oldGraphElement.setFillColor(newGraphElement.getFillColor());
+		workflowToGraph.put(oldGraphElement.getWorkflowBean(), oldGraphElement);
+	}
+
+	public void filterGraph(Set<?> dataflowEntities) {
+		Set<GraphElement> graphElements = new HashSet<>();
+		for (Entry<WorkflowBean, GraphElement> entry : workflowToGraph
+				.entrySet())
+			if (!dataflowEntities.contains(entry.getKey()))
+				graphElements.add(entry.getValue());
+		filterGraph(getGraph(), graphElements);
+	}
+
+	private void filterGraph(Graph graph, Set<GraphElement> graphElements) {
+		for (GraphNode node : graph.getNodes()) {
+			node.setFiltered(graphElements.contains(node));
+			Graph subgraph = node.getGraph();
+			if (subgraph != null)
+				if (graphElements.contains(subgraph)) {
+					removeFilter(subgraph);
+					subgraph.setFiltered(true);
+				} else {
+					subgraph.setFiltered(false);
+					filterGraph(subgraph, graphElements);
+				}
+		}
+		for (GraphEdge edge : graph.getEdges())
+			edge.setFiltered(graphElements.contains(edge));
+		for (Graph subgraph : graph.getSubgraphs())
+			if (graphElements.contains(subgraph)) {
+				removeFilter(subgraph);
+				subgraph.setFiltered(true);
+			} else {
+				subgraph.setFiltered(false);
+				filterGraph(subgraph, graphElements);
+			}
+	}
+
+	public void removeFilter() {
+		for (Entry<WorkflowBean, GraphElement> entry : workflowToGraph
+				.entrySet())
+			entry.getValue().setFiltered(false);
+	}
+
+	private void removeFilter(Graph graph) {
+		for (GraphNode node : graph.getNodes()) {
+			node.setOpacity(1f);
+			Graph subgraph = node.getGraph();
+			if (subgraph != null) {
+				subgraph.setFiltered(false);
+				removeFilter(subgraph);
+			}
+		}
+		for (GraphEdge edge : graph.getEdges())
+			edge.setFiltered(false);
+		for (Graph subgraph : graph.getSubgraphs()) {
+			subgraph.setFiltered(false);
+			removeFilter(subgraph);
+		}
+	}
+
+	private GraphEdge generateControlLinkEdge(BlockingControlLink condition,
+			int depth) {
+		GraphEdge edge = null;
+		GraphElement source = workflowToGraph.get(condition.getUntilFinished());
+		GraphElement sink = workflowToGraph.get(condition.getBlock());
+		if (source != null && sink != null) {
+			edge = createGraphEdge();
+			if (source instanceof Graph)
+				edge.setSource(outputControls.get(source));
+			else if (source instanceof GraphNode)
+				edge.setSource((GraphNode) source);
+			if (sink instanceof Graph)
+				edge.setSink(inputControls.get(sink));
+			else if (sink instanceof GraphNode)
+				edge.setSink((GraphNode) sink);
+			String sourceId = edge.getSource().getId();
+			String sinkId = edge.getSink().getId();
+			edge.setId(sourceId + "->" + sinkId);
+			edge.setLineStyle(LineStyle.SOLID);
+			edge.setColor(Color.decode("#505050"));
+			edge.setFillColor(null);
+			edge.setArrowHeadStyle(ArrowStyle.DOT);
+			if (depth == 0)
+				edge.setWorkflowBean(condition);
+			if (interactive)
+				edge.setWorkflowBean(condition);
+			workflowToGraph.put(condition, edge);
+			graphElementMap.put(edge.getId(), edge);
+		}
+		return edge;
+	}
+
+	private GraphEdge generateDataLinkEdge(DataLink datalink, int depth) {
+		GraphEdge edge = null;
+		Port sourcePort = datalink.getReceivesFrom();
+		Port sinkPort = datalink.getSendsTo();
+		if (nestedWorkflowPorts.containsKey(sourcePort))
+			sourcePort = nestedWorkflowPorts.get(sourcePort);
+		if (nestedWorkflowPorts.containsKey(sinkPort))
+			sinkPort = nestedWorkflowPorts.get(sinkPort);
+		GraphNode sourceNode = ports.get(sourcePort);
+		GraphNode sinkNode = ports.get(sinkPort);
+		if (sourceNode != null && sinkNode != null) {
+			edge = createGraphEdge();
+			edge.setSource(sourceNode);
+			edge.setSink(sinkNode);
+
+			StringBuilder id = new StringBuilder();
+			if (sourceNode.getParent() instanceof GraphNode) {
+				id.append(sourceNode.getParent().getId());
+				id.append(":");
+				id.append(sourceNode.getId());
+			} else
+				id.append(sourceNode.getId());
+			id.append("->");
+			if (sinkNode.getParent() instanceof GraphNode) {
+				id.append(sinkNode.getParent().getId());
+				id.append(":");
+				id.append(sinkNode.getId());
+			} else
+				id.append(sinkNode.getId());
+			edge.setId(id.toString());
+			edge.setLineStyle(LineStyle.SOLID);
+			edge.setColor(Color.BLACK);
+			edge.setFillColor(Color.BLACK);
+			if (depth == 0)
+				edge.setWorkflowBean(datalink);
+			if (interactive)
+				edge.setWorkflowBean(datalink);
+			workflowToGraph.put(datalink, edge);
+			graphElementMap.put(edge.getId(), edge);
+		}
+		return edge;
+	}
+
+	private Graph generateInputsGraph(NamedSet<InputWorkflowPort> inputPorts,
+			String prefix, Graph graph, int depth) {
+		Graph inputs = createGraph();
+		inputs.setId(prefix + "sources");
+		inputs.setColor(Color.BLACK);
+		inputs.setFillColor(null);
+		inputs.setShape(Shape.BOX);
+		inputs.setLineStyle(LineStyle.DOTTED);
+		if (getPortStyle().equals(PortStyle.BLOB))
+			inputs.setLabel("");
+		else
+			inputs.setLabel("Workflow input ports");
+
+		GraphNode triangle = createGraphNode();
+		triangle.setId(prefix + "WORKFLOWINTERNALSOURCECONTROL");
+		triangle.setLabel("");
+		triangle.setShape(Shape.TRIANGLE);
+		triangle.setSize(new Dimension((int) (0.2f * 72), (int) ((Math.sin(Math
+				.toRadians(60)) * 0.2) * 72)));
+		triangle.setFillColor(Color.decode("#ff4040"));
+		triangle.setColor(Color.BLACK);
+		triangle.setLineStyle(LineStyle.SOLID);
+		inputs.addNode(triangle);
+		inputControls.put(graph, triangle);
+
+		for (InputWorkflowPort inputWorkflowPort : inputPorts) {
+			GraphNode inputNode = createGraphNode();
+			inputNode.setId(prefix + "WORKFLOWINTERNALSOURCE_"
+					+ inputWorkflowPort.getName());
+			if (getPortStyle().equals(PortStyle.BLOB)) {
+				inputNode.setLabel("");
+				inputNode.setSize(new Dimension((int) (0.3f * 72),
+						(int) (0.3f * 72)));
+			} else
+				inputNode.setLabel(inputWorkflowPort.getName());
+			inputNode.setShape(getPortStyle().inputShape());
+			inputNode.setColor(Color.BLACK);
+			inputNode.setLineStyle(LineStyle.SOLID);
+			inputNode.setFillColor(Color.decode("#8ed6f0"));
+			if (depth == 0)
+				inputNode.setInteractive(true);
+			if (interactive)
+				inputNode.setInteractive(true);
+			if (depth < 2) {
+				inputNode.setWorkflowBean(inputWorkflowPort);
+				if (workflowPortToProcessorPort.containsKey(inputWorkflowPort)) {
+					ProcessorPort port = workflowPortToProcessorPort
+							.get(inputWorkflowPort);
+					inputNode.setWorkflowBean(port);
+					workflowToGraph.put(port, inputNode);
+				} else {
+					inputNode.setWorkflowBean(inputWorkflowPort);
+					workflowToGraph.put(inputWorkflowPort, inputNode);
+				}
+			}
+			ports.put(inputWorkflowPort, inputNode);
+			inputs.addNode(inputNode);
+			graphElementMap.put(inputNode.getId(), inputNode);
+		}
+		return inputs;
+	}
+
+	private Graph generateOutputsGraph(
+			NamedSet<OutputWorkflowPort> outputPorts, String prefix,
+			Graph graph, int depth) {
+		Graph outputs = createGraph();
+		outputs.setId(prefix + "sinks");
+		outputs.setColor(Color.BLACK);
+		outputs.setFillColor(null);
+		outputs.setShape(Shape.BOX);
+		outputs.setLineStyle(LineStyle.DOTTED);
+		if (getPortStyle().equals(PortStyle.BLOB))
+			outputs.setLabel("");
+		else
+			outputs.setLabel("Workflow output ports");
+
+		GraphNode triangle = createGraphNode();
+		triangle.setId(prefix + "WORKFLOWINTERNALSINKCONTROL");
+		triangle.setLabel("");
+		triangle.setShape(Shape.INVTRIANGLE);
+		triangle.setSize(new Dimension((int) (0.2f * 72), (int) ((Math.sin(Math
+				.toRadians(60)) * 0.2) * 72)));
+		triangle.setFillColor(Color.decode("#66cd00"));
+		triangle.setColor(Color.BLACK);
+		triangle.setLineStyle(LineStyle.SOLID);
+		outputs.addNode(triangle);
+		outputControls.put(graph, triangle);
+
+		for (OutputWorkflowPort outputWorkflowPort : outputPorts) {
+			GraphNode outputNode = createGraphNode();
+			outputNode.setId(prefix + "WORKFLOWINTERNALSINK_"
+					+ outputWorkflowPort.getName());
+			if (getPortStyle().equals(PortStyle.BLOB)) {
+				outputNode.setLabel("");
+				outputNode.setSize(new Dimension((int) (0.3f * 72),
+						(int) (0.3f * 72)));
+			} else
+				outputNode.setLabel(outputWorkflowPort.getName());
+			outputNode.setShape(getPortStyle().outputShape());
+			outputNode.setColor(Color.BLACK);
+			outputNode.setLineStyle(LineStyle.SOLID);
+			outputNode.setFillColor(Color.decode("#8ed6f0"));
+			if (depth == 0)
+				outputNode.setInteractive(true);
+			if (interactive)
+				outputNode.setInteractive(true);
+			if (depth < 2) {
+				if (workflowPortToProcessorPort.containsKey(outputWorkflowPort)) {
+					ProcessorPort port = workflowPortToProcessorPort
+							.get(outputWorkflowPort);
+					outputNode.setWorkflowBean(port);
+					workflowToGraph.put(port, outputNode);
+				} else {
+					outputNode.setWorkflowBean(outputWorkflowPort);
+					workflowToGraph.put(outputWorkflowPort, outputNode);
+				}
+			}
+			ports.put(outputWorkflowPort, outputNode);
+			outputs.addNode(outputNode);
+			graphElementMap.put(outputNode.getId(), outputNode);
+		}
+		return outputs;
+	}
+
+	private GraphNode generateProcessorNode(Processor processor, String prefix,
+			int depth) {
+		// Blatantly ignoring any other activities for now
+		ProcessorBinding processorBinding = scufl2Tools
+				.processorBindingForProcessor(processor, profile);
+		Activity activity = processorBinding.getBoundActivity();
+		@SuppressWarnings("unused")
+		URI activityType = activity.getType();
+
+		GraphNode node = createGraphNode();
+		node.setId(prefix + processor.getName());
+		if (getPortStyle().equals(PortStyle.BLOB)) {
+			node.setLabel("");
+			node.setSize(new Dimension((int) (0.3f * 72), (int) (0.3f * 72)));
+		} else
+			node.setLabel(processor.getName());
+		node.setShape(getPortStyle(processor).processorShape());
+		node.setColor(Color.BLACK);
+		node.setLineStyle(LineStyle.SOLID);
+		// if (activityType.equals(URI.create(NonExecutableActivity.URI))) {
+		// if (activityType.equals(URI.create(DisabledActivity.URI))) {
+		// node.setFillColor(GraphColorManager
+		// .getFillColor(((DisabledActivity) activity)
+		// .getActivity(), colourManager));
+		// } else {
+		// node.setFillColor(GraphColorManager
+		// .getFillColor(activityType, colourManager));
+		// }
+		// node.setOpacity(0.3f);
+		// } else
+		node.setFillColor(GraphColorManager.getFillColor(activity,
+				colourManager));
+
+		// check whether the nested workflow processors should be clickable or
+		// not, if top level workflow then should be clickable regardless
+		if (depth == 0) {
+			node.setInteractive(true);
+			node.setWorkflowBean(processor);
+		}
+		if (interactive) {
+			node.setInteractive(true);
+			node.setWorkflowBean(processor);
+		}
+
+		if (scufl2Tools.containsNestedWorkflow(processor, profile)
+				&& expandNestedDataflow(activity)) {
+			Workflow subDataflow = scufl2Tools.nestedWorkflowForProcessor(
+					processor, profile);
+
+			NamedSet<InputWorkflowPort> inputWorkflowPorts = subDataflow
+					.getInputPorts();
+			for (InputActivityPort inputActivityPort : activity.getInputPorts()) {
+				InputWorkflowPort inputWorkflowPort = inputWorkflowPorts
+						.getByName(inputActivityPort.getName());
+				InputProcessorPort inputProcessorPort = scufl2Tools
+						.processorPortBindingForPort(inputActivityPort, profile)
+						.getBoundProcessorPort();
+				nestedWorkflowPorts.put(inputProcessorPort, inputWorkflowPort);
+				workflowPortToProcessorPort.put(inputWorkflowPort,
+						inputProcessorPort);
+				processorBinding.getInputPortBindings();
+			}
+
+			NamedSet<OutputWorkflowPort> outputWorkflowPorts = subDataflow
+					.getOutputPorts();
+			for (OutputActivityPort outputActivityPort : activity
+					.getOutputPorts()) {
+				OutputWorkflowPort outputWorkflowPort = outputWorkflowPorts
+						.getByName(outputActivityPort.getName());
+				OutputProcessorPort outputProcessorPort = scufl2Tools
+						.processorPortBindingForPort(outputActivityPort,
+								profile).getBoundProcessorPort();
+				nestedWorkflowPorts
+						.put(outputProcessorPort, outputWorkflowPort);
+				workflowPortToProcessorPort.put(outputWorkflowPort,
+						outputProcessorPort);
+			}
+
+			Graph subGraph = generateGraph(subDataflow, prefix,
+					processor.getName(), depth + 1);
+			// TODO why does this depth matter?
+			if (depth == 0)
+				subGraph.setWorkflowBean(processor);
+			if (interactive)
+				subGraph.setWorkflowBean(processor);
+			node.setGraph(subGraph);
+			node.setExpanded(true);
+
+			workflowToGraph.put(processor, subGraph);
+		} else {
+			graphElementMap.put(node.getId(), node);
+			workflowToGraph.put(processor, node);
+		}
+
+		NamedSet<InputProcessorPort> inputPorts = processor.getInputPorts();
+		if (inputPorts.size() == 0) {
+			GraphNode portNode = createGraphNode();
+			portNode.setShape(Shape.BOX);
+			portNode.setColor(Color.BLACK);
+			portNode.setFillColor(node.getFillColor());
+			portNode.setLineStyle(LineStyle.SOLID);
+			node.addSinkNode(portNode);
+		} else
+			for (InputPort inputPort : inputPorts) {
+				GraphNode portNode = createGraphNode();
+				portNode.setId("i" + inputPort.getName().replaceAll("\\.", ""));
+				portNode.setLabel(inputPort.getName());
+				portNode.setShape(Shape.BOX);
+				portNode.setColor(Color.BLACK);
+				portNode.setFillColor(node.getFillColor());
+				portNode.setLineStyle(LineStyle.SOLID);
+				if (depth == 0)
+					portNode.setWorkflowBean(inputPort);
+				if (interactive)
+					portNode.setWorkflowBean(inputPort);
+				if (!node.isExpanded())
+					workflowToGraph.put(inputPort, portNode);
+				ports.put(inputPort, portNode);
+				node.addSinkNode(portNode);
+				graphElementMap.put(portNode.getId(), portNode);
+				// portToActivity.put(inputPort, activity);
+				portToProcessor.put(inputPort, processor);
+			}
+
+		NamedSet<OutputProcessorPort> outputPorts = processor.getOutputPorts();
+		if (outputPorts.size() == 0) {
+			GraphNode portNode = createGraphNode();
+			portNode.setShape(Shape.BOX);
+			portNode.setColor(Color.BLACK);
+			portNode.setFillColor(node.getFillColor());
+			portNode.setLineStyle(LineStyle.SOLID);
+			node.addSourceNode(portNode);
+		} else
+			for (OutputPort outputPort : outputPorts) {
+				GraphNode portNode = createGraphNode();
+				portNode.setId("o" + outputPort.getName().replaceAll("\\.", ""));
+				portNode.setLabel(outputPort.getName());
+				portNode.setShape(Shape.BOX);
+				portNode.setColor(Color.BLACK);
+				portNode.setFillColor(node.getFillColor());
+				portNode.setLineStyle(LineStyle.SOLID);
+				if (depth == 0)
+					portNode.setWorkflowBean(outputPort);
+				if (interactive)
+					portNode.setWorkflowBean(outputPort);
+				if (!node.isExpanded())
+					workflowToGraph.put(outputPort, portNode);
+				ports.put(outputPort, portNode);
+				node.addSourceNode(portNode);
+				graphElementMap.put(portNode.getId(), portNode);
+				// portToActivity.put(outputPort, activity);
+				portToProcessor.put(outputPort, processor);
+			}
+
+		return node;
+	}
+
+	/**
+	 * Returns the dataflow.
+	 * 
+	 * @return the dataflow
+	 */
+	public Workflow getWorkflow() {
+		return workflow;
+	}
+
+	public Profile getProfile() {
+		return profile;
+	}
+
+	/**
+	 * Returns the dataflowSelectionModel.
+	 * 
+	 * @return the dataflowSelectionModel
+	 */
+	public DataflowSelectionModel getDataflowSelectionModel() {
+		return dataflowSelectionModel;
+	}
+
+	/**
+	 * Sets the dataflowSelectionModel.
+	 * 
+	 * @param dataflowSelectionModel
+	 *            the new dataflowSelectionModel
+	 */
+	public void setDataflowSelectionModel(
+			DataflowSelectionModel dataflowSelectionModel) {
+		if (this.dataflowSelectionModel != null)
+			this.dataflowSelectionModel.removeObserver(this);
+		this.dataflowSelectionModel = dataflowSelectionModel;
+		this.dataflowSelectionModel.addObserver(this);
+	}
+
+	/**
+	 * Sets the proportion of the node's jobs that have been completed.
+	 * 
+	 * @param nodeId
+	 *            the id of the node
+	 * @param complete
+	 *            the proportion of the nodes's jobs that have been completed, a
+	 *            value between 0.0 and 1.0
+	 */
+	public void setNodeCompleted(String nodeId, float complete) {
+		if (graphElementMap.containsKey(nodeId)) {
+			GraphElement graphElement = graphElementMap.get(nodeId);
+			graphElement.setCompleted(complete);
+		}
+	}
+
+	public void setEdgeActive(String edgeId, boolean active) {
+	}
+
+	/**
+	 * Returns the alignment.
+	 * 
+	 * @return the alignment
+	 */
+	public Alignment getAlignment() {
+		return alignment;
+	}
+
+	/**
+	 * Returns the portStyle.
+	 * 
+	 * @return the portStyle
+	 */
+	public PortStyle getPortStyle() {
+		return portStyle;
+	}
+
+	/**
+	 * Returns the portStyle for a processor.
+	 * 
+	 * @return the portStyle for a processor
+	 */
+	public PortStyle getPortStyle(Processor processor) {
+		if (processorPortStyle.containsKey(processor))
+			return processorPortStyle.get(processor);
+		return portStyle;
+	}
+
+	/**
+	 * Sets the alignment.
+	 * 
+	 * @param alignment
+	 *            the new alignment
+	 */
+	public void setAlignment(Alignment alignment) {
+		this.alignment = alignment;
+	}
+
+	/**
+	 * Sets the portStyle.
+	 * 
+	 * @param style
+	 *            the new portStyle
+	 */
+	public void setPortStyle(PortStyle portStyle) {
+		this.portStyle = portStyle;
+		processorPortStyle.clear();
+	}
+
+	/**
+	 * Sets the portStyle for a processor.
+	 * 
+	 * @param style
+	 *            the new portStyle for the processor
+	 */
+	public void setPortStyle(Processor processor, PortStyle portStyle) {
+		processorPortStyle.put(processor, portStyle);
+	}
+
+	/**
+	 * Shut down any processing and update threads related to this controller.
+	 * 
+	 */
+	public void shutdown() {
+	}
+
+	/**
+	 * Returns true if the default is to expand nested workflows.
+	 * 
+	 * @return true if the default is to expand nested workflows
+	 */
+	public boolean expandNestedDataflows() {
+		return expandNestedDataflows;
+	}
+
+	/**
+	 * Returns true if the nested dataflow should be expanded.
+	 * 
+	 * @param dataflow
+	 * @return true if the nested dataflow should be expanded
+	 */
+	public boolean expandNestedDataflow(Activity dataflow) {
+		if (dataflowExpansion.containsKey(dataflow))
+			return dataflowExpansion.get(dataflow);
+		return expandNestedDataflows;
+	}
+
+	/**
+	 * Sets the default for expanding nested workflows.
+	 * 
+	 * @param expand
+	 *            the default for expanding nested workflows
+	 */
+	public void setExpandNestedDataflows(boolean expand) {
+		dataflowExpansion.clear();
+		this.expandNestedDataflows = expand;
+	}
+
+	/**
+	 * Sets whether the nested dataflow should be expanded.
+	 * 
+	 * @param expand
+	 *            whether the nested dataflow should be expanded
+	 * @param dataflow
+	 *            the nested dataflow
+	 */
+	public void setExpandNestedDataflow(Activity dataflow, boolean expand) {
+		dataflowExpansion.put(dataflow, expand);
+	}
+
+	private boolean isSingleOutputProcessor(Object dataflowObject) {
+		boolean result = false;
+		if (dataflowObject instanceof Processor) {
+			Processor processor = (Processor) dataflowObject;
+			result = processor.getOutputPorts().size() == 1;
+		}
+		return result;
+	}
+
+	public boolean startEdgeCreation(GraphElement graphElement, Point point) {
+		if (!edgeCreationFromSource && !edgeCreationFromSink) {
+			Object dataflowObject = graphElement.getWorkflowBean();
+			if (dataflowObject instanceof ReceiverPort) {
+				edgeCreationSink = graphElement;
+				edgeCreationFromSink = true;
+			} else if (dataflowObject instanceof SenderPort
+					|| isSingleOutputProcessor(dataflowObject)) {
+				edgeCreationSource = graphElement;
+				edgeCreationFromSource = true;
+			} else if (graphElement instanceof GraphEdge) {
+				GraphEdge edge = (GraphEdge) graphElement;
+				edgeCreationSource = edge.getSource();
+				edgeCreationFromSource = true;
+				edgeMoveElement = edge;
+			}
+		}
+		return edgeCreationFromSource || edgeCreationFromSink;
+	}
+
+	public boolean moveEdgeCreationTarget(GraphElement graphElement, Point point) {
+		boolean edgeValid = false;
+		Object dataflowObject = graphElement.getWorkflowBean();
+		if (edgeCreationFromSink) {
+			if (graphElement instanceof GraphNode) {
+				Object sinkObject = edgeCreationSink.getWorkflowBean();
+				if (dataflowObject instanceof OutputPort) {
+					Processor sourceProcessor = portToProcessor
+							.get(dataflowObject);
+					if (sourceProcessor != null) {
+						Processor sinkProcessor = null;
+						if (sinkObject instanceof Processor)
+							sinkProcessor = (Processor) sinkObject;
+						else if (portToProcessor.containsKey(sinkObject))
+							sinkProcessor = portToProcessor.get(sinkObject);
+						if (sinkProcessor != null) {
+							Set<Processor> possibleSinkProcessors = scufl2Tools
+									.possibleDownStreamProcessors(workflow,
+											sourceProcessor);
+							if (possibleSinkProcessors.contains(sinkProcessor)) {
+								edgeCreationSource = graphElement;
+								edgeValid = true;
+							}
+						}
+						if (sinkObject instanceof OutputWorkflowPort) {
+							edgeCreationSource = graphElement;
+							edgeValid = true;
+						}
+					}
+				} else if (dataflowObject instanceof InputWorkflowPort) {
+					edgeCreationSource = graphElement;
+					edgeValid = true;
+				} else if (dataflowObject instanceof Processor) {
+					Processor sourceProcessor = (Processor) dataflowObject;
+					Processor sinkProcessor = null;
+					if (sinkObject instanceof Processor)
+						sinkProcessor = (Processor) sinkObject;
+					else if (portToProcessor.containsKey(sinkObject))
+						sinkProcessor = portToProcessor.get(sinkObject);
+					if (sinkProcessor != null) {
+						Set<Processor> possibleSinkProcessors = scufl2Tools
+								.possibleDownStreamProcessors(workflow,
+										sourceProcessor);
+						if (possibleSinkProcessors.contains(sinkProcessor)) {
+							edgeCreationSource = graphElement;
+							edgeValid = true;
+						}
+					}
+					if (sinkObject instanceof OutputWorkflowPort) {
+						edgeCreationSource = graphElement;
+						edgeValid = true;
+					}
+				}
+			}
+			if (!edgeValid)
+				edgeCreationSource = null;
+		} else if (edgeCreationFromSource) {
+			if (graphElement instanceof GraphNode) {
+				Object sourceObject = edgeCreationSource.getWorkflowBean();
+				if (dataflowObject instanceof InputPort) {
+					Processor sinkProcessor = portToProcessor
+							.get(dataflowObject);
+					if (sinkProcessor != null) {
+						Processor sourceProcessor = null;
+						if (sourceObject instanceof Processor)
+							sourceProcessor = (Processor) sourceObject;
+						else if (portToProcessor.containsKey(sourceObject))
+							sourceProcessor = portToProcessor.get(sourceObject);
+						if (sourceProcessor != null) {
+							Set<Processor> possibleSourceProcessors = scufl2Tools
+									.possibleUpStreamProcessors(workflow,
+											sinkProcessor);
+							if (possibleSourceProcessors
+									.contains(sourceProcessor)) {
+								edgeCreationSink = graphElement;
+								edgeValid = true;
+							}
+						}
+						if (sourceObject instanceof InputWorkflowPort) {
+							edgeCreationSink = graphElement;
+							edgeValid = true;
+						}
+					}
+				} else if (dataflowObject instanceof OutputWorkflowPort) {
+					if (sourceObject != null) {
+						edgeCreationSink = graphElement;
+						edgeValid = true;
+					}
+				} else if (dataflowObject instanceof Processor) {
+					Processor sinkProcessor = (Processor) dataflowObject;
+					Processor sourceProcessor = null;
+					if (sourceObject instanceof Processor)
+						sourceProcessor = (Processor) sourceObject;
+					else if (portToProcessor.containsKey(sourceObject))
+						sourceProcessor = portToProcessor.get(sourceObject);
+					if (sourceProcessor != null) {
+						Set<Processor> possibleSourceProcessors = scufl2Tools
+								.possibleUpStreamProcessors(workflow,
+										sinkProcessor);
+						if (possibleSourceProcessors.contains(sourceProcessor)) {
+							edgeCreationSink = graphElement;
+							edgeValid = true;
+						}
+					}
+					if (sourceObject instanceof InputWorkflowPort) {
+						edgeCreationSink = graphElement;
+						edgeValid = true;
+					}
+				}
+			}
+			if (!edgeValid)
+				edgeCreationSink = null;
+		}
+		return edgeValid;
+	}
+
+	public boolean stopEdgeCreation(GraphElement graphElement, Point point) {
+		boolean edgeCreated = false;
+		if (edgeCreationSource != null && edgeCreationSink != null) {
+			SenderPort source = null;
+			ReceiverPort sink = null;
+			Object sourceDataflowObject = edgeCreationSource.getWorkflowBean();
+			Object sinkDataflowObject = edgeCreationSink.getWorkflowBean();
+			if (sourceDataflowObject instanceof SenderPort)
+				source = (SenderPort) sourceDataflowObject;
+			else if (sourceDataflowObject instanceof Processor) {
+				Processor processor = (Processor) sourceDataflowObject;
+				source = showPortOptions(processor.getOutputPorts(), "output",
+						componentForPopups, point);
+			}
+			if (sinkDataflowObject instanceof ReceiverPort)
+				sink = (ReceiverPort) sinkDataflowObject;
+			else if (sinkDataflowObject instanceof Processor) {
+				Processor processor = (Processor) sinkDataflowObject;
+				sink = showPortOptions(processor.getInputPorts(), "input",
+						componentForPopups, point);
+			}
+			if (source != null && sink != null) {
+				Edit<?> edit = null;
+				if (edgeMoveElement == null) {
+					DataLink dataLink = new DataLink();
+					dataLink.setReceivesFrom(source);
+					dataLink.setSendsTo(sink);
+					edit = new AddDataLinkEdit(workflow, dataLink);
+				} else {
+					Object existingSink = edgeMoveElement.getSink()
+							.getWorkflowBean();
+					if (existingSink != sink) {
+						List<Edit<?>> editList = new ArrayList<Edit<?>>();
+						DataLink existingDataLink = (DataLink) edgeMoveElement
+								.getWorkflowBean();
+						DataLink newDataLink = new DataLink();
+						newDataLink.setReceivesFrom(existingDataLink
+								.getReceivesFrom());
+						newDataLink.setSendsTo(sink);
+						editList.add(new RemoveDataLinkEdit(workflow,
+								existingDataLink));
+						editList.add(new AddDataLinkEdit(workflow, newDataLink));
+						edit = new CompoundEdit(editList);
+					}
+				}
+				try {
+					if (edit != null) {
+						editManager.doDataflowEdit(workflow.getParent(), edit);
+						edgeCreated = true;
+					}
+				} catch (EditException e) {
+					logger.debug("Failed to create datalink from '"
+							+ source.getName() + "' to '" + sink.getName()
+							+ "'");
+				}
+			}
+		}
+		edgeCreationSource = null;
+		edgeCreationSink = null;
+		edgeMoveElement = null;
+		edgeCreationFromSource = false;
+		edgeCreationFromSink = false;
+
+		return edgeCreated;
+	}
+
+	private <T extends Port> T showPortOptions(NamedSet<T> ports,
+			String portType, Component component, Point point) {
+		T result = null;
+		if (ports.size() == 0) {
+			showMessageDialog(component, "Service has no " + portType
+					+ " ports to connect to");
+		} else if (ports.size() == 1)
+			result = ports.first();
+		else {
+			Object[] portNames = ports.getNames().toArray();
+			String portName = (String) showInputDialog(component, "Select an "
+					+ portType + " port", "Port Chooser", PLAIN_MESSAGE, null,
+					portNames, portNames[0]);
+			if (portName != null)
+				result = ports.getByName(portName);
+		}
+		return result;
+
+	}
+
+	public void resetSelection() {
+		if (dataflowSelectionModel != null)
+			for (Object dataflowElement : dataflowSelectionModel.getSelection()) {
+				GraphElement graphElement = workflowToGraph
+						.get(dataflowElement);
+				if (graphElement != null)
+					graphElement.setSelected(true);
+			}
+	}
+
+	public void setIteration(String nodeId, int iteration) {
+		if (graphElementMap.containsKey(nodeId)) {
+			GraphElement graphElement = graphElementMap.get(nodeId);
+			graphElement.setIteration(iteration);
+		}
+	}
+
+	public void setErrors(String nodeId, int errors) {
+		if (graphElementMap.containsKey(nodeId)) {
+			GraphElement graphElement = graphElementMap.get(nodeId);
+			graphElement.setErrors(errors);
+		}
+	}
+
+	@Override
+	public void notify(Observable<DataflowSelectionMessage> sender,
+			DataflowSelectionMessage message) throws Exception {
+		GraphElement graphElement = workflowToGraph.get(message.getElement());
+		if (graphElement != null)
+			graphElement.setSelected(message.getType().equals(
+					DataflowSelectionMessage.Type.ADDED));
+	}
+
+	/**
+	 * Returns the GraphEventManager.
+	 * 
+	 * @return the GraphEventManager
+	 */
+	public GraphEventManager getGraphEventManager() {
+		return graphEventManager;
+	}
+
+	/**
+	 * Sets the GraphEventManager.
+	 * 
+	 * @param graphEventManager
+	 *            the new GraphEventManager
+	 */
+	public void setGraphEventManager(GraphEventManager graphEventManager) {
+		this.graphEventManager = graphEventManager;
+	}
+}

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/GraphEdge.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEdge.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEdge.java
new file mode 100644
index 0000000..d1348d2
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEdge.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * 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 java.awt.Point;
+import java.util.List;
+
+/**
+ * An edge connecting two nodes in a graph.
+ *
+ * @author David Withers
+ */
+public class GraphEdge extends GraphElement {
+	public enum ArrowStyle {NONE, NORMAL, DOT}
+
+	private GraphNode source;
+	private GraphNode sink;
+	private ArrowStyle arrowHeadStyle = ArrowStyle.NORMAL;
+	private ArrowStyle arrowTailStyle = ArrowStyle.NONE;
+	private List<Point> path;
+
+	/**
+	 * Constructs a new instance of Edge.
+	 *
+	 */
+	public GraphEdge(GraphController graphController) {
+		super(graphController);
+	}
+
+	/**
+	 * Returns the source.
+	 *
+	 * @return the source
+	 */
+	public GraphNode getSource() {
+		return source;
+	}
+
+	/**
+	 * Sets the source.
+	 *
+	 * @param source the new source
+	 */
+	public void setSource(GraphNode source) {
+		this.source = source;
+	}
+
+	/**
+	 * Returns the sink.
+	 *
+	 * @return the sink
+	 */
+	public GraphNode getSink() {
+		return sink;
+	}
+
+	/**
+	 * Sets the sink.
+	 *
+	 * @param sink the new sink
+	 */
+	public void setSink(GraphNode sink) {
+		this.sink = sink;
+	}
+
+	/**
+	 * Returns the arrowHeadStyle.
+	 *
+	 * @return the arrowHeadStyle
+	 */
+	public ArrowStyle getArrowHeadStyle() {
+		return arrowHeadStyle;
+	}
+
+	/**
+	 * Sets the arrowHeadStyle.
+	 *
+	 * @param arrowHeadStyle the new arrowHeadStyle
+	 */
+	public void setArrowHeadStyle(ArrowStyle arrowHeadStyle) {
+		this.arrowHeadStyle = arrowHeadStyle;
+	}
+
+	/**
+	 * Returns the arrowTailStyle.
+	 *
+	 * @return the arrowTailStyle
+	 */
+	public ArrowStyle getArrowTailStyle() {
+		return arrowTailStyle;
+	}
+
+	/**
+	 * Sets the arrowTailStyle.
+	 *
+	 * @param arrowTailStyle the new arrowTailStyle
+	 */
+	public void setArrowTailStyle(ArrowStyle arrowTailStyle) {
+		this.arrowTailStyle = arrowTailStyle;
+	}
+
+	/**
+	 * Returns the path.
+	 *
+	 * @return the path
+	 */
+	public List<Point> getPath() {
+		return path;
+	}
+
+	/**
+	 * Sets the path.
+	 *
+	 * @param path the new path
+	 */
+	public void setPath(List<Point> path) {
+		this.path = path;
+	}
+}

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/GraphElement.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphElement.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphElement.java
new file mode 100644
index 0000000..8bb7bc8
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphElement.java
@@ -0,0 +1,430 @@
+/*******************************************************************************
+ * 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 java.awt.Color;
+import java.awt.Point;
+
+import uk.org.taverna.scufl2.api.common.WorkflowBean;
+
+/**
+ * An element of a graph.
+ * 
+ * @author David Withers
+ */
+public abstract class GraphElement {
+	public enum LineStyle {
+		NONE, SOLID, DOTTED
+	}
+
+	private String id;
+	private String label;
+	private Point labelPosition;
+	private LineStyle lineStyle = LineStyle.SOLID;
+	private Color color = Color.BLACK;
+	private Color fillColor;
+	private float opacity = 1f;
+	private GraphElement parent;
+	private boolean selected;
+	private boolean active;
+	private boolean interactive;
+	private boolean visible = true;
+	private boolean filtered;
+	private WorkflowBean workflowBean;
+	protected GraphController graphController;
+	protected float completed;
+	protected int iteration;
+	protected int errors;
+
+	protected GraphElement(GraphController graphController) {
+		this.graphController = graphController;
+	}
+
+	/**
+	 * Returns the eventManager.
+	 * 
+	 * @return the eventManager
+	 */
+	public GraphEventManager getEventManager() {
+		if (graphController != null) {
+			return graphController.getGraphEventManager();
+		}
+		return null;
+	}
+
+	/**
+	 * Returns the workflowBean.
+	 * 
+	 * @return the workflowBean
+	 */
+	public WorkflowBean getWorkflowBean() {
+		return workflowBean;
+	}
+
+	/**
+	 * Sets the workflowBean.
+	 * 
+	 * @param workflowBean
+	 *            the new workflowBean
+	 */
+	public void setWorkflowBean(WorkflowBean workflowBean) {
+		this.workflowBean = workflowBean;
+	}
+
+	/**
+	 * Returns the parent.
+	 * 
+	 * @return the parent
+	 */
+	public GraphElement getParent() {
+		return parent;
+	}
+
+	/**
+	 * Sets the parent.
+	 * 
+	 * @param parent
+	 *            the new parent
+	 */
+	protected void setParent(GraphElement parent) {
+		this.parent = parent;
+	}
+
+	/**
+	 * Returns the label.
+	 * 
+	 * @return the label
+	 */
+	public String getLabel() {
+		return label;
+	}
+
+	/**
+	 * Sets the label.
+	 * 
+	 * @param label
+	 *            the new label
+	 */
+	public void setLabel(String label) {
+		this.label = label;
+	}
+
+	/**
+	 * Returns the labelPosition.
+	 * 
+	 * @return the labelPosition
+	 */
+	public Point getLabelPosition() {
+		return labelPosition;
+	}
+
+	/**
+	 * Sets the labelPosition.
+	 * 
+	 * @param labelPosition
+	 *            the new labelPosition
+	 */
+	public void setLabelPosition(Point labelPosition) {
+		this.labelPosition = labelPosition;
+	}
+
+	/**
+	 * Returns the id.
+	 * 
+	 * @return the id
+	 */
+	public String getId() {
+		return id;
+	}
+
+	/**
+	 * Sets the id.
+	 * 
+	 * @param id
+	 *            the new id
+	 */
+	public void setId(String id) {
+		if (graphController != null) {
+			graphController.mapElement(id, this);
+		}
+		this.id = id;
+	}
+
+	/**
+	 * Returns the colour.
+	 * 
+	 * @return the colour
+	 */
+	public Color getColor() {
+		return color;
+	}
+
+	/**
+	 * Sets the colour.
+	 * 
+	 * @param color
+	 *            the new colour
+	 */
+	public void setColor(Color color) {
+		this.color = color;
+	}
+
+	/**
+	 * Returns the fillColor.
+	 * 
+	 * @return the fillColor
+	 */
+	public Color getFillColor() {
+		return fillColor;
+	}
+
+	/**
+	 * Sets the fillColor.
+	 * 
+	 * @param fillColor
+	 *            the new fillColor
+	 */
+	public void setFillColor(Color fillColor) {
+		this.fillColor = fillColor;
+	}
+
+	/**
+	 * Returns the lineStyle.
+	 * 
+	 * @return the lineStyle
+	 */
+	public LineStyle getLineStyle() {
+		return lineStyle;
+	}
+
+	/**
+	 * Sets the lineStyle.
+	 * 
+	 * @param lineStyle
+	 *            the new lineStyle
+	 */
+	public void setLineStyle(LineStyle lineStyle) {
+		this.lineStyle = lineStyle;
+	}
+
+	@Override
+	public String toString() {
+		return id + "[" + label + "]";
+	}
+
+	/**
+	 * Returns the selected.
+	 * 
+	 * @return the selected
+	 */
+	public boolean isSelected() {
+		return selected;
+	}
+
+	/**
+	 * Sets the selected.
+	 * 
+	 * @param selected
+	 *            the new selected
+	 */
+	public void setSelected(boolean selected) {
+		this.selected = selected;
+	}
+
+	/**
+	 * Returns the iteration.
+	 * 
+	 * @return the value of iteration
+	 */
+	public int getIteration() {
+		return iteration;
+	}
+
+	/**
+	 * Sets the iteration.
+	 * 
+	 * @param iteration
+	 *            the new value for iteration
+	 */
+	public void setIteration(int iteration) {
+		this.iteration = iteration;
+	}
+
+	/**
+	 * Returns the errors.
+	 * 
+	 * @return the value of errors
+	 */
+	public int getErrors() {
+		return errors;
+	}
+
+	/**
+	 * Sets the errors.
+	 * 
+	 * @param errors
+	 *            the new value for errors
+	 */
+	public void setErrors(int errors) {
+		this.errors = errors;
+	}
+
+	/**
+	 * Returns the completed.
+	 * 
+	 * @return the value of completed
+	 */
+	public float getCompleted() {
+		return completed;
+	}
+
+	/**
+	 * Sets the completed value.
+	 * 
+	 * @param completed
+	 */
+	public void setCompleted(float completed) {
+		this.completed = completed;
+	}
+
+	/**
+	 * Returns <code>true</code> if the element is active. The default value is
+	 * <code>false</code>.
+	 * 
+	 * @return <code>true</code> if the element is active
+	 */
+	public boolean isActive() {
+		return active;
+	}
+
+	/**
+	 * Sets the value of active.
+	 * 
+	 * @param active
+	 *            the new active
+	 */
+	public void setActive(boolean active) {
+		this.active = active;
+	}
+
+	/**
+	 * Returns <code>true</code> if the element is interactive. The default
+	 * value is <code>false</code>.
+	 * 
+	 * @return <code>true</code> if the element is interactive
+	 */
+	public boolean isInteractive() {
+		return interactive;
+	}
+
+	/**
+	 * Sets the value of interactive.
+	 * 
+	 * @param interactive
+	 *            the new interactive
+	 */
+	public void setInteractive(boolean interactive) {
+		this.interactive = interactive;
+	}
+
+	/**
+	 * Returns <code>true</code> if the element is visible. The default value is
+	 * <code>true</code>.
+	 * 
+	 * @return <code>true</code> if the element is visible
+	 */
+	public boolean isVisible() {
+		return visible;
+	}
+
+	/**
+	 * Sets whether the element is visible.
+	 * 
+	 * @param visible
+	 *            the new value for visible
+	 */
+	public void setVisible(boolean visible) {
+		this.visible = visible;
+	}
+
+	/**
+	 * Returns the opacity value. The default value is 1.0
+	 * 
+	 * @return the opacity value
+	 */
+	public float getOpacity() {
+		return opacity;
+	}
+
+	/**
+	 * Sets the opacity of the element. Must be a value between 0.0 and 1.0.
+	 * 
+	 * @param opacity
+	 *            the new opacity value
+	 */
+	public void setOpacity(float opacity) {
+		this.opacity = opacity;
+	}
+
+	/**
+	 * Returns <code>true</code> if the element is filtered.
+	 * 
+	 * @return <code>true</code> if the element is filtered
+	 */
+	public boolean isFiltered() {
+		return filtered;
+	}
+
+	/**
+	 * Sets the value of filtered.
+	 * 
+	 * @param filtered
+	 *            the new value for filtered
+	 */
+	public void setFiltered(boolean filtered) {
+		this.filtered = filtered;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((id == null) ? 0 : id.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+
+		// Equality by id
+		GraphElement other = (GraphElement) obj;
+		if (id == null)
+			return (other.id == null);
+		return id.equals(other.id);
+	}
+
+}

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/GraphEventManager.java
----------------------------------------------------------------------
diff --git a/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEventManager.java b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEventManager.java
new file mode 100644
index 0000000..a6b9b0e
--- /dev/null
+++ b/taverna-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEventManager.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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;
+
+public interface GraphEventManager {
+	void mouseClicked(GraphElement graphElement, short button, boolean altKey,
+			boolean ctrlKey, boolean metaKey, int x, int y, int screenX,
+			int screenY);
+
+	void mouseDown(GraphElement graphElement, short button, boolean altKey,
+			boolean ctrlKey, boolean metaKey, int x, int y, int screenX,
+			int screenY);
+
+	void mouseUp(GraphElement graphElement, short button, boolean altKey,
+			boolean ctrlKey, boolean metaKey, final int x, final int y,
+			int screenX, int screenY);
+
+	void mouseMoved(GraphElement graphElement, short button, boolean altKey,
+			boolean ctrlKey, boolean metaKey, int x, int y, int screenX,
+			int screenY);
+
+	void mouseOver(GraphElement graphElement, short button, boolean altKey,
+			boolean ctrlKey, boolean metaKey, int x, int y, int screenX,
+			int screenY);
+
+	void mouseOut(GraphElement graphElement, short button, boolean altKey,
+			boolean ctrlKey, boolean metaKey, int x, int y, int screenX,
+			int screenY);
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-file-impl/pom.xml b/taverna-file-impl/pom.xml
new file mode 100644
index 0000000..bf0e019
--- /dev/null
+++ b/taverna-file-impl/pom.xml
@@ -0,0 +1,120 @@
+<?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>taverna-file-impl</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna File opening implementation</name>
+	<description>
+		Implementation for doing file (i.e. workflow) open/save in the
+		workbench.
+	</description>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-file-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-edits-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-helper-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-menu-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.engine</groupId>
+			<artifactId>taverna-observer</artifactId>
+			<version>${taverna.engine.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.osgi</groupId>
+			<artifactId>taverna-app-configuration-api</artifactId>
+			<version>${taverna.osgi.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+			<version>${taverna.language.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>${log4j.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-collections</groupId>
+			<artifactId>commons-collections</artifactId>
+			<version>${commons.collections.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>com.springsource.org.apache.commons.lang</artifactId>
+			<version>${commons.lang.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-codec</groupId>
+			<artifactId>commons-codec</artifactId>
+			<version>${commons.codec.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.jdom</groupId>
+			<artifactId>com.springsource.org.jdom</artifactId>
+			<version>${jdom.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-edits-impl</artifactId>
+			<version>${project.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-wfbundle</artifactId>
+			<version>${taverna.language.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-t2flow</artifactId>
+			<version>${taverna.language.version}</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowFromDataflowPersistenceHandler.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowFromDataflowPersistenceHandler.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowFromDataflowPersistenceHandler.java
new file mode 100644
index 0000000..86bc091
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowFromDataflowPersistenceHandler.java
@@ -0,0 +1,49 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.file.impl;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.file.AbstractDataflowPersistenceHandler;
+import net.sf.taverna.t2.workbench.file.DataflowInfo;
+import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * @author alanrw
+ */
+public class DataflowFromDataflowPersistenceHandler extends
+		AbstractDataflowPersistenceHandler implements
+		DataflowPersistenceHandler {
+	private static final WorkflowBundleFileType WORKFLOW_BUNDLE_FILE_TYPE = new WorkflowBundleFileType();
+
+	@Override
+	public DataflowInfo openDataflow(FileType fileType, Object source)
+			throws OpenException {
+		if (!getOpenFileTypes().contains(fileType))
+			throw new IllegalArgumentException("Unsupported file type "
+					+ fileType);
+
+		WorkflowBundle workflowBundle = (WorkflowBundle) source;
+		Date lastModified = null;
+		Object canonicalSource = null;
+		return new DataflowInfo(WORKFLOW_BUNDLE_FILE_TYPE, canonicalSource,
+				workflowBundle, lastModified);
+	}
+
+	@Override
+	public List<FileType> getOpenFileTypes() {
+		return Arrays.<FileType> asList(WORKFLOW_BUNDLE_FILE_TYPE);
+	}
+
+	@Override
+	public List<Class<?>> getOpenSourceTypes() {
+		return Arrays.<Class<?>> asList(Workflow.class);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowPersistenceHandlerRegistry.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowPersistenceHandlerRegistry.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowPersistenceHandlerRegistry.java
new file mode 100644
index 0000000..39117e9
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowPersistenceHandlerRegistry.java
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * 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.file.impl;
+
+import static org.apache.commons.collections.map.LazyMap.decorate;
+import static org.apache.commons.lang.ClassUtils.getAllInterfaces;
+import static org.apache.commons.lang.ClassUtils.getAllSuperclasses;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler;
+import net.sf.taverna.t2.workbench.file.FileType;
+
+import org.apache.commons.collections.Factory;
+
+// TODO: Cache lookups / build one massive structure
+public class DataflowPersistenceHandlerRegistry {
+	private static final MapFactory MAP_FACTORY = new MapFactory();
+	private static final SetFactory SET_FACTORY = new SetFactory();
+
+	@SuppressWarnings("unchecked")
+	protected static List<Class<?>> findAllParentClasses(
+			final Class<?> sourceClass) {
+		List<Class<?>> superClasses = new ArrayList<>();
+		superClasses.add(sourceClass);
+		superClasses.addAll(getAllSuperclasses(sourceClass));
+		superClasses.addAll(getAllInterfaces(sourceClass));
+		return superClasses;
+	}
+
+	private Map<Class<?>, Set<DataflowPersistenceHandler>> openClassToHandlers;
+	private Map<Class<?>, Set<FileType>> openClassToTypes;
+	private Map<FileType, Map<Class<?>, Set<DataflowPersistenceHandler>>> openFileClassToHandler;
+	private Map<FileType, Set<DataflowPersistenceHandler>> openFileToHandler;
+	private Map<Class<?>, Set<DataflowPersistenceHandler>> saveClassToHandlers;
+	private Map<Class<?>, Set<FileType>> saveClassToTypes;
+	private Map<FileType, Map<Class<?>, Set<DataflowPersistenceHandler>>> saveFileClassToHandler;
+	private Map<FileType, Set<DataflowPersistenceHandler>> saveFileToHandler;
+
+	private List<DataflowPersistenceHandler> dataflowPersistenceHandlers;
+
+	public DataflowPersistenceHandlerRegistry() {
+	}
+
+	public Set<FileType> getOpenFileTypes() {
+		return getOpenFileClassToHandler().keySet();
+	}
+
+	public Set<FileType> getOpenFileTypesFor(Class<?> sourceClass) {
+		Set<FileType> fileTypes = new LinkedHashSet<>();
+		for (Class<?> candidateClass : findAllParentClasses(sourceClass))
+			fileTypes.addAll(getOpenClassToTypes().get(candidateClass));
+		return fileTypes;
+	}
+
+	public Set<DataflowPersistenceHandler> getOpenHandlersFor(
+			Class<? extends Object> sourceClass) {
+		Set<DataflowPersistenceHandler> handlers = new LinkedHashSet<>();
+		for (Class<?> candidateClass : findAllParentClasses(sourceClass))
+			handlers.addAll(getOpenClassToHandlers().get(candidateClass));
+		return handlers;
+	}
+
+	public Set<DataflowPersistenceHandler> getOpenHandlersFor(
+			FileType fileType, Class<? extends Object> sourceClass) {
+		Set<DataflowPersistenceHandler> handlers = new LinkedHashSet<>();
+		for (Class<?> candidateClass : findAllParentClasses(sourceClass))
+			handlers.addAll(getOpenFileClassToHandler().get(fileType).get(
+					candidateClass));
+		return handlers;
+	}
+
+	public Set<DataflowPersistenceHandler> getOpenHandlersForType(
+			FileType fileType) {
+		return getOpenFileToHandler().get(fileType);
+	}
+
+	public synchronized Set<DataflowPersistenceHandler> getOpenHandlersForType(
+			FileType fileType, Class<?> sourceClass) {
+		Set<DataflowPersistenceHandler> handlers = new LinkedHashSet<>();
+		for (Class<?> candidateClass : findAllParentClasses(sourceClass))
+			handlers.addAll(getOpenFileClassToHandler().get(fileType).get(
+					candidateClass));
+		return handlers;
+	}
+
+	public Set<FileType> getSaveFileTypes() {
+		return getSaveFileClassToHandler().keySet();
+	}
+
+	public Set<FileType> getSaveFileTypesFor(Class<?> destinationClass) {
+		Set<FileType> fileTypes = new LinkedHashSet<>();
+		for (Class<?> candidateClass : findAllParentClasses(destinationClass))
+			fileTypes.addAll(getSaveClassToTypes().get(candidateClass));
+		return fileTypes;
+	}
+
+	public Set<DataflowPersistenceHandler> getSaveHandlersFor(
+			Class<? extends Object> destinationClass) {
+		Set<DataflowPersistenceHandler> handlers = new LinkedHashSet<>();
+		for (Class<?> candidateClass : findAllParentClasses(destinationClass))
+			handlers.addAll(getSaveClassToHandlers().get(candidateClass));
+		return handlers;
+	}
+
+	public Set<DataflowPersistenceHandler> getSaveHandlersForType(
+			FileType fileType, Class<?> destinationClass) {
+		Set<DataflowPersistenceHandler> handlers = new LinkedHashSet<>();
+		for (Class<?> candidateClass : findAllParentClasses(destinationClass))
+			handlers.addAll(getSaveFileClassToHandler().get(fileType).get(
+					candidateClass));
+		return handlers;
+	}
+
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	private synchronized void createCollections() {
+		openFileClassToHandler = decorate(new HashMap(), MAP_FACTORY);
+		openFileToHandler = decorate(new HashMap(), SET_FACTORY);
+		openClassToTypes = decorate(new HashMap(), SET_FACTORY);
+		openClassToHandlers = decorate(new HashMap(), SET_FACTORY);
+
+		saveFileClassToHandler = decorate(new HashMap(), MAP_FACTORY);
+		saveFileToHandler = decorate(new HashMap(), SET_FACTORY);
+		saveClassToTypes = decorate(new HashMap(), SET_FACTORY);
+		saveClassToHandlers = decorate(new HashMap(), SET_FACTORY);
+	}
+
+	private Map<Class<?>, Set<DataflowPersistenceHandler>> getOpenClassToHandlers() {
+		return openClassToHandlers;
+	}
+
+	private synchronized Map<Class<?>, Set<FileType>> getOpenClassToTypes() {
+		return openClassToTypes;
+	}
+
+	private synchronized Map<FileType, Map<Class<?>, Set<DataflowPersistenceHandler>>> getOpenFileClassToHandler() {
+		return openFileClassToHandler;
+	}
+
+	private Map<FileType, Set<DataflowPersistenceHandler>> getOpenFileToHandler() {
+		return openFileToHandler;
+	}
+
+	private Map<Class<?>, Set<DataflowPersistenceHandler>> getSaveClassToHandlers() {
+		return saveClassToHandlers;
+	}
+
+	private synchronized Map<Class<?>, Set<FileType>> getSaveClassToTypes() {
+		return saveClassToTypes;
+	}
+
+	private synchronized Map<FileType, Map<Class<?>, Set<DataflowPersistenceHandler>>> getSaveFileClassToHandler() {
+		return saveFileClassToHandler;
+	}
+
+	/**
+	 * Bind method for SpringDM.
+	 * 
+	 * @param service
+	 * @param properties
+	 */
+	public void update(Object service, Map<?, ?> properties) {
+		if (dataflowPersistenceHandlers != null)
+			updateColletions();
+	}
+
+	public synchronized void updateColletions() {
+		createCollections();
+		for (DataflowPersistenceHandler handler : dataflowPersistenceHandlers) {
+			for (FileType openFileType : handler.getOpenFileTypes()) {
+				Set<DataflowPersistenceHandler> set = openFileToHandler
+						.get(openFileType);
+				set.add(handler);
+				for (Class<?> openClass : handler.getOpenSourceTypes()) {
+					openFileClassToHandler.get(openFileType).get(openClass)
+							.add(handler);
+					openClassToTypes.get(openClass).add(openFileType);
+				}
+			}
+			for (Class<?> openClass : handler.getOpenSourceTypes())
+				openClassToHandlers.get(openClass).add(handler);
+
+			for (FileType saveFileType : handler.getSaveFileTypes()) {
+				saveFileToHandler.get(saveFileType).add(handler);
+				for (Class<?> saveClass : handler.getSaveDestinationTypes()) {
+					saveFileClassToHandler.get(saveFileType).get(saveClass)
+							.add(handler);
+					saveClassToTypes.get(saveClass).add(saveFileType);
+				}
+			}
+			for (Class<?> openClass : handler.getSaveDestinationTypes())
+				saveClassToHandlers.get(openClass).add(handler);
+		}
+	}
+
+	public void setDataflowPersistenceHandlers(
+			List<DataflowPersistenceHandler> dataflowPersistenceHandlers) {
+		this.dataflowPersistenceHandlers = dataflowPersistenceHandlers;
+	}
+
+	private static class MapFactory implements Factory {
+		@Override
+		@SuppressWarnings("rawtypes")
+		public Object create() {
+			return decorate(new HashMap(), SET_FACTORY);
+		}
+	}
+
+	private static class SetFactory implements Factory {
+		@Override
+		public Object create() {
+			return new LinkedHashSet<Object>();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileDataflowInfo.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileDataflowInfo.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileDataflowInfo.java
new file mode 100644
index 0000000..89ae39c
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileDataflowInfo.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * 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.file.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+
+import net.sf.taverna.t2.workbench.file.DataflowInfo;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.FileType;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * Information about an open dataflow that was opened from or saved to a
+ * {@link File}.
+ * 
+ * @see DataflowInfo
+ * @see FileManager
+ * @author Stian Soiland-Reyes
+ */
+public class FileDataflowInfo extends DataflowInfo {
+	private static Logger logger = Logger.getLogger(FileDataflowInfo.class);
+
+	public FileDataflowInfo(FileType fileType, File source,
+			WorkflowBundle workflowBundle) {
+		super(fileType, canonicalFile(source), workflowBundle,
+				lastModifiedFile(source));
+	}
+
+	protected static Date lastModifiedFile(File file) {
+		long lastModifiedLong = file.lastModified();
+		if (lastModifiedLong == 0)
+			return null;
+		return new Date(lastModifiedLong);
+	}
+
+	public static File canonicalFile(File file) {
+		try {
+			return file.getCanonicalFile();
+		} catch (IOException e) {
+			logger.warn("Could not find canonical file for " + file);
+			return file;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileManagerImpl.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileManagerImpl.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileManagerImpl.java
new file mode 100644
index 0000000..aadb3f1
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileManagerImpl.java
@@ -0,0 +1,601 @@
+/*******************************************************************************
+ * Copyright (C) 2007-2010 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.file.impl;
+
+import static java.awt.GraphicsEnvironment.isHeadless;
+import static java.util.Collections.singleton;
+import static javax.swing.SwingUtilities.invokeAndWait;
+import static javax.swing.SwingUtilities.isEventDispatchThread;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.swing.filechooser.FileFilter;
+
+import net.sf.taverna.t2.lang.observer.MultiCaster;
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+import net.sf.taverna.t2.workbench.file.DataflowInfo;
+import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.ClosingDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.file.events.OpenedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.SavedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.SetCurrentDataflowEvent;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+import net.sf.taverna.t2.workbench.file.exceptions.OverwriteException;
+import net.sf.taverna.t2.workbench.file.exceptions.SaveException;
+import net.sf.taverna.t2.workbench.file.exceptions.UnsavedException;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+/**
+ * Implementation of {@link FileManager}
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class FileManagerImpl implements FileManager {
+	private static Logger logger = Logger.getLogger(FileManagerImpl.class);
+	private static int nameIndex = 1;
+
+	/**
+	 * The last blank workflowBundle created using #newDataflow() until it has
+	 * been changed - when this variable will be set to null again. Used to
+	 * automatically close unmodified blank workflowBundles on open.
+	 */
+	private WorkflowBundle blankWorkflowBundle = null;
+	@SuppressWarnings("unused")
+	private EditManager editManager;
+	private EditManagerObserver editManagerObserver = new EditManagerObserver();
+	protected MultiCaster<FileManagerEvent> observers = new MultiCaster<>(this);
+	/**
+	 * Ordered list of open WorkflowBundle
+	 */
+	private LinkedHashMap<WorkflowBundle, OpenDataflowInfo> openDataflowInfos = new LinkedHashMap<>();
+	private DataflowPersistenceHandlerRegistry dataflowPersistenceHandlerRegistry;
+	private Scufl2Tools scufl2Tools = new Scufl2Tools();
+	private WorkflowBundle currentWorkflowBundle;
+
+	public DataflowPersistenceHandlerRegistry getPersistanceHandlerRegistry() {
+		return dataflowPersistenceHandlerRegistry;
+	}
+
+	public FileManagerImpl(EditManager editManager) {
+		this.editManager = editManager;
+		editManager.addObserver(editManagerObserver);
+	}
+
+	/**
+	 * Add an observer to be notified of {@link FileManagerEvent}s, such as
+	 * {@link OpenedDataflowEvent} and {@link SavedDataflowEvent}.
+	 * 
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addObserver(Observer<FileManagerEvent> observer) {
+		observers.addObserver(observer);
+	}
+
+	@Override
+	public boolean canSaveWithoutDestination(WorkflowBundle workflowBundle) {
+		OpenDataflowInfo dataflowInfo = getOpenDataflowInfo(workflowBundle);
+		if (dataflowInfo.getSource() == null)
+			return false;
+		Set<?> handlers = getPersistanceHandlerRegistry()
+				.getSaveHandlersForType(
+						dataflowInfo.getFileType(),
+						dataflowInfo.getDataflowInfo().getCanonicalSource()
+								.getClass());
+		return !handlers.isEmpty();
+	}
+
+	@Override
+	public boolean closeDataflow(WorkflowBundle workflowBundle,
+			boolean failOnUnsaved) throws UnsavedException {
+		if (workflowBundle == null)
+			throw new NullPointerException("Dataflow can't be null");
+		ClosingDataflowEvent message = new ClosingDataflowEvent(workflowBundle);
+		observers.notify(message);
+		if (message.isAbortClose())
+			return false;
+		if ((failOnUnsaved && getOpenDataflowInfo(workflowBundle).isChanged()))
+			throw new UnsavedException(workflowBundle);
+		if (workflowBundle.equals(getCurrentDataflow())) {
+			// We'll need to change current workflowBundle
+			// Find best candidate to the left or right
+			List<WorkflowBundle> workflowBundles = getOpenDataflows();
+			int openIndex = workflowBundles.indexOf(workflowBundle);
+			if (openIndex == -1)
+				throw new IllegalArgumentException("Workflow was not opened "
+						+ workflowBundle);
+
+			if (openIndex > 0)
+				setCurrentDataflow(workflowBundles.get(openIndex - 1));
+			else if (openIndex == 0 && workflowBundles.size() > 1)
+				setCurrentDataflow(workflowBundles.get(1));
+			else
+				// If it was the last one, start a new, empty workflowBundle
+				newDataflow();
+		}
+		if (workflowBundle == blankWorkflowBundle)
+			blankWorkflowBundle = null;
+		openDataflowInfos.remove(workflowBundle);
+		observers.notify(new ClosedDataflowEvent(workflowBundle));
+		return true;
+	}
+
+	@Override
+	public WorkflowBundle getCurrentDataflow() {
+		return currentWorkflowBundle;
+	}
+
+	@Override
+	public WorkflowBundle getDataflowBySource(Object source) {
+		for (Entry<WorkflowBundle, OpenDataflowInfo> infoEntry : openDataflowInfos
+				.entrySet()) {
+			OpenDataflowInfo info = infoEntry.getValue();
+			if (source.equals(info.getSource()))
+				return infoEntry.getKey();
+		}
+		// Not found
+		return null;
+	}
+
+	@Override
+	public String getDataflowName(WorkflowBundle workflowBundle) {
+		Object source = null;
+		if (isDataflowOpen(workflowBundle))
+			source = getDataflowSource(workflowBundle);
+		// Fallback
+		String name;
+		Workflow workflow = workflowBundle.getMainWorkflow();
+		if (workflow != null)
+			name = workflow.getName();
+		else
+			name = workflowBundle.getName();
+		if (source == null)
+			return name;
+		if (source instanceof File)
+			return ((File) source).getAbsolutePath();
+		else if (source instanceof URL)
+			return source.toString();
+
+		// Check if it has implemented a toString() method
+		Method toStringMethod = null;
+		Method toStringMethodFromObject = null;
+		try {
+			toStringMethod = source.getClass().getMethod("toString");
+			toStringMethodFromObject = Object.class.getMethod("toString");
+		} catch (Exception e) {
+			throw new IllegalStateException(
+					"Source did not implement Object.toString() " + source);
+		}
+		if (!toStringMethod.equals(toStringMethodFromObject))
+			return source.toString();
+		return name;
+	}
+
+	@Override
+	public String getDefaultWorkflowName() {
+		return "Workflow" + (nameIndex++);
+	}
+
+	@Override
+	public Object getDataflowSource(WorkflowBundle workflowBundle) {
+		return getOpenDataflowInfo(workflowBundle).getSource();
+	}
+
+	@Override
+	public FileType getDataflowType(WorkflowBundle workflowBundle) {
+		return getOpenDataflowInfo(workflowBundle).getFileType();
+	}
+
+	@Override
+	public List<Observer<FileManagerEvent>> getObservers() {
+		return observers.getObservers();
+	}
+
+	/**
+	 * Get the {@link OpenDataflowInfo} for the given WorkflowBundle
+	 * 
+	 * @throws NullPointerException
+	 *             if the WorkflowBundle was <code>null</code>
+	 * @throws IllegalArgumentException
+	 *             if the WorkflowBundle was not open.
+	 * @param workflowBundle
+	 *            WorkflowBundle which information is to be found
+	 * @return The {@link OpenDataflowInfo} describing the WorkflowBundle
+	 */
+	protected synchronized OpenDataflowInfo getOpenDataflowInfo(
+			WorkflowBundle workflowBundle) {
+		if (workflowBundle == null)
+			throw new NullPointerException("Dataflow can't be null");
+		OpenDataflowInfo info = openDataflowInfos.get(workflowBundle);
+		if (info == null)
+			throw new IllegalArgumentException("Workflow was not opened "
+					+ workflowBundle);
+		return info;
+	}
+
+	@Override
+	public List<WorkflowBundle> getOpenDataflows() {
+		return new ArrayList<>(openDataflowInfos.keySet());
+	}
+
+	@Override
+	public List<FileFilter> getOpenFileFilters() {
+		List<FileFilter> fileFilters = new ArrayList<>();
+
+		Set<FileType> fileTypes = getPersistanceHandlerRegistry()
+				.getOpenFileTypes();
+		if (!fileTypes.isEmpty())
+			fileFilters.add(new MultipleFileTypes(fileTypes,
+					"All supported workflows"));
+		for (FileType fileType : fileTypes)
+			fileFilters.add(new FileTypeFileFilter(fileType));
+		return fileFilters;
+	}
+
+	@Override
+	public List<FileFilter> getOpenFileFilters(Class<?> sourceClass) {
+		List<FileFilter> fileFilters = new ArrayList<>();
+		for (FileType fileType : getPersistanceHandlerRegistry()
+				.getOpenFileTypesFor(sourceClass))
+			fileFilters.add(new FileTypeFileFilter(fileType));
+		return fileFilters;
+	}
+
+	@Override
+	public List<FileFilter> getSaveFileFilters() {
+		List<FileFilter> fileFilters = new ArrayList<>();
+		for (FileType fileType : getPersistanceHandlerRegistry()
+				.getSaveFileTypes())
+			fileFilters.add(new FileTypeFileFilter(fileType));
+		return fileFilters;
+	}
+
+	@Override
+	public List<FileFilter> getSaveFileFilters(Class<?> destinationClass) {
+		List<FileFilter> fileFilters = new ArrayList<>();
+		for (FileType fileType : getPersistanceHandlerRegistry()
+				.getSaveFileTypesFor(destinationClass))
+			fileFilters.add(new FileTypeFileFilter(fileType));
+		return fileFilters;
+	}
+
+	@Override
+	public boolean isDataflowChanged(WorkflowBundle workflowBundle) {
+		return getOpenDataflowInfo(workflowBundle).isChanged();
+	}
+
+	@Override
+	public boolean isDataflowOpen(WorkflowBundle workflowBundle) {
+		return openDataflowInfos.containsKey(workflowBundle);
+	}
+
+	@Override
+	public WorkflowBundle newDataflow() {
+		WorkflowBundle workflowBundle = new WorkflowBundle();
+		workflowBundle.setMainWorkflow(new Workflow());
+		workflowBundle.getMainWorkflow().setName(getDefaultWorkflowName());
+		workflowBundle.setMainProfile(new Profile());
+		scufl2Tools.setParents(workflowBundle);
+		blankWorkflowBundle = null;
+		openDataflowInternal(workflowBundle);
+		blankWorkflowBundle = workflowBundle;
+		observers.notify(new OpenedDataflowEvent(workflowBundle));
+		return workflowBundle;
+	}
+
+	@Override
+	public void openDataflow(WorkflowBundle workflowBundle) {
+		openDataflowInternal(workflowBundle);
+		observers.notify(new OpenedDataflowEvent(workflowBundle));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public WorkflowBundle openDataflow(FileType fileType, Object source)
+			throws OpenException {
+		if (isHeadless())
+			return performOpenDataflow(fileType, source);
+
+		OpenDataflowRunnable r = new OpenDataflowRunnable(this, fileType,
+				source);
+		if (isEventDispatchThread()) {
+			r.run();
+		} else
+			try {
+				invokeAndWait(r);
+			} catch (InterruptedException | InvocationTargetException e) {
+				throw new OpenException("Opening was interrupted", e);
+			}
+		OpenException thrownException = r.getException();
+		if (thrownException != null)
+			throw thrownException;
+		return r.getDataflow();
+	}
+
+	public WorkflowBundle performOpenDataflow(FileType fileType, Object source)
+			throws OpenException {
+		DataflowInfo dataflowInfo;
+		WorkflowBundle workflowBundle;
+		dataflowInfo = openDataflowSilently(fileType, source);
+		workflowBundle = dataflowInfo.getDataflow();
+		openDataflowInternal(workflowBundle);
+		getOpenDataflowInfo(workflowBundle).setOpenedFrom(dataflowInfo);
+		observers.notify(new OpenedDataflowEvent(workflowBundle));
+		return workflowBundle;
+	}
+
+	@Override
+	public DataflowInfo openDataflowSilently(FileType fileType, Object source)
+			throws OpenException {
+		Set<DataflowPersistenceHandler> handlers;
+		Class<? extends Object> sourceClass = source.getClass();
+
+		boolean unknownFileType = (fileType == null);
+		if (unknownFileType)
+			handlers = getPersistanceHandlerRegistry().getOpenHandlersFor(
+					sourceClass);
+		else
+			handlers = getPersistanceHandlerRegistry().getOpenHandlersFor(
+					fileType, sourceClass);
+		if (handlers.isEmpty())
+			throw new OpenException("Unsupported file type or class "
+					+ fileType + " " + sourceClass);
+
+		Throwable lastException = null;
+		for (DataflowPersistenceHandler handler : handlers) {
+			Collection<FileType> fileTypes;
+			if (unknownFileType)
+				fileTypes = handler.getOpenFileTypes();
+			else
+				fileTypes = singleton(fileType);
+			for (FileType candidateFileType : fileTypes) {
+				if (unknownFileType && (source instanceof File))
+					/*
+					 * If source is file but fileType was not explicitly set
+					 * from the open workflow dialog - check the file extension
+					 * and decide which handler to use based on that (so that we
+					 * do not loop though all handlers)
+					 */
+					if (!((File) source).getPath().endsWith(
+							candidateFileType.getExtension()))
+						continue;
+
+				try {
+					DataflowInfo openDataflow = handler.openDataflow(
+							candidateFileType, source);
+					WorkflowBundle workflowBundle = openDataflow.getDataflow();
+					logger.info("Loaded workflow: " + workflowBundle.getName()
+							+ " " + workflowBundle.getGlobalBaseURI()
+							+ " from " + source + " using " + handler);
+					return openDataflow;
+				} catch (OpenException ex) {
+					logger.warn("Could not open workflow " + source + " using "
+							+ handler + " of type " + candidateFileType);
+					lastException = ex;
+				}
+			}
+		}
+		throw new OpenException("Could not open workflow " + source + "\n",
+				lastException);
+	}
+
+	/**
+	 * Mark the WorkflowBundle as opened, and close the blank WorkflowBundle if
+	 * needed.
+	 * 
+	 * @param workflowBundle
+	 *            WorkflowBundle that has been opened
+	 */
+	protected void openDataflowInternal(WorkflowBundle workflowBundle) {
+		if (workflowBundle == null)
+			throw new NullPointerException("Dataflow can't be null");
+		if (isDataflowOpen(workflowBundle))
+			throw new IllegalArgumentException("Workflow is already open: "
+					+ workflowBundle);
+
+		openDataflowInfos.put(workflowBundle, new OpenDataflowInfo());
+		setCurrentDataflow(workflowBundle);
+		if (openDataflowInfos.size() == 2 && blankWorkflowBundle != null)
+			/*
+			 * Behave like a word processor and close the blank WorkflowBundle
+			 * when another workflow has been opened
+			 */
+			try {
+				closeDataflow(blankWorkflowBundle, true);
+			} catch (UnsavedException e) {
+				logger.error("Blank workflow was modified "
+						+ "and could not be closed");
+			}
+	}
+
+	@Override
+	public void removeObserver(Observer<FileManagerEvent> observer) {
+		observers.removeObserver(observer);
+	}
+
+	@Override
+	public void saveDataflow(WorkflowBundle workflowBundle,
+			boolean failOnOverwrite) throws SaveException {
+		if (workflowBundle == null)
+			throw new NullPointerException("Dataflow can't be null");
+		OpenDataflowInfo lastSave = getOpenDataflowInfo(workflowBundle);
+		if (lastSave.getSource() == null)
+			throw new SaveException("Can't save without source "
+					+ workflowBundle);
+		saveDataflow(workflowBundle, lastSave.getFileType(),
+				lastSave.getSource(), failOnOverwrite);
+	}
+
+	@Override
+	public void saveDataflow(WorkflowBundle workflowBundle, FileType fileType,
+			Object destination, boolean failOnOverwrite) throws SaveException {
+		DataflowInfo savedDataflow = saveDataflowSilently(workflowBundle,
+				fileType, destination, failOnOverwrite);
+		getOpenDataflowInfo(workflowBundle).setSavedTo(savedDataflow);
+		observers.notify(new SavedDataflowEvent(workflowBundle));
+	}
+
+	@Override
+	public DataflowInfo saveDataflowSilently(WorkflowBundle workflowBundle,
+			FileType fileType, Object destination, boolean failOnOverwrite)
+			throws SaveException, OverwriteException {
+		Set<DataflowPersistenceHandler> handlers;
+
+		Class<? extends Object> destinationClass = destination.getClass();
+		if (fileType != null)
+			handlers = getPersistanceHandlerRegistry().getSaveHandlersForType(
+					fileType, destinationClass);
+		else
+			handlers = getPersistanceHandlerRegistry().getSaveHandlersFor(
+					destinationClass);
+
+		SaveException lastException = null;
+		for (DataflowPersistenceHandler handler : handlers) {
+			if (failOnOverwrite) {
+				OpenDataflowInfo openDataflowInfo = getOpenDataflowInfo(workflowBundle);
+				if (handler.wouldOverwriteDataflow(workflowBundle, fileType,
+						destination, openDataflowInfo.getDataflowInfo()))
+					throw new OverwriteException(destination);
+			}
+			try {
+				DataflowInfo savedDataflow = handler.saveDataflow(
+						workflowBundle, fileType, destination);
+				savedDataflow.getDataflow();
+				logger.info("Saved workflow: " + workflowBundle.getName() + " "
+						+ workflowBundle.getGlobalBaseURI() + " to "
+						+ savedDataflow.getCanonicalSource() + " using "
+						+ handler);
+				return savedDataflow;
+			} catch (SaveException ex) {
+				logger.warn("Could not save to " + destination + " using "
+						+ handler);
+				lastException = ex;
+			}
+		}
+
+		if (lastException == null)
+			throw new SaveException("Unsupported file type or class "
+					+ fileType + " " + destinationClass);
+		throw new SaveException("Could not save to " + destination + ":\n"
+				+ lastException.getLocalizedMessage(), lastException);
+	}
+
+	@Override
+	public void setCurrentDataflow(WorkflowBundle workflowBundle) {
+		setCurrentDataflow(workflowBundle, false);
+	}
+
+	@Override
+	public void setCurrentDataflow(WorkflowBundle workflowBundle,
+			boolean openIfNeeded) {
+		currentWorkflowBundle = workflowBundle;
+		if (!isDataflowOpen(workflowBundle)) {
+			if (!openIfNeeded)
+				throw new IllegalArgumentException("Workflow is not open: "
+						+ workflowBundle);
+			openDataflow(workflowBundle);
+			return;
+		}
+		observers.notify(new SetCurrentDataflowEvent(workflowBundle));
+	}
+
+	@Override
+	public void setDataflowChanged(WorkflowBundle workflowBundle,
+			boolean isChanged) {
+		getOpenDataflowInfo(workflowBundle).setIsChanged(isChanged);
+		if (blankWorkflowBundle == workflowBundle)
+			blankWorkflowBundle = null;
+	}
+
+	@Override
+	public Object getCanonical(Object source) throws IllegalArgumentException,
+			URISyntaxException, IOException {
+		Object canonicalSource = source;
+
+		if (source instanceof URL) {
+			URL url = ((URL) source);
+			if (url.getProtocol().equalsIgnoreCase("file"))
+				canonicalSource = new File(url.toURI());
+		}
+
+		if (canonicalSource instanceof File)
+			canonicalSource = ((File) canonicalSource).getCanonicalFile();
+		return canonicalSource;
+	}
+
+	public void setDataflowPersistenceHandlerRegistry(
+			DataflowPersistenceHandlerRegistry dataflowPersistenceHandlerRegistry) {
+		this.dataflowPersistenceHandlerRegistry = dataflowPersistenceHandlerRegistry;
+	}
+
+	/**
+	 * Observe the {@link EditManager} for changes to open workflowBundles. A
+	 * change of an open workflow would set it as changed using
+	 * {@link FileManagerImpl#setDataflowChanged(Dataflow, boolean)}.
+	 * 
+	 * @author Stian Soiland-Reyes
+	 * 
+	 */
+	private final class EditManagerObserver implements
+			Observer<EditManagerEvent> {
+		@Override
+		public void notify(Observable<EditManagerEvent> sender,
+				EditManagerEvent message) throws Exception {
+			if (message instanceof AbstractDataflowEditEvent) {
+				AbstractDataflowEditEvent dataflowEdit = (AbstractDataflowEditEvent) message;
+				WorkflowBundle workflowBundle = dataflowEdit.getDataFlow();
+				/**
+				 * TODO: on undo/redo - keep last event or similar to determine
+				 * if workflow was saved before. See
+				 * FileManagerTest#isChangedWithUndo().
+				 */
+				setDataflowChanged(workflowBundle, true);
+			}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileTypeFileFilter.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileTypeFileFilter.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileTypeFileFilter.java
new file mode 100644
index 0000000..6416163
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileTypeFileFilter.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * 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.file.impl;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+import net.sf.taverna.t2.workbench.file.FileType;
+
+public class FileTypeFileFilter extends FileFilter {
+	private final FileType fileType;
+
+	public FileTypeFileFilter(FileType fileType) {
+		this.fileType = fileType;
+	}
+
+	@Override
+	public String getDescription() {
+		return fileType.getDescription();
+	}
+
+	@Override
+	public boolean accept(File file) {
+		if (file.isDirectory())
+			// Don't grey out directories
+			return true;
+		if (fileType.getExtension() == null)
+			return false;
+		return file.getName().toLowerCase()
+				.endsWith("." + fileType.getExtension());
+	}
+
+	public FileType getFileType() {
+		return fileType;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/MultipleFileTypes.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/MultipleFileTypes.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/MultipleFileTypes.java
new file mode 100644
index 0000000..c398805
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/MultipleFileTypes.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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.file.impl;
+
+import java.io.File;
+import java.util.Set;
+
+import javax.swing.filechooser.FileFilter;
+
+import net.sf.taverna.t2.workbench.file.FileType;
+
+public class MultipleFileTypes extends FileFilter {
+	private String description;
+	private final Set<FileType> fileTypes;
+
+	public MultipleFileTypes(Set<FileType> fileTypes, String description) {
+		this.fileTypes = fileTypes;
+		this.description = description;
+	}
+
+	@Override
+	public String getDescription() {
+		return description;
+	}
+
+	@Override
+	public boolean accept(File file) {
+		if (file.isDirectory())
+			return true;
+
+		String lowerFileName = file.getName().toLowerCase();
+		for (FileType fileType : fileTypes) {
+			if (fileType.getExtension() == null)
+				continue;
+			if (lowerFileName.endsWith(fileType.getExtension()))
+				return true;
+		}
+		return false;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInProgressDialog.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInProgressDialog.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInProgressDialog.java
new file mode 100644
index 0000000..dc08cff
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInProgressDialog.java
@@ -0,0 +1,88 @@
+
+/*******************************************************************************
+ * Copyright (C) 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.file.impl;
+
+import static java.awt.BorderLayout.CENTER;
+import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow;
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.workingIcon;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+
+/**
+ * Dialog that is popped up while we are opening a workflow.
+ * 
+ * @author Alex Nenadic
+ * @author Alan R Williams
+ */
+@SuppressWarnings("serial")
+public class OpenDataflowInProgressDialog extends HelpEnabledDialog {
+	private boolean userCancelled = false;
+
+	public OpenDataflowInProgressDialog() {
+		super(getMainWindow(), "Opening workflow", true);
+		setResizable(false);
+		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+		
+		JPanel panel = new JPanel(new BorderLayout());
+		panel.setBorder(new EmptyBorder(10,10,10,10));
+		
+		JPanel textPanel = new JPanel();
+		JLabel text = new JLabel(workingIcon);
+		text.setText("Opening workflow...");
+		text.setBorder(new EmptyBorder(10,0,10,0));
+		textPanel.add(text);
+		panel.add(textPanel, CENTER);
+		
+/*
+ * Cancellation does not work when opening
+ 
+		// Cancel button
+		JButton cancelButton = new JButton("Cancel");
+		cancelButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				userCancelled = true;
+				setVisible(false);
+				dispose();
+			}
+		});
+		JPanel cancelButtonPanel = new JPanel();
+		cancelButtonPanel.add(cancelButton);
+		panel.add(cancelButtonPanel, BorderLayout.SOUTH);
+*/
+		setContentPane(panel);
+		setPreferredSize(new Dimension(300, 100));
+
+		pack();		
+	}
+
+	public boolean hasUserCancelled() {
+		return userCancelled;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInfo.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInfo.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInfo.java
new file mode 100644
index 0000000..4a4a1e3
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInfo.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * 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.file.impl;
+
+import java.util.Date;
+
+import net.sf.taverna.t2.workbench.file.DataflowInfo;
+import net.sf.taverna.t2.workbench.file.FileType;
+
+/**
+ * Information about an open dataflow.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class OpenDataflowInfo {
+	private DataflowInfo dataflowInfo;
+	private boolean isChanged;
+	private Date openedAt;
+
+	public OpenDataflowInfo() {
+	}
+
+	public FileType getFileType() {
+		if (dataflowInfo == null)
+			return null;
+		return dataflowInfo.getFileType();
+	}
+
+	public Date getLastModified() {
+		if (dataflowInfo == null)
+			return null;
+		return dataflowInfo.getLastModified();
+	}
+
+	public Date getOpenedAtDate() {
+		return openedAt;
+	}
+
+	public Object getSource() {
+		if (dataflowInfo == null)
+			return null;
+		return dataflowInfo.getCanonicalSource();
+	}
+
+	public boolean isChanged() {
+		return isChanged;
+	}
+
+	public void setIsChanged(boolean isChanged) {
+		this.isChanged = isChanged;
+	}
+
+	public synchronized void setOpenedFrom(DataflowInfo dataflowInfo) {
+		setDataflowInfo(dataflowInfo);
+		setOpenedAt(new Date());
+		setIsChanged(false);
+	}
+
+	public synchronized void setSavedTo(DataflowInfo dataflowInfo) {
+		setDataflowInfo(dataflowInfo);
+		setIsChanged(false);
+	}
+
+	private void setDataflowInfo(DataflowInfo dataflowInfo) {
+		this.dataflowInfo = dataflowInfo;
+	}
+
+	private void setOpenedAt(Date openedAt) {
+		this.openedAt = openedAt;
+	}
+
+	public DataflowInfo getDataflowInfo() {
+		return dataflowInfo;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowRunnable.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowRunnable.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowRunnable.java
new file mode 100644
index 0000000..9d687b8
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowRunnable.java
@@ -0,0 +1,71 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.file.impl;
+
+import static java.lang.Thread.sleep;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+import net.sf.taverna.t2.workbench.ui.SwingWorkerCompletionWaiter;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * @author alanrw
+ */
+public class OpenDataflowRunnable implements Runnable {
+	private final FileManagerImpl fileManager;
+	private final FileType fileType;
+	private final Object source;
+	private WorkflowBundle dataflow;
+	private OpenException e;
+
+	public OpenDataflowRunnable(FileManagerImpl fileManager, FileType fileType,
+			Object source) {
+		this.fileManager = fileManager;
+		this.fileType = fileType;
+		this.source = source;
+	}
+
+	@Override
+	public void run() {
+		OpenDataflowSwingWorker openDataflowSwingWorker = new OpenDataflowSwingWorker(
+				fileType, source, fileManager);
+		OpenDataflowInProgressDialog dialog = new OpenDataflowInProgressDialog();
+		openDataflowSwingWorker
+				.addPropertyChangeListener(new SwingWorkerCompletionWaiter(
+						dialog));
+		openDataflowSwingWorker.execute();
+
+		/*
+		 * Give a chance to the SwingWorker to finish so we do not have to
+		 * display the dialog
+		 */
+		try {
+			sleep(500);
+		} catch (InterruptedException e) {
+		    this.e = new OpenException("Opening was interrupted");
+		}
+		if (!openDataflowSwingWorker.isDone())
+			dialog.setVisible(true); // this will block the GUI
+		boolean userCancelled = dialog.hasUserCancelled(); // see if user cancelled the dialog
+
+		if (userCancelled) {
+			// Stop the OpenDataflowSwingWorker if it is still working
+			openDataflowSwingWorker.cancel(true);
+			dataflow = null;
+			this.e = new OpenException("Opening was cancelled");
+			// exit
+			return;
+		}
+		dataflow = openDataflowSwingWorker.getDataflow();
+		this.e = openDataflowSwingWorker.getException();
+	}
+
+	public WorkflowBundle getDataflow() {
+		return dataflow;
+	}
+
+	public OpenException getException() {
+		return this.e;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowSwingWorker.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowSwingWorker.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowSwingWorker.java
new file mode 100644
index 0000000..4cbd2f8
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowSwingWorker.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (C) 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.file.impl;
+
+import javax.swing.SwingWorker;
+
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+public class OpenDataflowSwingWorker extends
+		SwingWorker<WorkflowBundle, Object> {
+	@SuppressWarnings("unused")
+	private Logger logger = Logger.getLogger(OpenDataflowSwingWorker.class);
+	private FileType fileType;
+	private Object source;
+	private FileManagerImpl fileManagerImpl;
+	private WorkflowBundle workflowBundle;
+	private OpenException e = null;
+
+	public OpenDataflowSwingWorker(FileType fileType, Object source,
+			FileManagerImpl fileManagerImpl) {
+		this.fileType = fileType;
+		this.source = source;
+		this.fileManagerImpl = fileManagerImpl;
+	}
+
+	@Override
+	protected WorkflowBundle doInBackground() throws Exception {
+		try {
+			workflowBundle = fileManagerImpl.performOpenDataflow(fileType,
+					source);
+		} catch (OpenException e) {
+			this.e = e;
+		}
+		return workflowBundle;
+	}
+
+	public WorkflowBundle getDataflow() {
+		return workflowBundle;
+	}
+
+	public OpenException getException() {
+		return e;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2DataflowOpener.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2DataflowOpener.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2DataflowOpener.java
new file mode 100644
index 0000000..bf37faf
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2DataflowOpener.java
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * 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.file.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.file.AbstractDataflowPersistenceHandler;
+import net.sf.taverna.t2.workbench.file.DataflowInfo;
+import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.io.ReaderException;
+import uk.org.taverna.scufl2.api.io.WorkflowBundleIO;
+
+public class T2DataflowOpener extends AbstractDataflowPersistenceHandler
+		implements DataflowPersistenceHandler {
+	private static final T2FlowFileType T2_FLOW_FILE_TYPE = new T2FlowFileType();
+	private static Logger logger = Logger.getLogger(T2DataflowOpener.class);
+
+	private WorkflowBundleIO workflowBundleIO;
+
+	@SuppressWarnings("resource")
+	@Override
+	public DataflowInfo openDataflow(FileType fileType, Object source)
+			throws OpenException {
+		if (!getOpenFileTypes().contains(fileType))
+			throw new OpenException("Unsupported file type "
+					+ fileType);
+		InputStream inputStream;
+		Date lastModified = null;
+		Object canonicalSource = source;
+		if (source instanceof InputStream)
+			inputStream = (InputStream) source;
+		else if (source instanceof File)
+			try {
+				inputStream = new FileInputStream((File) source);
+			} catch (FileNotFoundException e) {
+				throw new OpenException("Could not open file " + source + ":\n" + e.getLocalizedMessage(), e);
+			}
+		else if (source instanceof URL) {
+			URL url = ((URL) source);
+			try {
+				URLConnection connection = url.openConnection();
+				connection.setRequestProperty("Accept", "text/xml");
+				inputStream = connection.getInputStream();
+				if (connection.getLastModified() != 0)
+					lastModified = new Date(connection.getLastModified());
+			} catch (IOException e) {
+				throw new OpenException("Could not open connection to URL "
+						+ source+ ":\n" + e.getLocalizedMessage(), e);
+			}
+			try {
+				if (url.getProtocol().equalsIgnoreCase("file"))
+					canonicalSource = new File(url.toURI());
+			} catch (URISyntaxException e) {
+				logger.warn("Invalid file URI created from " + url);
+			}
+		} else {
+			throw new OpenException("Unsupported source type "
+					+ source.getClass());
+		}
+
+		final WorkflowBundle workflowBundle;
+		try {
+			workflowBundle = openDataflowStream(inputStream);
+		} finally {
+			try {
+				if (!(source instanceof InputStream))
+					// We created the stream, we'll close it
+					inputStream.close();
+			} catch (IOException ex) {
+				logger.warn("Could not close inputstream " + inputStream, ex);
+			}
+		}
+		if (canonicalSource instanceof File)
+			return new FileDataflowInfo(T2_FLOW_FILE_TYPE,
+					(File) canonicalSource, workflowBundle);
+		return new DataflowInfo(T2_FLOW_FILE_TYPE, canonicalSource,
+				workflowBundle, lastModified);
+	}
+
+	protected WorkflowBundle openDataflowStream(InputStream workflowXMLstream)
+			throws OpenException {
+		WorkflowBundle workflowBundle;
+		try {
+			workflowBundle = workflowBundleIO.readBundle(workflowXMLstream, null);
+		} catch (ReaderException e) {
+			throw new OpenException("Could not read the workflow", e);
+		} catch (IOException e) {
+			throw new OpenException("Could not open the workflow file for parsing", e);
+		} catch (Exception e) {
+			throw new OpenException("Error while opening workflow", e);
+		}
+
+		return workflowBundle;
+	}
+
+	@Override
+	public List<FileType> getOpenFileTypes() {
+		return Arrays.<FileType> asList(new T2FlowFileType());
+	}
+
+	@Override
+	public List<Class<?>> getOpenSourceTypes() {
+		return Arrays.<Class<?>> asList(InputStream.class, URL.class,
+				File.class);
+	}
+
+	public void setWorkflowBundleIO(WorkflowBundleIO workflowBundleIO) {
+		this.workflowBundleIO = workflowBundleIO;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FileFilter.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FileFilter.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FileFilter.java
new file mode 100644
index 0000000..62b5892
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FileFilter.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.file.impl;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+public class T2FileFilter extends FileFilter {
+	@Override
+	public boolean accept(final File file) {
+		return file.getName().toLowerCase().endsWith(".t2flow");
+	}
+
+	@Override
+	public String getDescription() {
+		return "Taverna 2 workflows";
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FlowFileType.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FlowFileType.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FlowFileType.java
new file mode 100644
index 0000000..a2774e4
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FlowFileType.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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.file.impl;
+
+import net.sf.taverna.t2.workbench.file.FileType;
+
+public class T2FlowFileType extends FileType {
+	public static final String APPLICATION_VND_TAVERNA_T2FLOW_XML = "application/vnd.taverna.t2flow+xml";
+
+	@Override
+	public String getDescription() {
+		return "Taverna 2 workflow";
+	}
+
+	@Override
+	public String getExtension() {
+		return "t2flow";
+	}
+
+	@Override
+	public String getMimeType() {
+		return APPLICATION_VND_TAVERNA_T2FLOW_XML;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileFilter.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileFilter.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileFilter.java
new file mode 100644
index 0000000..d272b41
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileFilter.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.file.impl;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+public class WorkflowBundleFileFilter extends FileFilter {
+	@Override
+	public boolean accept(final File file) {
+		return file.getName().toLowerCase().endsWith(".wfbundle");
+	}
+
+	@Override
+	public String getDescription() {
+		return "Taverna 3 workflows";
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileType.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileType.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileType.java
new file mode 100644
index 0000000..09b30b0
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileType.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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.file.impl;
+
+import net.sf.taverna.t2.workbench.file.FileType;
+
+public class WorkflowBundleFileType extends FileType {
+	public static final String APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE = "application/vnd.taverna.scufl2.workflow-bundle";
+
+	@Override
+	public String getDescription() {
+		return "Taverna 3 workflow";
+	}
+
+	@Override
+	public String getExtension() {
+		return "wfbundle";
+	}
+
+	@Override
+	public String getMimeType() {
+		return APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleOpener.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleOpener.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleOpener.java
new file mode 100644
index 0000000..7c54f7e
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleOpener.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * 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.file.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.file.AbstractDataflowPersistenceHandler;
+import net.sf.taverna.t2.workbench.file.DataflowInfo;
+import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.io.ReaderException;
+import uk.org.taverna.scufl2.api.io.WorkflowBundleIO;
+
+public class WorkflowBundleOpener extends AbstractDataflowPersistenceHandler
+		implements DataflowPersistenceHandler {
+	private static final WorkflowBundleFileType WF_BUNDLE_FILE_TYPE = new WorkflowBundleFileType();
+	private static Logger logger = Logger.getLogger(WorkflowBundleOpener.class);
+	private WorkflowBundleIO workflowBundleIO;
+
+	@SuppressWarnings("resource")
+	@Override
+	public DataflowInfo openDataflow(FileType fileType, Object source)
+			throws OpenException {
+		if (!getOpenFileTypes().contains(fileType))
+			throw new OpenException("Unsupported file type " + fileType);
+		InputStream inputStream;
+		Date lastModified = null;
+		Object canonicalSource = source;
+		if (source instanceof InputStream) {
+			inputStream = (InputStream) source;
+		} else if (source instanceof File) {
+			try {
+				inputStream = new FileInputStream((File) source);
+			} catch (FileNotFoundException e) {
+				throw new OpenException("Could not open file " + source + ":\n"
+						+ e.getLocalizedMessage(), e);
+			}
+		} else if (source instanceof URL) {
+			URL url = ((URL) source);
+			try {
+				URLConnection connection = url.openConnection();
+				connection.setRequestProperty("Accept", "application/zip");
+				inputStream = connection.getInputStream();
+				if (connection.getLastModified() != 0)
+					lastModified = new Date(connection.getLastModified());
+			} catch (IOException e) {
+				throw new OpenException("Could not open connection to URL "
+						+ source + ":\n" + e.getLocalizedMessage(), e);
+			}
+			try {
+				if (url.getProtocol().equalsIgnoreCase("file"))
+					canonicalSource = new File(url.toURI());
+			} catch (URISyntaxException e) {
+				logger.warn("Invalid file URI created from " + url);
+			}
+		} else
+			throw new OpenException("Unsupported source type "
+					+ source.getClass());
+
+		final WorkflowBundle workflowBundle;
+		try {
+			workflowBundle = openDataflowStream(inputStream);
+		} finally {
+			// We created the stream, we'll close it
+			try {
+				if (!(source instanceof InputStream))
+					inputStream.close();
+			} catch (IOException ex) {
+				logger.warn("Could not close inputstream " + inputStream, ex);
+			}
+		}
+		if (canonicalSource instanceof File)
+			return new FileDataflowInfo(WF_BUNDLE_FILE_TYPE,
+					(File) canonicalSource, workflowBundle);
+		return new DataflowInfo(WF_BUNDLE_FILE_TYPE, canonicalSource,
+				workflowBundle, lastModified);
+	}
+
+	protected WorkflowBundle openDataflowStream(InputStream inputStream)
+			throws OpenException {
+		WorkflowBundle workflowBundle;
+		try {
+			workflowBundle = workflowBundleIO.readBundle(inputStream, null);
+		} catch (ReaderException e) {
+			throw new OpenException("Could not read the workflow", e);
+		} catch (IOException e) {
+			throw new OpenException("Could not open the workflow for parsing",
+					e);
+		} catch (Exception e) {
+			throw new OpenException("Error while opening workflow", e);
+		}
+
+		return workflowBundle;
+	}
+
+	@Override
+	public List<FileType> getOpenFileTypes() {
+		return Arrays.<FileType> asList(WF_BUNDLE_FILE_TYPE);
+	}
+
+	@Override
+	public List<Class<?>> getOpenSourceTypes() {
+		return Arrays.<Class<?>> asList(InputStream.class, URL.class,
+				File.class);
+	}
+
+	public void setWorkflowBundleIO(WorkflowBundleIO workflowBundleIO) {
+		this.workflowBundleIO = workflowBundleIO;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleSaver.java
----------------------------------------------------------------------
diff --git a/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleSaver.java b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleSaver.java
new file mode 100644
index 0000000..f6b7108
--- /dev/null
+++ b/taverna-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleSaver.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * 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.file.impl;
+
+import static net.sf.taverna.t2.workbench.file.impl.WorkflowBundleFileType.APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.file.AbstractDataflowPersistenceHandler;
+import net.sf.taverna.t2.workbench.file.DataflowInfo;
+import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.exceptions.SaveException;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.io.WorkflowBundleIO;
+
+public class WorkflowBundleSaver extends AbstractDataflowPersistenceHandler
+		implements DataflowPersistenceHandler {
+	private static final WorkflowBundleFileType WF_BUNDLE_FILE_TYPE = new WorkflowBundleFileType();
+	private static Logger logger = Logger.getLogger(WorkflowBundleSaver.class);
+	private WorkflowBundleIO workflowBundleIO;
+
+	@Override
+	public DataflowInfo saveDataflow(WorkflowBundle workflowBundle, FileType fileType,
+			Object destination) throws SaveException {
+		if (!getSaveFileTypes().contains(fileType))
+			throw new IllegalArgumentException("Unsupported file type "
+					+ fileType);
+		OutputStream outStream;
+		if (destination instanceof File)
+			try {
+				outStream = new FileOutputStream((File) destination);
+			} catch (FileNotFoundException e) {
+				throw new SaveException("Can't create workflow file "
+						+ destination + ":\n" + e.getLocalizedMessage(), e);
+			}
+		else if (destination instanceof OutputStream)
+			outStream = (OutputStream) destination;
+		else
+			throw new SaveException("Unsupported destination type "
+					+ destination.getClass());
+
+		try {
+			saveDataflowToStream(workflowBundle, outStream);
+		} finally {
+			try {
+				// Only close if we opened the stream
+				if (!(destination instanceof OutputStream))
+					outStream.close();
+			} catch (IOException e) {
+				logger.warn("Could not close stream", e);
+			}
+		}
+
+		if (destination instanceof File)
+			return new FileDataflowInfo(WF_BUNDLE_FILE_TYPE, (File) destination,
+					workflowBundle);
+		return new DataflowInfo(WF_BUNDLE_FILE_TYPE, destination, workflowBundle);
+	}
+
+	protected void saveDataflowToStream(WorkflowBundle workflowBundle,
+			OutputStream fileOutStream) throws SaveException {
+		try {
+			workflowBundleIO.writeBundle(workflowBundle, fileOutStream,
+					APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE);
+		} catch (Exception e) {
+			throw new SaveException("Can't write workflow:\n"
+					+ e.getLocalizedMessage(), e);
+		}
+	}
+
+	@Override
+	public List<FileType> getSaveFileTypes() {
+		return Arrays.<FileType> asList(WF_BUNDLE_FILE_TYPE);
+	}
+
+	@Override
+	public List<Class<?>> getSaveDestinationTypes() {
+		return Arrays.<Class<?>> asList(File.class, OutputStream.class);
+	}
+
+	@Override
+	public boolean wouldOverwriteDataflow(WorkflowBundle workflowBundle, FileType fileType,
+			Object destination, DataflowInfo lastDataflowInfo) {
+		if (!getSaveFileTypes().contains(fileType))
+			throw new IllegalArgumentException("Unsupported file type "
+					+ fileType);
+		if (!(destination instanceof File))
+			return false;
+
+		File file;
+		try {
+			file = ((File) destination).getCanonicalFile();
+		} catch (IOException e) {
+			return false;
+		}
+		if (!file.exists())
+			return false;
+		if (lastDataflowInfo == null)
+			return true;
+		Object lastDestination = lastDataflowInfo.getCanonicalSource();
+		if (!(lastDestination instanceof File))
+			return true;
+		File lastFile = (File) lastDestination;
+		if (!lastFile.getAbsoluteFile().equals(file))
+			return true;
+
+		Date lastModified = new Date(file.lastModified());
+		if (lastModified.equals(lastDataflowInfo.getLastModified()))
+			return false;
+		return true;
+	}
+
+	public void setWorkflowBundleIO(WorkflowBundleIO workflowBundleIO) {
+		this.workflowBundleIO = workflowBundleIO;
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/pling-sphere-35.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/pling-sphere-35.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/pling-sphere-35.png
new file mode 100644
index 0000000..3326aad
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/pling-sphere-35.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/pling-sphere-50.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/pling-sphere-50.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/pling-sphere-50.png
new file mode 100644
index 0000000..1f47035
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/pling-sphere-50.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/query-sphere-35.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/query-sphere-35.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/query-sphere-35.png
new file mode 100644
index 0000000..8091ff3
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/query-sphere-35.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/query-sphere-50.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/query-sphere-50.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/query-sphere-50.png
new file mode 100644
index 0000000..443a868
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/query-sphere-50.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_multitype.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_multitype.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_multitype.png
new file mode 100644
index 0000000..1a8e9cf
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_multitype.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_rest.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_rest.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_rest.png
new file mode 100644
index 0000000..a4276c2
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_rest.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_soap.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_soap.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_soap.png
new file mode 100644
index 0000000..aa80209
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_soap.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_unknown.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_unknown.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_unknown.png
new file mode 100644
index 0000000..92638f1
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/service_type_unknown.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/soap_rest_multitype_unknown.pdn
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/soap_rest_multitype_unknown.pdn b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/soap_rest_multitype_unknown.pdn
new file mode 100644
index 0000000..cc7fcb9
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/service_icons/soap_rest_multitype_unknown.pdn differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/styles.css
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/styles.css b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/styles.css
new file mode 100644
index 0000000..48bbdff
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/styles.css
@@ -0,0 +1,290 @@
+/* BEGIN service preview header styles */
+
+p.service_aka_names {
+  margin-top: 5px;
+  margin-bottom: 0;
+}
+
+span.service_aka_name {
+  font-weight: bold;
+  margin-right: 0.5em;
+}
+
+a.service_status_details {
+  text-decoration: none;
+}
+
+a.service_status_details:hover {
+  color: #ff7400;
+  text-decoration: underline;
+}
+
+h1.service_title {
+  border: 1px solid #D7EFA4;
+  background-color: #E9F7CC;
+  color: #000033;
+  text-align: center;
+  font-size: 180%;
+  margin: 0.2em 0 0 0;
+  padding: 0;
+}
+
+
+h2.soap_operation_name {
+  border: 1px solid #D7EFA4;
+  background-color: #E9F7CC;
+  color: #000033;
+  text-align: center;
+  font-size: 120%;
+  margin: 0.2em 0 0 0;
+  padding: 0;
+}
+
+h3.soap_operation_inputs_or_outputs_section {
+  background-color: #EEEEEE;
+  border: 1px solid #CCCCCC;
+  padding: 0.1em 0.6em;
+  font-size: 110%;
+  margin-top: 2em;
+}
+
+div.soap_input_or_output {
+  border: 1px solid #C6961D;
+  margin: 0.5em 1em;
+}
+
+div.soap_input_or_output p.name {
+  background-color: #FFC526;
+  border: 1px solid #C6961D;
+  padding: 0.2em 1em;
+  margin: 0;
+}
+
+div.soap_input_or_output p.body {
+  border: 1px solid #C6961D;
+  padding: 0.5em 1em;
+  margin: 0;
+}
+
+
+.orange_highlight {
+ 	background-color: #ffbb7f;
+	padding: 1px 5px 1px 5px;
+  margin: 0 2px;
+}
+
+.none_text {
+  font-weight: normal;
+  font-style: italic;
+  color: #777777;
+}
+
+.left_indented {
+  margin-left: 2em;
+}
+
+/* END service preview header styles */
+
+
+/* BEGIN service preview annotation styles */
+
+div.annotation {
+  margin: 1em;
+  font-size: 100%;
+}
+
+div.annotation p.key {
+  margin: 0;
+  padding: 0;
+  font-weight: bold;
+}
+
+div.annotation p.value {
+  margin: 0;
+  padding: 0 0.7em;
+}
+
+div.annotation p.value_with_submitter {
+  margin: 0.4em 0;
+  padding: 0.3em 0.7em;
+  background-color: #AAFFC1;
+  border: 1px solid #91D8A4;
+}
+
+div.annotation p.value_with_submitter span.submitter{
+  float: right;
+  font-size: 80%;
+  margin: 0 0.2em 0 1em;
+  padding: 2px 5px 1px 5px;
+  background-color: #D8FFE2;
+}
+
+div.annotation img.link_icon {
+  padding: 0 3px;
+  vertical-align: middle;
+}
+
+div.annotation a {
+  vertical-align: middle;
+  color: #000000;
+}
+
+div.annotation a:link, a:visited  {
+  /*color: #333333;*/
+  text-decoration: underline;
+}
+
+div.annotation a:hover {
+  color: #ff7400;
+  text-decoration: underline;
+}
+
+div.annotation a:active {
+  color: #ff7400;
+  text-decoration: underline;
+}
+
+/* END service preview annotation styles */
+
+
+
+/* BEGIN service type styles */
+
+p.service_types {
+  margin-top: 0.5em;
+  margin-bottom: 0px;
+}
+
+a.service_type_badge {
+	font-size: 85%;
+	font-weight: bold;
+	background-color: #FFF;
+	border: 1px solid #DDD;
+	padding: 2px 3px;
+	text-decoration: none;
+	line-height: 1.0;
+}
+
+a.service_type_badge:hover {
+	color: #FFF;
+	background-color: #999;
+}
+
+/* END service type styles */
+
+
+/* BEGIN categories styles */
+
+.categories_box {
+	font-size: 90%;
+}
+
+ul.categories {
+	float: left;
+	overflow: hidden;
+	margin: 0;
+	padding: 0;
+	list-style-type: none;
+}
+
+ul.categories li {
+	display: inline;
+}
+
+ul.categories li span.title {
+  line-height: 1.6;
+  font-weight: bold;
+  margin-right: 0.7em;
+  vertical-align: middle;
+}
+
+ul.categories li span.main {
+	display:-moz-inline-block;
+	display:-moz-inline-box;
+	display: inline-block;
+	background-color: #FFC;
+	margin-bottom: 0.4em;
+	margin-right: 0.5em;
+	padding: 0;
+	border-top: 1px solid #DEDEDE;
+	border-left: 1px solid #DEDEDE;
+	border-right: 1px solid #999;
+	border-bottom: 1px solid #999;
+}
+
+ul.categories li span.main a.category {
+	line-height: 1.6;
+	text-decoration: none;
+	vertical-align: middle;
+  padding: 3px 5px 2px 5px;
+}
+
+ul.categories li span.main a.category:hover {
+	color: #000;
+  background-color: #F0E68C;
+}
+
+ul.categories li span.main:hover {
+	background-color: #F0E68C;
+}
+
+/* END categories styles */
+
+
+
+/* BEGIN tag cloud styles */
+
+div.tag_cloud a {
+  font-family:Arial,Helvetica,sans-serif;
+  color: #000099;        /* font color as on BioCatalogue */
+  margin: 0 10px;        /* vertical margin doesn't seem to work, so adding line-height to make sure that selection borders on different lines do not overlap */
+  line-height: 1.9em;
+  padding: 0 4px 2px 4px;
+  text-decoration: none;
+  
+  vertical-align: middle;
+}
+
+div.tag_cloud a:hover {
+  color: orange;
+}
+
+div.tag_cloud a.unselected {
+	/* 
+	 * this style is used to make sure that equal space
+	 * around anchor tags is taken by the transparent border
+	 * even when the tag is not "selected" 
+	 */
+	border-style: solid;
+	border-width: 2px;
+	border-color: transparent;
+}
+
+div.tag_cloud a.unselected_ontological_term {
+	/* 
+	 * this style is used to make sure that equal space
+	 * around anchor tags is taken by the transparent border
+	 * even when the tag is not "selected" 
+	 */
+	border-style: solid solid double solid;
+	border-width: 2px 2px 3px 2px;
+	border-color: transparent transparent #000099 transparent;
+}
+
+#ontological_term{
+    color:#3090C7	; /*Deep Sky Blue3*/
+}
+
+div.tag_cloud a.selected {
+	border-style: solid;
+	border-width: 2px;
+	background: #D6E9FF;
+}
+
+div.tag_cloud a.selected_ontological_term {
+	border-style: double double double double;
+	border-width: 3px 3px 3px 3px;
+	background: #D6E9FF;
+}
+
+/* END tag cloud styles */
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/test.html
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/test.html b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/test.html
new file mode 100644
index 0000000..e23f24c
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/test.html
@@ -0,0 +1,78 @@
+<html>
+  <head>
+    <link href="styles.css" media="screen" rel="stylesheet" type="text/css" />
+  </head>
+  <body>
+    <h1 class="service_title">
+      Service:
+      <span class="orange_highlight">Blast</span>
+      <a class="service_type_badge" style="margin-left: 1.5em" href="">SOAP</a>
+    </h1>
+    <p class="left_indented service_aka_names"><span class="none_text">aka</span><span class="service_aka_name"> BLAST(DDBJ)</span></p>
+    
+    <div class="categories_box">
+      <ul class="categories">
+        <li>
+          <span class="title">
+            Categories:
+          </span>
+        </li>
+        <li>
+          <span class="main">
+            <a href="/services?cat=%5B18%5D" class="category">Nucleotide Sequence Similarity</a>
+          </span>
+        </li>
+        <li>
+          <span class="main">
+            <a href="/services?cat=%5B6%5D" class="category">Protein Sequence Similarity</a>
+          </span>
+        </li>
+      </ul>
+    </div>
+
+    <p style="border-top: 1px solid #000000; margin-top: 3em;"></p>
+    
+    <div class="annotation">
+      <p class="key">Provider</p>
+      <p class="value"><a href="">xml.nig.ac.jp</a></p>
+    </div>
+    
+    <div class="annotation">
+      <p class="key">Location</p>
+      <p class="value">Japan</p>
+    </div>
+    
+    <div class="annotation">
+      <p class="key">Submitter / Source</p>
+      <p class="value">
+        <img src="famfamfam_silk/user.png" class="link_icon"/>
+        <a href="">Franck</a>
+        <span class="none_text" style="margin-left: 0.7em; vertical-align: middle;">(2009-01-01)</span>
+      </p>
+    </div>
+    
+    <div class="annotation">
+      <p class="key">Endpoint</p>
+      <p class="value"><a href="">http://xml.nig.ac.jp/xddbj/Blast</a></p>
+    </div>
+    
+    <div class="annotation">
+      <p class="key">WSDL Location</p>
+      <p class="value"><a href="">http://xml.nig.ac.jp/xddbj/Blast.wsdl</a></p>
+    </div>
+    
+    <div class="annotation">
+      <p class="key">Documentation URL(s)</p>
+      <p class="value_with_submitter">
+        <span class="submitter">
+          <span style="vertical-align: middle;">by</span>
+          <img src="famfamfam_silk/remote_resource.png" class="link_icon"/>
+          <a href="">The EMBRACE Registry</a>
+          <span class="none_text" style="margin-left: 0.3em;">(2009-02-03)</span>
+        </span>
+        <a href="">http://xml.nig.ac.jp/index.html</a>
+      </p>
+    </div>
+    
+  </body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tick-sphere-35.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tick-sphere-35.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tick-sphere-35.png
new file mode 100644
index 0000000..3593666
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tick-sphere-35.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tick-sphere-50.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tick-sphere-50.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tick-sphere-50.png
new file mode 100644
index 0000000..f93f269
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tick-sphere-50.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/trash.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/trash.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/trash.png
new file mode 100644
index 0000000..80f5624
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/trash.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_checked.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_checked.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_checked.png
new file mode 100644
index 0000000..af679a2
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_checked.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_partial.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_partial.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_partial.png
new file mode 100644
index 0000000..9a61aa4
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_partial.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_partial_green.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_partial_green.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_partial_green.png
new file mode 100644
index 0000000..9d1eeea
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_partial_green.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_unchecked.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_unchecked.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_unchecked.png
new file mode 100644
index 0000000..d9b5c92
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/tristate_checkbox/tristate_checkbox_unchecked.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/unchecked.png
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/unchecked.png b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/unchecked.png
new file mode 100644
index 0000000..c131d07
Binary files /dev/null and b/taverna-perspective-biocatalogue/src/main/resources/net/sf/taverna/t2/ui/perspectives/biocatalogue/unchecked.png differ

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/xsd/dc.xsd
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/xsd/dc.xsd b/taverna-perspective-biocatalogue/src/main/xsd/dc.xsd
new file mode 100644
index 0000000..815d1a2
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/xsd/dc.xsd
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns="http://purl.org/dc/elements/1.1/"
+           targetNamespace="http://purl.org/dc/elements/1.1/"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified">
+
+  <xs:annotation>
+    <xs:documentation xml:lang="en">
+      Simplified XML Schema for http://purl.org/dc/elements/1.1/
+
+      Created 2007-06-28
+
+      Created by
+
+      Stian Soiland (ssoiland@cs.man.ac.uk)
+
+      This simplification removes SimpleLiteral and specifies dc:any as
+      an xs:string, except for dc:date which is specified as an
+      xs:dateTime. This makes the schema more usable for tools like
+      XMLBeans.
+             
+
+
+      Based on
+      http://dublincore.org/schemas/xmls/qdc/2006/01/06/dc.xsd :
+
+    
+      DCMES 1.1 XML Schema
+      XML Schema for http://purl.org/dc/elements/1.1/ namespace
+
+      Created 2003-04-02
+
+      Created by 
+
+      Tim Cole (t-cole3@uiuc.edu)
+      Tom Habing (thabing@uiuc.edu)
+      Jane Hunter (jane@dstc.edu.au)
+      Pete Johnston (p.johnston@ukoln.ac.uk),
+      Carl Lagoze (lagoze@cs.cornell.edu)
+
+      This schema declares XML elements for the 15 DC elements from the
+      http://purl.org/dc/elements/1.1/ namespace.
+
+      It defines a complexType SimpleLiteral which permits mixed content 
+      and makes the xml:lang attribute available. It disallows child elements by
+      use of minOcccurs/maxOccurs.
+
+      However, this complexType does permit the derivation of other complexTypes
+      which would permit child elements.
+
+      All elements are declared as substitutable for the abstract element any, 
+      which means that the default type for all elements is dc:SimpleLiteral.
+
+    </xs:documentation>
+
+  </xs:annotation>
+
+
+  <xs:import namespace="http://www.w3.org/XML/1998/namespace"
+             schemaLocation="http://www.w3.org/2001/xml.xsd">
+
+  </xs:import>
+
+
+  <xs:element name="any" type="xs:string" abstract="true"/>
+
+  <xs:element name="title" substitutionGroup="any"/>
+  <xs:element name="creator" substitutionGroup="any"/>
+  <xs:element name="subject" substitutionGroup="any"/>
+  <xs:element name="description" substitutionGroup="any"/>
+  <xs:element name="publisher" substitutionGroup="any"/>
+
+  <xs:element name="contributor" substitutionGroup="any"/>
+  <xs:element name="date" type="xs:dateTime" />
+  <xs:element name="type" substitutionGroup="any"/>
+  <xs:element name="format" substitutionGroup="any"/>
+  <xs:element name="identifier" substitutionGroup="any"/>
+  <xs:element name="source" substitutionGroup="any"/>
+  <xs:element name="language" substitutionGroup="any"/>
+  <xs:element name="relation" substitutionGroup="any"/>
+  <xs:element name="coverage" substitutionGroup="any"/>
+
+  <xs:element name="rights" substitutionGroup="any"/>
+
+  <xs:group name="elementsGroup">
+  	<xs:annotation>
+    	<xs:documentation xml:lang="en">
+    	    This group is included as a convenience for schema authors
+            who need to refer to all the elements in the 
+            http://purl.org/dc/elements/1.1/ namespace.
+    	</xs:documentation>
+  	</xs:annotation>
+
+  <xs:sequence>
+
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element ref="any"/>
+    </xs:choice>
+    </xs:sequence>
+  </xs:group>
+
+  <xs:complexType name="elementContainer">
+  	<xs:annotation>
+    	<xs:documentation xml:lang="en">
+
+    		This complexType is included as a convenience for schema authors who need to define a root
+    		or container element for all of the DC elements.
+    	</xs:documentation>
+  	</xs:annotation>
+
+    <xs:choice>
+      <xs:group ref="elementsGroup"/>
+    </xs:choice>
+  </xs:complexType>
+
+
+</xs:schema>
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/xsd/dcterms.xsd
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/xsd/dcterms.xsd b/taverna-perspective-biocatalogue/src/main/xsd/dcterms.xsd
new file mode 100644
index 0000000..205bf48
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/xsd/dcterms.xsd
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns:dc="http://purl.org/dc/elements/1.1/"
+           targetNamespace="http://purl.org/dc/terms/"
+           xmlns="http://purl.org/dc/terms/"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified">
+
+  <xs:annotation>
+    <xs:documentation xml:lang="en">
+      Simplified XML Schema for http://purl.org/dc/terms/
+
+      Created 2007-06-28
+
+      Created by
+
+      Stian Soiland (ssoiland@cs.man.ac.uk)
+
+      This simplification removes SimpleLiteral references
+      so that it can be used with the simplified dc.xsd.
+      This makes the schema more usable for tools like
+      XMLBeans.
+
+
+      Based on
+      http://dublincore.org/schemas/xmls/qdc/2006/01/06/dcterms.xsd :
+
+
+      DCterms XML Schema
+      XML Schema for http://purl.org/dc/terms/ namespace
+
+      Created 2006-01-06
+
+      Created by 
+
+      Tim Cole (t-cole3@uiuc.edu)
+      Tom Habing (thabing@uiuc.edu)
+      Jane Hunter (jane@dstc.edu.au)
+      Pete Johnston (p.johnston@ukoln.ac.uk),
+      Carl Lagoze (lagoze@cs.cornell.edu)
+
+      This schema declares XML elements for the DC elements and
+      DC element refinements from the http://purl.org/dc/terms/ namespace.
+      
+      It reuses the complexType dc:SimpleLiteral, imported from the dc.xsd
+      schema, which permits simple element content, and makes the xml:lang
+      attribute available.
+
+      This complexType permits the derivation of other complexTypes
+      which would permit child elements.
+
+      DC elements are declared as substitutable for the abstract element dc:any, and 
+      DC element refinements are defined as substitutable for the base elements 
+      which they refine.
+
+      This means that the default type for all XML elements (i.e. all DC elements and 
+      element refinements) is dc:SimpleLiteral.
+
+      Encoding schemes are defined as complexTypes which are restrictions
+      of the dc:SimpleLiteral complexType. These complexTypes restrict 
+      values to an appropriates syntax or format using data typing,
+      regular expressions, or enumerated lists.
+  
+      In order to specify one of these encodings an xsi:type attribute must 
+      be used in the instance document.
+
+      Also, note that one shortcoming of this approach is that any type can be 
+      applied to any of the elements or refinements.  There is no convenient way
+      to restrict types to specific elements using this approach.
+
+    </xs:documentation>
+
+  </xs:annotation>
+
+
+  <xs:import namespace="http://www.w3.org/XML/1998/namespace"
+             schemaLocation="http://www.w3.org/2001/xml.xsd">
+
+  </xs:import>
+
+   <xs:import namespace="http://purl.org/dc/elements/1.1/"
+              schemaLocation="dc.xsd"/>
+
+   <xs:element name="alternative" substitutionGroup="dc:title"/>
+
+   <xs:element name="tableOfContents" substitutionGroup="dc:description"/>
+   <xs:element name="abstract" substitutionGroup="dc:description"/>
+
+   <xs:element name="created" substitutionGroup="dc:date"/>
+   <xs:element name="valid" substitutionGroup="dc:date"/>
+
+   <xs:element name="available" substitutionGroup="dc:date"/>
+   <xs:element name="issued" substitutionGroup="dc:date"/>
+   <xs:element name="modified" substitutionGroup="dc:date"/>
+   <xs:element name="dateAccepted" substitutionGroup="dc:date"/>
+   <xs:element name="dateCopyrighted" substitutionGroup="dc:date"/>
+   <xs:element name="dateSubmitted" substitutionGroup="dc:date"/>
+
+   <xs:element name="extent" substitutionGroup="dc:format"/>
+   <xs:element name="medium" substitutionGroup="dc:format"/>
+
+   <xs:element name="isVersionOf" substitutionGroup="dc:relation"/>
+   <xs:element name="hasVersion" substitutionGroup="dc:relation"/>
+   <xs:element name="isReplacedBy" substitutionGroup="dc:relation"/>
+   <xs:element name="replaces" substitutionGroup="dc:relation"/>
+   <xs:element name="isRequiredBy" substitutionGroup="dc:relation"/>
+   <xs:element name="requires" substitutionGroup="dc:relation"/>
+   <xs:element name="isPartOf" substitutionGroup="dc:relation"/>
+   <xs:element name="hasPart" substitutionGroup="dc:relation"/>
+
+   <xs:element name="isReferencedBy" substitutionGroup="dc:relation"/>
+   <xs:element name="references" substitutionGroup="dc:relation"/>
+   <xs:element name="isFormatOf" substitutionGroup="dc:relation"/>
+   <xs:element name="hasFormat" substitutionGroup="dc:relation"/>
+   <xs:element name="conformsTo" substitutionGroup="dc:relation"/>
+
+   <xs:element name="spatial" substitutionGroup="dc:coverage"/>
+   <xs:element name="temporal" substitutionGroup="dc:coverage"/>
+
+   <xs:element name="audience" substitutionGroup="dc:any"/>
+
+   <xs:element name="accrualMethod" substitutionGroup="dc:any"/>
+   <xs:element name="accrualPeriodicity" substitutionGroup="dc:any"/>
+   <xs:element name="accrualPolicy" substitutionGroup="dc:any"/>
+   <xs:element name="instructionalMethod" substitutionGroup="dc:any"/>
+   <xs:element name="provenance" substitutionGroup="dc:any"/>
+   <xs:element name="rightsHolder" substitutionGroup="dc:any"/>
+
+   <xs:element name="mediator" substitutionGroup="audience"/>
+   <xs:element name="educationLevel" substitutionGroup="audience"/>
+
+   <xs:element name="accessRights" substitutionGroup="dc:rights"/>
+   <xs:element name="license" substitutionGroup="dc:rights"/>
+
+   <xs:element name="bibliographicCitation" substitutionGroup="dc:identifier"/>
+
+</xs:schema>


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewFactory.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewFactory.java
new file mode 100644
index 0000000..128cfd3
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ResourcePreviewFactory.java
@@ -0,0 +1,1359 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextPane;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.event.HyperlinkListener;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.HTMLEditorKit;
+
+import net.sf.taverna.t2.lang.ui.DialogTextArea;
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Comment;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.File;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Group;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Pack;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.PackItem;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Tag;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.User;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Util;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Workflow;
+
+import org.apache.log4j.Logger;
+import org.jdom.Document;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class ResourcePreviewFactory {
+  // CONSTANTS
+  private static final int PREFERRED_LOWER_TABBED_PANE_HEIGHT = 250; // used for
+  // all
+  // tabbed
+  // views
+  // inside
+  // preview
+  // for
+  // every
+  // resource
+  // type
+
+  private final MainComponent pluginMainComponent;
+  private final MyExperimentClient myExperimentClient;
+  private final Logger logger;
+
+  // icons which are used in several places in the preview factory
+  private final ImageIcon iconWorkflow;
+  private final ImageIcon iconFile;
+  private final ImageIcon iconPack;
+  private final ImageIcon iconUser;
+  private final ImageIcon iconGroup;
+
+  public ResourcePreviewFactory(MainComponent component, MyExperimentClient client, Logger logger) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the
+	// parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	// set up the icons
+	iconWorkflow = new ImageIcon(MyExperimentPerspective.getLocalIconURL(Resource.WORKFLOW));
+	iconFile = new ImageIcon(MyExperimentPerspective.getLocalIconURL(Resource.FILE));
+	iconPack = new ImageIcon(MyExperimentPerspective.getLocalIconURL(Resource.PACK));
+	iconUser = new ImageIcon(MyExperimentPerspective.getLocalIconURL(Resource.USER));
+	iconGroup = new ImageIcon(MyExperimentPerspective.getLocalIconURL(Resource.GROUP));
+  }
+
+  // main worker method - generates the content to be shown in the preview;
+  // responsible for parsing the preview action request, fetching data and
+  // generating all the content (via helpers)
+  public ResourcePreviewContent createPreview(String action, EventListener eventHandler) {
+	JPanel panelToPopulate = new JPanel();
+
+	// === PREPROCESSING ===
+
+	// return error message if the action string isn't actually a request for
+	// preview
+	if (!action.startsWith("preview:")) {
+	  this.logger.error("Bad preview request: \"" + action + "\"");
+	  panelToPopulate.add(new JLabel("An error has occurred."));
+	  Resource r = new Resource();
+	  r.setItemType(Resource.UNEXPECTED_TYPE);
+	  r.setTitle("Bad preview request");
+	  r.setURI(action);
+	  return (new ResourcePreviewContent(r, panelToPopulate));
+	}
+
+	// parse the action string - we are now sure that it starts with a
+	// 'preview:'
+	action = action.substring(action.indexOf(":") + 1); // remove "preview:"
+	int iType = Integer.parseInt(action.substring(0, action.indexOf(":"))); // get
+	// type
+	action = action.substring(action.indexOf(":") + 1); // remove type
+	String strURI = action; // get URI
+
+	// === FETCHING RESOURCE DATA ===
+	Document doc = null;
+	try {
+	  // the resource type is known at this point, hence can use specialist
+	  // method
+	  // that only fetches required metadata for (individual for each resource
+	  // type)
+	  doc = this.myExperimentClient.getResource(iType, strURI, Resource.REQUEST_FULL_PREVIEW);
+	} catch (Exception e) {
+	  logger.error("Error while fetching resource data from myExperiment to generate a preview.\nResource type: "
+		  + Resource.getResourceTypeName(iType)
+		  + "\nResource URI: "
+		  + strURI
+		  + "\nException: " + e);
+	}
+
+	// === GENERATING PREVIEW ===
+	Resource resource = null;
+	switch (iType) {
+	  case Resource.WORKFLOW:
+		Workflow w = Workflow.buildFromXML(doc, this.logger);
+		resource = w;
+		this.generateWorkflowPreviewContent(w, panelToPopulate, eventHandler);
+		break;
+
+	  case Resource.FILE:
+		File f = File.buildFromXML(doc, this.logger);
+		resource = f;
+		this.generateFilePreviewContent(f, panelToPopulate, eventHandler);
+		break;
+
+	  case Resource.PACK:
+		Pack p = Pack.buildFromXML(doc, this.myExperimentClient, this.logger);
+		resource = p;
+		this.generatePackPreviewContent(p, panelToPopulate, eventHandler);
+		break;
+
+	  case Resource.USER:
+		User u = User.buildFromXML(doc, logger);
+		resource = u;
+		this.generateUserPreviewContent(u, panelToPopulate, eventHandler);
+		break;
+
+	  case Resource.GROUP:
+		Group g = Group.buildFromXML(doc, logger);
+		resource = g;
+		this.generateGroupPreviewContent(g, panelToPopulate, eventHandler);
+		break;
+
+	  default:
+		// unexpected resource type - can't generate preview
+		this.logger.error("Failed generating preview. Reason: unknown resource type - \""
+			+ Resource.getResourceTypeName(iType) + "\"");
+		panelToPopulate.add(new JLabel("Cannot generate preview for unknown resource types."));
+		Resource r = new Resource();
+		r.setItemType(iType);
+		r.setTitle("Error: unknown resource type");
+		r.setURI(strURI);
+		return (new ResourcePreviewContent(r, panelToPopulate));
+	}
+
+	// format output data
+	return (new ResourcePreviewContent(resource, panelToPopulate));
+  }
+
+  private void generateWorkflowPreviewContent(Workflow w, JPanel panelToPopulate, EventListener eventHandler) {
+	if (w != null) {
+	  try {
+		StringBuffer content = new StringBuffer();
+		content.append("<div class='outer'>");
+		content.append("<div class='workflow'>");
+
+		content.append("<br>");
+
+		content.append("<p class='title'>");
+		content.append("Workflow Entry: <a href='preview:" + Resource.WORKFLOW
+			+ ":" + w.getURI() + "'>" + w.getTitle() + "</a> (version "
+			+ w.getVersion() + ")");
+		content.append("</p>");
+
+		content.append("<br>");
+
+		content.append("<p class='info'>");
+		content.append("<b>Type:</b> " + w.getVisibleType() + "<br><br>");
+		content.append("<b>Uploader:</b> <a href='preview:" + Resource.USER
+			+ ":" + w.getUploader().getURI() + "'>" + w.getUploader().getName()
+			+ "</a><br>");
+		content.append("<b>Created at: </b> " + w.getCreatedAt() + "<br>");
+		content.append("<b>License: </b> <a href='"
+			+ w.getLicense().getLink()
+			+ "'>"
+			+ w.getLicense().getText()
+			+ "</a>"
+			+ "&nbsp;<img src='"
+			+ MyExperimentPerspective.getLocalResourceURL("external_link_small_icon")
+			+ "' />");
+		content.append("</p>");
+
+		content.append("<br>");
+
+		content.append("<a href='" + w.getPreview() + "'>");
+		content.append("<img class='preview' src='" + w.getThumbnailBig()
+			+ "'></img>");
+		content.append("</a>");
+
+		content.append("<br>");
+		content.append("<br>");
+
+		if (!w.getDescription().equals("")) {
+		  content.append("<p class='desc'>");
+		  content.append("<br>");
+		  content.append(Util.stripHTML(w.getDescription()));
+		  content.append("<br>");
+		  content.append("</p>");
+		} else {
+		  content.append("<span class='none_text'>No description</span>");
+		}
+
+		content.append("<br>");
+		content.append("</div>");
+		content.append("</div>");
+
+		HTMLEditorKit kit = new StyledHTMLEditorKit(pluginMainComponent.getStyleSheet());
+		HTMLDocument doc = (HTMLDocument) (kit.createDefaultDocument());
+		doc.insertAfterStart(doc.getRootElements()[0].getElement(0), content.toString());
+
+		// === Now render user's items as Swing components ===
+		// .. TABS for components, tags, comments, credits, attributions ..
+		JScrollPane spComponentsTab = createWorkflowComponentPreviewTab(w);
+		JScrollPane spTagsTab = createTagPreviewTab(w.getTags());
+		JScrollPane spCommentsTab = createCommentsPreviewTab(w.getComments());
+		JScrollPane spCreditsTab = createCreditsPreviewTab(w.getCredits());
+		JScrollPane spAttributionsTab = createAttributionsPreviewTab(w.getAttributions());
+
+		// .. ASSEMBLE ALL TABS together
+		JTabbedPane tpTabbedView = new JTabbedPane();
+		tpTabbedView.add("Components", spComponentsTab);
+		tpTabbedView.add("Tags (" + w.getTags().size() + ")", spTagsTab);
+		tpTabbedView.add("Comments (" + w.getComments().size() + ")", spCommentsTab);
+		tpTabbedView.addTab("Credits (" + w.getCredits().size() + ")", spCreditsTab);
+		tpTabbedView.addTab("Attributions (" + w.getAttributions().size() + ")", spAttributionsTab);
+
+		// PUT EVERYTHING TOGETHER
+		JTextPane tpWorkflowPreview = new JTextPane();
+		tpWorkflowPreview.setEditable(false);
+		tpWorkflowPreview.setEditorKit(kit);
+		tpWorkflowPreview.setDocument(doc);
+		tpWorkflowPreview.addHyperlinkListener((HyperlinkListener) eventHandler);
+
+		JPanel jpFullWorkflowPreview = wrapTextPaneAndTabbedViewIntoFullPreview(tpWorkflowPreview, tpTabbedView);
+
+		// POPULATE THE GIVEN PANEL
+		panelToPopulate.setLayout(new BorderLayout());
+		panelToPopulate.add(jpFullWorkflowPreview, BorderLayout.CENTER);
+
+		// this.statusLabel.setText("Workflow information found. Last fetched: "
+		// + new Date().toString());
+
+		// this.clearButton.setEnabled(true);
+		// this.refreshButton.setEnabled(true);
+		// this.loadButton.setEnabled(true);
+		// this.importButton.setEnabled(true);
+	  } catch (Exception e) {
+		logger.error("Failed to populate Workflow Preview pane", e);
+	  }
+	} else {
+	  // statusLabel.setText("Could not find information for workflow ID: " +
+	  // currentWorkflowId);
+	  // clearContentTextPane();
+	  // disableButtons();
+	}
+  }
+
+  private void generateFilePreviewContent(File f, JPanel panelToPopulate, EventListener eventHandler) {
+	if (f != null) {
+	  try {
+		StringBuffer content = new StringBuffer();
+		content.append("<div class='outer'>");
+		content.append("<div class='file'>");
+
+		content.append("<br>");
+
+		content.append("<p class='title'>");
+		content.append("File: <a href='preview:" + Resource.FILE + ":"
+			+ f.getURI() + "'>" + f.getTitle() + "</a>");
+		content.append("</p>");
+
+		content.append("<br>");
+
+		content.append("<p class='info'>");
+		content.append("<b>Type:</b> " + f.getVisibleType() + "<br>");
+		content.append("<b>Filename:</b> " + f.getFilename() + "<br><br>");
+		content.append("<b>Uploader:</b> <a href='preview:" + Resource.USER
+			+ ":" + f.getUploader().getURI() + "'>" + f.getUploader().getName()
+			+ "</a><br>");
+		content.append("<b>Created at: </b> " + f.getCreatedAt() + "<br>");
+		content.append("<b>Last updated at: </b> " + f.getUpdatedAt() + "<br>");
+		content.append("<b>License: </b> <a href='"
+			+ f.getLicense().getLink()
+			+ "'>"
+			+ f.getLicense().getText()
+			+ "</a>"
+			+ "&nbsp;<img src='"
+			+ MyExperimentPerspective.getLocalResourceURL("external_link_small_icon")
+			+ "' />");
+		content.append("</p>");
+
+		content.append("<br>");
+
+		if (!f.getDescription().equals("")) {
+		  content.append("<p class='desc'>");
+		  content.append("<br>");
+		  content.append(Util.stripHTML(f.getDescription()));
+		  content.append("<br>");
+		  content.append("</p>");
+		} else {
+		  content.append("<span class='none_text'>No description</span>");
+		}
+
+		content.append("<br>");
+		content.append("</div>");
+		content.append("</div>");
+
+		HTMLEditorKit kit = new StyledHTMLEditorKit(pluginMainComponent.getStyleSheet());
+		HTMLDocument doc = (HTMLDocument) (kit.createDefaultDocument());
+		doc.insertAfterStart(doc.getRootElements()[0].getElement(0), content.toString());
+
+		// === Now render group's items as Swing components ===
+		// TABS FOR file's tags, credits, etc
+		JScrollPane spTagsTab = createTagPreviewTab(f.getTags());
+		JScrollPane spCommentsTab = createCommentsPreviewTab(f.getComments());
+		JScrollPane spCreditsTab = createCreditsPreviewTab(f.getCredits());
+		JScrollPane spAttributionsTab = createAttributionsPreviewTab(f.getAttributions());
+
+		// ASSEMBLE tabs into tabbed view
+		JTabbedPane tpTabbedView = new JTabbedPane();
+		tpTabbedView.add("Tags (" + f.getTags().size() + ")", spTagsTab);
+		tpTabbedView.add("Comments (" + f.getComments().size() + ")", spCommentsTab);
+		tpTabbedView.add("Credits (" + f.getCredits().size() + ")", spCreditsTab);
+		tpTabbedView.add("Attributions (" + f.getAttributions().size() + ")", spAttributionsTab);
+
+		// PUT EVERYTHING TOGETHER
+		JTextPane tpFilePreview = new JTextPane();
+		tpFilePreview.setEditable(false);
+		tpFilePreview.setEditorKit(kit);
+		tpFilePreview.setDocument(doc);
+		tpFilePreview.addHyperlinkListener((HyperlinkListener) eventHandler);
+
+		JPanel jpFullFilePreview = new JPanel();
+		jpFullFilePreview.setBackground(Color.WHITE); // white background for
+		// the whole file preview
+		// panel
+		jpFullFilePreview.setLayout(new GridBagLayout());
+		GridBagConstraints c = new GridBagConstraints();
+
+		c.gridx = GridBagConstraints.REMAINDER;
+		c.gridy = 0;
+		c.weighty = 0; // will not change size when the window is resized
+		jpFullFilePreview.add(tpFilePreview, c);
+
+		c.gridx = GridBagConstraints.REMAINDER;
+		c.gridy = 1;
+		c.weighty = 1; // will grow in size when the window is resized..
+		c.fill = GridBagConstraints.VERTICAL; // ..and fill all available space
+		// vertically
+		c.insets = new Insets(20, 0, 5, 0); // a bit of margin at the top &
+		// bottom
+		jpFullFilePreview.add(tpTabbedView, c);
+
+		// POPULATE THE GIVEN PANEL
+		panelToPopulate.setLayout(new BorderLayout());
+		panelToPopulate.add(jpFullFilePreview, BorderLayout.CENTER);
+
+		// this.statusLabel.setText("File information found. Last fetched: " +
+		// new Date().toString());
+
+		// this.clearButton.setEnabled(true);
+		// this.refreshButton.setEnabled(true);
+		// this.loadButton.setEnabled(true);
+		// this.importButton.setEnabled(true);
+	  } catch (Exception e) {
+		logger.error("Failed to populate File Preview pane", e);
+	  }
+	} else {
+	  // statusLabel.setText("Could not find information for file ID: " +
+	  // currentFileId);
+	  // clearContentTextPane();
+	  // disableButtons();
+	}
+  }
+
+  private void generatePackPreviewContent(Pack p, JPanel panelToPopulate, EventListener eventHandler) {
+	if (p != null) {
+	  try {
+		// === Render pack details in HTML format ===
+		StringBuffer content = new StringBuffer();
+		content.append("<div class='outer'>");
+		content.append("<div class='pack'>");
+
+		content.append("<br>");
+
+		content.append("<p class='title'>");
+		content.append("Pack: <a href='preview:" + Resource.PACK + ":"
+			+ p.getURI() + "'>" + p.getTitle() + "</a>");
+		content.append("</p>");
+
+		content.append("<br>");
+
+		content.append("<p class='info'>");
+		content.append("<b>Creator:</b> <a href='preview:" + Resource.USER
+			+ ":" + p.getCreator().getURI() + "'>" + p.getCreator().getName()
+			+ "</a><br>");
+		content.append("<b>Created at: </b> " + p.getCreatedAt() + "<br>");
+		content.append("<b>Last updated at: </b> " + p.getUpdatedAt() + "<br>");
+		content.append("</p>");
+
+		content.append("<br>");
+
+		if (!p.getDescription().equals("")) {
+		  content.append("<p class='desc'>");
+		  content.append("<br>");
+		  content.append(Util.stripHTML(p.getDescription()));
+		  content.append("<br>");
+		  content.append("<br>");
+		  content.append("</p>");
+		} else {
+		  content.append("<span class='none_text'>No description</span>");
+		}
+
+		content.append("<br>");
+		content.append("</div>");
+		content.append("</div>");
+
+		HTMLEditorKit kit = new StyledHTMLEditorKit(pluginMainComponent.getStyleSheet());
+		HTMLDocument doc = (HTMLDocument) (kit.createDefaultDocument());
+		doc.insertAfterStart(doc.getRootElements()[0].getElement(0), content.toString());
+
+		// === Now render group's items as Swing components ===
+		// TABS FOR pack items, tags, etc
+		JScrollPane spPackItemsTab = createPackItemPreviewTab(p);
+		JScrollPane spTagsTab = createTagPreviewTab(p.getTags());
+		JScrollPane spCommentsTab = createCommentsPreviewTab(p.getComments());
+
+		// ASSEMBLE tabs into tabbed view
+		JTabbedPane tpTabbedView = new JTabbedPane();
+		tpTabbedView.addTab("Pack Items (" + p.getItemCount() + ")", spPackItemsTab);
+		tpTabbedView.add("Tags (" + p.getTags().size() + ")", spTagsTab);
+		tpTabbedView.add("Comments (" + p.getComments().size() + ")", spCommentsTab);
+
+		// PUT EVERYTHING TOGETHER
+		JTextPane tpPackPreview = new JTextPane();
+		tpPackPreview.setEditable(false);
+		tpPackPreview.setEditorKit(kit);
+		tpPackPreview.setDocument(doc);
+		tpPackPreview.addHyperlinkListener((HyperlinkListener) eventHandler);
+
+		JPanel jpFullPackPreview = new JPanel();
+		jpFullPackPreview.setBackground(Color.WHITE); // white background for
+		// the whole pack preview
+		// panel
+		jpFullPackPreview.setLayout(new GridBagLayout());
+		GridBagConstraints c = new GridBagConstraints();
+
+		c.gridx = GridBagConstraints.REMAINDER;
+		c.gridy = 0;
+		c.weighty = 0; // will not change size when the window is resized
+		jpFullPackPreview.add(tpPackPreview, c);
+
+		c.gridx = GridBagConstraints.REMAINDER;
+		c.gridy = 1;
+		c.weighty = 1; // will grow in size when the window is resized..
+		c.fill = GridBagConstraints.VERTICAL; // ..and fill all available space
+		// vertically
+		c.insets = new Insets(20, 0, 5, 0); // a bit of margin at the top &
+		// bottom
+		jpFullPackPreview.add(tpTabbedView, c);
+
+		// POPULATE THE GIVEN PANEL
+		panelToPopulate.setLayout(new BorderLayout());
+		panelToPopulate.add(jpFullPackPreview, BorderLayout.CENTER);
+
+		// this.statusLabel.setText("Pack information found. Last fetched: " +
+		// new Date().toString());
+
+		// this.clearButton.setEnabled(true);
+		// this.refreshButton.setEnabled(true);
+		// this.loadButton.setEnabled(true);
+		// this.importButton.setEnabled(true);
+	  } catch (Exception e) {
+		logger.error("Failed to populate Pack Preview pane", e);
+	  }
+	} else {
+	  // statusLabel.setText("Could not find information for pack ID: " +
+	  // currentPackId);
+	  // clearContentTextPane();
+	  // disableButtons();
+	}
+  }
+
+  private void generateUserPreviewContent(User u, JPanel panelToPopulate, EventListener eventHandler) {
+	if (u != null) {
+	  try {
+		// === Render user details in HTML format ===
+		StringBuffer content = new StringBuffer();
+		content.append("<div class='outer'>");
+		content.append("<div class='user'>");
+
+		content.append("<br>");
+
+		content.append("<p class='name'>");
+		content.append("User: <a href=preview:" + Resource.USER + ":"
+			+ u.getURI() + "'>" + u.getName() + "</a>");
+		content.append("</p>");
+
+		content.append("<br>");
+
+		content.append("<p class='info'>");
+		String strLocation;
+		if (u.getCity().length() == 0 && u.getCountry().length() == 0)
+		  strLocation = "<span class='none_text'>Not specified</span>";
+		else
+		  strLocation = u.getCity()
+			  + (u.getCity().length() == 0 || u.getCountry().length() == 0 ? "" : ", ")
+			  + u.getCountry();
+		content.append("<b>Location:</b> " + strLocation + "<br>");
+		content.append("<b>Joined at: </b> " + u.getCreatedAt() + "<br>");
+		content.append("<b>Last seen at: </b> " + u.getUpdatedAt() + "<br>");
+		content.append("</p>");
+
+		content.append("<br>");
+
+		content.append("<a href='" + u.getAvatarResource() + "'>");
+		content.append("<img class='avatar' src='" + u.getAvatarResource()
+			+ "'></img>");
+		content.append("</a>");
+
+		content.append("<br>");
+		content.append("<br>");
+
+		if (!u.getDescription().equals("")) {
+		  // HACK: the way JAVA renders html causes styling not to be inherited;
+		  // hence need to
+		  // remove any nested <p> or <div> tags to get a proper layout
+		  content.append("<p class='desc'>"
+			  + Util.stripHTML(u.getDescription()) + "<br><br></p>");
+		} else {
+		  content.append("<span class='none_text'>No description</span>");
+		}
+
+		content.append("<p class='contact_details_header'>Contact Details</p>");
+		content.append("<p class='contact_details'>");
+		content.append("<b>Email: </b>"
+			+ (u.getEmail().length() == 0 ? "<span class='none_text'>Not specified</span>" : u.getEmail())
+			+ "<br>");
+		content.append("<b>Website: </b>"
+			+ (u.getWebsite().length() == 0 ? "<span class='none_text'>Not specified</span>" : u.getWebsite()));
+		content.append("</p>");
+
+		content.append("</div>");
+		content.append("</div>");
+
+		HTMLEditorKit kit = new StyledHTMLEditorKit(pluginMainComponent.getStyleSheet());
+		HTMLDocument doc = (HTMLDocument) (kit.createDefaultDocument());
+		doc.insertAfterStart(doc.getRootElements()[0].getElement(0), content.toString());
+
+		// === Now render user's items as Swing components ===
+		// .. WORKFLOWS ..
+		JPanel jpWorkflowsTabContent = new JPanel();
+		jpWorkflowsTabContent.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
+		jpWorkflowsTabContent.setLayout(new BoxLayout(jpWorkflowsTabContent, BoxLayout.Y_AXIS));
+
+		// iterate through all workflows and add all to the panel
+		Iterator<HashMap<String, String>> iWorkflows = u.getWorkflows().iterator();
+		while (iWorkflows.hasNext()) {
+		  HashMap<String, String> hmCurWF = iWorkflows.next();
+		  jpWorkflowsTabContent.add(new JClickableLabel(hmCurWF.get("name"), "preview:"
+			  + Resource.WORKFLOW + ":" + hmCurWF.get("uri"), pluginMainComponent.getPreviewBrowser(), this.iconWorkflow));
+		}
+
+		// .. FILES ..
+		JPanel jpFilesTabContent = new JPanel();
+		jpFilesTabContent.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
+		jpFilesTabContent.setLayout(new BoxLayout(jpFilesTabContent, BoxLayout.Y_AXIS));
+
+		// iterate through all files and add all to the panel
+		Iterator<HashMap<String, String>> iFiles = u.getFiles().iterator();
+		while (iFiles.hasNext()) {
+		  HashMap<String, String> hmCurFile = iFiles.next();
+		  jpFilesTabContent.add(new JClickableLabel(hmCurFile.get("name"), "preview:"
+			  + Resource.FILE + ":" + hmCurFile.get("uri"), pluginMainComponent.getPreviewBrowser(), this.iconFile));
+		}
+
+		// .. PACKS ..
+		JPanel jpPacksTabContent = new JPanel();
+		jpPacksTabContent.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
+		jpPacksTabContent.setLayout(new BoxLayout(jpPacksTabContent, BoxLayout.Y_AXIS));
+
+		// iterate through all packs and add all to the panel
+		Iterator<HashMap<String, String>> iPacks = u.getPacks().iterator();
+		while (iPacks.hasNext()) {
+		  HashMap<String, String> hmCurPack = iPacks.next();
+		  jpPacksTabContent.add(new JClickableLabel(hmCurPack.get("name"), "preview:"
+			  + Resource.PACK + ":" + hmCurPack.get("uri"), pluginMainComponent.getPreviewBrowser(), this.iconPack));
+		}
+
+		// .. FRIENDS ..
+		JPanel jpFriendsTabContent = new JPanel();
+		jpFriendsTabContent.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
+		jpFriendsTabContent.setLayout(new BoxLayout(jpFriendsTabContent, BoxLayout.Y_AXIS));
+
+		// iterate through all friends and add all to the panel
+		Iterator<HashMap<String, String>> iFriends = u.getFriends().iterator();
+		while (iFriends.hasNext()) {
+		  HashMap<String, String> hmCurFriend = iFriends.next();
+		  jpFriendsTabContent.add(new JClickableLabel(hmCurFriend.get("name"), "preview:"
+			  + Resource.USER + ":" + hmCurFriend.get("uri"), pluginMainComponent.getPreviewBrowser(), this.iconUser));
+		}
+
+		// .. GROUPS ..
+		JPanel jpGroupsTabContent = new JPanel();
+		jpGroupsTabContent.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
+		jpGroupsTabContent.setLayout(new BoxLayout(jpGroupsTabContent, BoxLayout.Y_AXIS));
+
+		// iterate through all groups and add all to the panel
+		Iterator<HashMap<String, String>> iGroups = u.getGroups().iterator();
+		while (iGroups.hasNext()) {
+		  HashMap<String, String> hmCurGroup = iGroups.next();
+		  jpGroupsTabContent.add(new JClickableLabel(hmCurGroup.get("name"), "preview:"
+			  + Resource.GROUP + ":" + hmCurGroup.get("uri"), pluginMainComponent.getPreviewBrowser(), this.iconGroup));
+		}
+
+		// .. WRAP EVERY TAB content into it's own scroll pane ..
+		Dimension dPreferredTabSize = new Dimension(ResourcePreviewBrowser.PREFERRED_WIDTH - 50, PREFERRED_LOWER_TABBED_PANE_HEIGHT);
+
+		JScrollPane spWorkflowsTab = new JScrollPane(jpWorkflowsTabContent);
+		spWorkflowsTab.setBorder(BorderFactory.createEmptyBorder());
+		spWorkflowsTab.setPreferredSize(dPreferredTabSize);
+		spWorkflowsTab.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+		JScrollPane spFilesTab = new JScrollPane(jpFilesTabContent);
+		spFilesTab.setBorder(BorderFactory.createEmptyBorder());
+		spFilesTab.setPreferredSize(dPreferredTabSize);
+		spFilesTab.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+		JScrollPane spPacksTab = new JScrollPane(jpPacksTabContent);
+		spPacksTab.setBorder(BorderFactory.createEmptyBorder());
+		spPacksTab.setPreferredSize(dPreferredTabSize);
+		spPacksTab.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+		JScrollPane spFriendsTab = new JScrollPane(jpFriendsTabContent);
+		spFriendsTab.setBorder(BorderFactory.createEmptyBorder());
+		spFriendsTab.setPreferredSize(dPreferredTabSize);
+		spFriendsTab.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+		JScrollPane spGroupsTab = new JScrollPane(jpGroupsTabContent);
+		spGroupsTab.setBorder(BorderFactory.createEmptyBorder());
+		spGroupsTab.setPreferredSize(dPreferredTabSize);
+		spGroupsTab.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+		// .. ASSEMBLE ALL TABS together
+		JTabbedPane tpTabbedItems = new JTabbedPane();
+		tpTabbedItems.addTab("Workflows (" + u.getWorkflows().size() + ")", spWorkflowsTab);
+		tpTabbedItems.addTab("Files (" + u.getFiles().size() + ")", spFilesTab);
+		tpTabbedItems.addTab("Packs (" + u.getPacks().size() + ")", spPacksTab);
+		tpTabbedItems.addTab("Friends (" + u.getFriends().size() + ")", spFriendsTab);
+		tpTabbedItems.addTab("Groups (" + u.getGroups().size() + ")", spGroupsTab);
+
+		// === PUT EVERYTHING TOGETHER ===
+		JTextPane tpUserPreview = new JTextPane();
+		tpUserPreview.setEditable(false);
+		tpUserPreview.setEditorKit(kit);
+		tpUserPreview.setDocument(doc);
+		tpUserPreview.addHyperlinkListener((HyperlinkListener) eventHandler);
+
+		JPanel jpFullUserPreview = new JPanel();
+		jpFullUserPreview.setBackground(Color.WHITE); // white background for
+		// the whole user preview
+		// panel
+		jpFullUserPreview.setLayout(new GridBagLayout());
+		GridBagConstraints c = new GridBagConstraints();
+
+		c.gridx = GridBagConstraints.REMAINDER;
+		c.gridy = 0;
+		c.weighty = 0; // will not change size when the window is resized
+		jpFullUserPreview.add(tpUserPreview, c);
+
+		c.gridx = GridBagConstraints.REMAINDER;
+		c.gridy = 1;
+		c.weighty = 1; // will grow in size when the window is resized..
+		c.fill = GridBagConstraints.VERTICAL; // ..and fill all available space
+		// vertically
+		c.insets = new Insets(20, 0, 5, 0); // a bit of margin at the top &
+		// bottom
+		jpFullUserPreview.add(tpTabbedItems, c);
+
+		// POPULATE THE GIVEN PANEL
+		panelToPopulate.setLayout(new BorderLayout());
+		panelToPopulate.add(jpFullUserPreview, BorderLayout.CENTER);
+
+		// this.statusLabel.setText("User information found. Last fetched: " +
+		// new Date().toString());
+
+		// this.clearButton.setEnabled(true);
+		// this.refreshButton.setEnabled(true);
+		// this.loadButton.setEnabled(true);
+		// this.importButton.setEnabled(true);
+	  } catch (Exception e) {
+		logger.error("Failed to populate Workflow Preview pane", e);
+	  }
+	} else {
+	  // statusLabel.setText("Could not find information for workflow ID: " +
+	  // currentWorkflowId);
+	  // clearContentTextPane();
+	  // disableButtons();
+	}
+  }
+
+  private void generateGroupPreviewContent(Group g, JPanel panelToPopulate, EventListener eventHandler) {
+	if (g != null) {
+	  try {
+		// === Render group details in HTML format ===
+		StringBuffer content = new StringBuffer();
+		content.append("<div class='outer'>");
+		content.append("<div class='group'>");
+
+		content.append("<br>");
+
+		content.append("<p class='title'>");
+		content.append("Group: <a href='preview:" + Resource.GROUP + ":"
+			+ g.getURI() + "'>" + g.getTitle() + "</a>");
+		content.append("</p>");
+
+		content.append("<br>");
+
+		content.append("<p class='info'>");
+		content.append("<b>Administrator:</b> <a href='preview:"
+			+ Resource.USER + ":" + g.getAdmin().getURI() + "'>"
+			+ g.getAdmin().getName() + "</a><br>");
+		content.append("<b>Created at: </b> " + g.getCreatedAt() + "<br>");
+		content.append("</p>");
+
+		content.append("<br>");
+
+		if (!g.getDescription().equals("")) {
+		  content.append("<p class='desc'>");
+		  content.append("<br>");
+		  content.append(Util.stripHTML(g.getDescription()));
+		  content.append("<br>");
+		  content.append("</p>");
+		} else {
+		  content.append("<span class='none_text'>No description</span>");
+		}
+
+		content.append("<br>");
+		content.append("</div>");
+		content.append("</div>");
+
+		HTMLEditorKit kit = new StyledHTMLEditorKit(pluginMainComponent.getStyleSheet());
+		HTMLDocument doc = (HTMLDocument) (kit.createDefaultDocument());
+		doc.insertAfterStart(doc.getRootElements()[0].getElement(0), content.toString());
+
+		// === Now render group's items as Swing components ===
+
+		// .. MEMBERS ..
+		JPanel jpMembersTabContent = createStandardTabContentPanel();
+
+		// iterate through all shared items and add all to the panel
+		Iterator<User> iMembers = g.getMembers().iterator();
+		while (iMembers.hasNext()) {
+		  User uCurMember = iMembers.next();
+		  jpMembersTabContent.add(new JClickableLabel(uCurMember.getName(), "preview:"
+			  + uCurMember.getItemType() + ":" + uCurMember.getURI(), pluginMainComponent.getPreviewBrowser(), new ImageIcon(MyExperimentPerspective.getLocalIconURL(uCurMember.getItemType()))));
+		}
+
+		// wrap into a standard scroll pane
+		JScrollPane spMembersTabContent = wrapPreviewTabContentIntoScrollPane(jpMembersTabContent);
+
+		// .. SHARED ITEMS ..
+		JPanel jpSharedItemsTabContent = createStandardTabContentPanel();
+
+		// iterate through all shared items and add all to the panel
+		Iterator<Resource> iSharedItems = g.getSharedItems().iterator();
+		while (iSharedItems.hasNext()) {
+		  Resource rCurItem = iSharedItems.next();
+		  jpSharedItemsTabContent.add(new JClickableLabel(rCurItem.getTitle(), "preview:"
+			  + rCurItem.getItemType() + ":" + rCurItem.getURI(), pluginMainComponent.getPreviewBrowser(), new ImageIcon(MyExperimentPerspective.getLocalIconURL(rCurItem.getItemType()))));
+		}
+
+		// wrap into a standard scroll pane
+		JScrollPane spSharedItemsTabContent = wrapPreviewTabContentIntoScrollPane(jpSharedItemsTabContent);
+
+		// .. TAGS, COMMENTS ..
+		JScrollPane spTagsTabContent = createTagPreviewTab(g.getTags());
+		JScrollPane spCommentsTab = createCommentsPreviewTab(g.getComments());
+
+		// ASSEMBLE tabs together
+		JTabbedPane tpTabbedItems = new JTabbedPane();
+		tpTabbedItems.addTab("Members (" + g.getMemberCount() + ")", spMembersTabContent);
+		tpTabbedItems.addTab("Shared Items (" + g.getSharedItemCount() + ")", spSharedItemsTabContent);
+		tpTabbedItems.addTab("Tags (" + g.getTags().size() + ")", spTagsTabContent);
+		tpTabbedItems.addTab("Comments (" + g.getComments().size() + ")", spCommentsTab);
+
+		// PUT EVERYTHING TOGETHER
+		JTextPane tpGroupPreview = new JTextPane();
+		tpGroupPreview.setEditable(false);
+		tpGroupPreview.setEditorKit(kit);
+		tpGroupPreview.setDocument(doc);
+		tpGroupPreview.addHyperlinkListener((HyperlinkListener) eventHandler);
+
+		JPanel jpFullGroupPreview = new JPanel();
+		jpFullGroupPreview.setBackground(Color.WHITE); // white background for
+		// the whole group
+		// preview panel
+		jpFullGroupPreview.setLayout(new GridBagLayout());
+		GridBagConstraints c = new GridBagConstraints();
+
+		c.gridx = GridBagConstraints.REMAINDER;
+		c.gridy = 0;
+		c.weighty = 0; // will not change size when the window is resized
+		jpFullGroupPreview.add(tpGroupPreview, c);
+
+		c.gridx = GridBagConstraints.REMAINDER;
+		c.gridy = 1;
+		c.weighty = 1; // will grow in size when the window is resized..
+		c.fill = GridBagConstraints.VERTICAL; // ..and fill all available space
+		// vertically
+		c.insets = new Insets(20, 0, 5, 0); // a bit of margin at the top &
+		// bottom
+		jpFullGroupPreview.add(tpTabbedItems, c);
+
+		// POPULATE THE GIVEN PANEL
+		panelToPopulate.setLayout(new BorderLayout());
+		panelToPopulate.add(jpFullGroupPreview, BorderLayout.CENTER);
+
+		// this.statusLabel.setText("Group information found. Last fetched: " +
+		// new Date().toString());
+
+		// this.clearButton.setEnabled(true);
+		// this.refreshButton.setEnabled(true);
+		// this.loadButton.setEnabled(true);
+		// this.importButton.setEnabled(true);
+	  } catch (Exception e) {
+		logger.error("Failed to populate Group Preview pane", e);
+	  }
+	} else {
+	  // statusLabel.setText("Could not find information for group ID: " +
+	  // currentGroupId);
+	  // clearContentTextPane();
+	  // disableButtons();
+	}
+  }
+
+  // *** Helper methods follow that generate particular reusable pieces of the
+  // previews ***
+
+  private JScrollPane createWorkflowComponentPreviewTab(Workflow w) {
+	final JPanel jpWorkflowComponentsTabContent = createStandardTabContentPanel();
+
+	if (!w.isTavernaWorkflow()) {
+	  // can only display components for Taverna 1 workflows at the moment
+	  JLabel lNotSupported = new JLabel("<html>This is a "
+		  + w.getVisibleType()
+		  + " workflow;<br>myExperiment can only display Taverna workflow components at the moment.</html>");
+	  lNotSupported.setFont(lNotSupported.getFont().deriveFont(Font.ITALIC));
+	  lNotSupported.setForeground(Color.GRAY);
+	  jpWorkflowComponentsTabContent.add(lNotSupported);
+	} else if (!w.isDownloadAllowed()) {
+	  // can display components for workflow of this type, but current user is
+	  // not
+	  // allowed to download this workflow - and, hence, to view its components
+	  JLabel lNotAuthorized = new JLabel("You are not authorised to download this workflow, "
+		  + "and hence component preview is not available.");
+	  lNotAuthorized.setFont(lNotAuthorized.getFont().deriveFont(Font.ITALIC));
+	  lNotAuthorized.setForeground(Color.GRAY);
+	  jpWorkflowComponentsTabContent.add(lNotAuthorized);
+	} else {
+	  // can display the components
+
+	  // storage for table column names
+	  Vector<String> vColumnNames = new Vector<String>();
+
+	  // ** inputs **
+	  vColumnNames.clear();
+	  vColumnNames.addAll(Arrays.asList(new String[] { "Name", "Description" }));
+
+	  Vector<Vector<String>> vInputsData = new Vector<Vector<String>>();
+	  ArrayList<HashMap<String, String>> inputs = w.getComponents().get("inputs");
+	  if (inputs != null) {
+		for (HashMap<String, String> curInput : inputs) {
+		  Vector<String> vCurData = new Vector<String>();
+		  vCurData.add(curInput.get("name"));
+		  vCurData.add(curInput.get("description"));
+
+		  vInputsData.add(vCurData);
+		}
+	  }
+
+	  JTable jtInputs = new JTable(vInputsData, vColumnNames);
+	  jtInputs.getColumnModel().getColumn(0).setPreferredWidth(100);
+	  jtInputs.getTableHeader().setFont(jtInputs.getTableHeader().getFont().deriveFont(Font.BOLD));
+	  JPanel jpInputs = new JPanel();
+	  jpInputs.setLayout(new BorderLayout());
+	  jpInputs.add(jtInputs.getTableHeader(), BorderLayout.NORTH);
+	  jpInputs.add(jtInputs, BorderLayout.CENTER);
+
+	  JPanel jpInputsWithTitle = new JPanel();
+	  jpInputsWithTitle.setBorder(BorderFactory.createEtchedBorder());
+	  jpInputsWithTitle.setLayout(new BorderLayout());
+	  jpInputsWithTitle.add(new ShadedLabel("Workflow input ports ("
+		  + vInputsData.size() + ")", ShadedLabel.BLUE, true), BorderLayout.NORTH);
+	  if (vInputsData.size() > 0) {
+		jpInputsWithTitle.add(jpInputs, BorderLayout.CENTER);
+	  }
+
+	  // ** processors **
+	  vColumnNames.clear();
+	  vColumnNames.addAll(Arrays.asList(new String[] { "Name", "Type", "Description" }));
+
+	  Vector<Vector<String>> vProcessorsData = new Vector<Vector<String>>();
+	  ArrayList<HashMap<String, String>> processors = w.getComponents().get("processors");
+	  if (processors != null) {
+		for (HashMap<String, String> curProcessor : processors) {
+		  Vector<String> vCurData = new Vector<String>();
+		  vCurData.add(curProcessor.get("name"));
+		  vCurData.add(curProcessor.get("type"));
+		  vCurData.add(curProcessor.get("description"));
+
+		  vProcessorsData.add(vCurData);
+		}
+	  }
+
+	  JTable jtProcessors = new JTable(vProcessorsData, vColumnNames);
+	  jtProcessors.getTableHeader().setFont(jtProcessors.getTableHeader().getFont().deriveFont(Font.BOLD));
+	  JPanel jpProcessors = new JPanel();
+	  jpProcessors.setLayout(new BorderLayout());
+	  jpProcessors.add(jtProcessors.getTableHeader(), BorderLayout.NORTH);
+	  jpProcessors.add(jtProcessors, BorderLayout.CENTER);
+
+	  JPanel jpProcessorsWithTitle = new JPanel();
+	  jpProcessorsWithTitle.setBorder(BorderFactory.createEtchedBorder());
+	  jpProcessorsWithTitle.setLayout(new BorderLayout());
+	  jpProcessorsWithTitle.add(new ShadedLabel("Services ("
+		  + vProcessorsData.size() + ")", ShadedLabel.BLUE, true), BorderLayout.NORTH);
+	  if (vProcessorsData.size() > 0) {
+		jpProcessorsWithTitle.add(jpProcessors, BorderLayout.CENTER);
+	  }
+
+	  // ** links **
+	  vColumnNames.clear();
+	  vColumnNames.addAll(Arrays.asList(new String[] { "Source", "Sink" }));
+
+	  Vector<Vector<String>> vLinksData = new Vector<Vector<String>>();
+	  ArrayList<HashMap<String, String>> links = w.getComponents().get("links");
+	  if (links != null) {
+		for (HashMap<String, String> curLink : links) {
+		  Vector<String> vCurData = new Vector<String>();
+		  vCurData.add(curLink.get("source"));
+		  vCurData.add(curLink.get("sink"));
+
+		  vLinksData.add(vCurData);
+		}
+	  }
+
+	  JTable jtLinks = new JTable(vLinksData, vColumnNames);
+	  jtLinks.getColumnModel().getColumn(0).setPreferredWidth(100);
+	  jtLinks.getTableHeader().setFont(jtLinks.getTableHeader().getFont().deriveFont(Font.BOLD));
+	  JPanel jpLinks = new JPanel();
+	  jpLinks.setLayout(new BorderLayout());
+	  jpLinks.add(jtLinks.getTableHeader(), BorderLayout.NORTH);
+	  jpLinks.add(jtLinks, BorderLayout.CENTER);
+
+	  JPanel jpLinksWithTitle = new JPanel();
+	  jpLinksWithTitle.setBorder(BorderFactory.createEtchedBorder());
+	  jpLinksWithTitle.setLayout(new BorderLayout());
+	  jpLinksWithTitle.add(new ShadedLabel("Links (" + vLinksData.size() + ")", ShadedLabel.BLUE, true), BorderLayout.NORTH);
+	  if (vLinksData.size() > 0) {
+		jpLinksWithTitle.add(jpLinks, BorderLayout.CENTER);
+	  }
+
+	  // ** outputs **
+	  vColumnNames.clear();
+	  vColumnNames.addAll(Arrays.asList(new String[] { "Name", "Description" }));
+
+	  Vector<Vector<String>> vOutputsData = new Vector<Vector<String>>();
+	  ArrayList<HashMap<String, String>> outputs = w.getComponents().get("outputs");
+	  if (outputs != null) {
+		for (HashMap<String, String> curOutput : outputs) {
+		  Vector<String> vCurData = new Vector<String>();
+		  vCurData.add(curOutput.get("name"));
+		  vCurData.add(curOutput.get("description"));
+
+		  vOutputsData.add(vCurData);
+		}
+	  }
+
+	  JTable jtOutputs = new JTable(vOutputsData, vColumnNames);
+	  jtOutputs.getColumnModel().getColumn(0).setPreferredWidth(100);
+	  jtOutputs.getTableHeader().setFont(jtOutputs.getTableHeader().getFont().deriveFont(Font.BOLD));
+	  JPanel jpOutputs = new JPanel();
+	  jpOutputs.setLayout(new BorderLayout());
+	  jpOutputs.add(jtOutputs.getTableHeader(), BorderLayout.NORTH);
+	  jpOutputs.add(jtOutputs, BorderLayout.CENTER);
+
+	  JPanel jpOutputsWithTitle = new JPanel();
+	  jpOutputsWithTitle.setBorder(BorderFactory.createEtchedBorder());
+	  jpOutputsWithTitle.setLayout(new BorderLayout());
+	  jpOutputsWithTitle.add(new ShadedLabel("Workflow output ports ("
+		  + vOutputsData.size() + ")", ShadedLabel.BLUE, true), BorderLayout.NORTH);
+	  if (vOutputsData.size() > 0) {
+		jpOutputsWithTitle.add(jpOutputs, BorderLayout.CENTER);
+	  }
+
+	  // PUT EVERYTHING TOGETHER
+	  jpWorkflowComponentsTabContent.setLayout(new GridBagLayout());
+	  GridBagConstraints c = new GridBagConstraints();
+
+	  c.gridx = 0;
+	  c.gridy = GridBagConstraints.RELATIVE;
+	  c.weightx = 1.0;
+	  c.fill = GridBagConstraints.HORIZONTAL;
+	  c.anchor = GridBagConstraints.NORTH;
+	  jpWorkflowComponentsTabContent.add(jpInputsWithTitle, c);
+
+	  c.insets = new Insets(10, 0, 0, 0);
+	  jpWorkflowComponentsTabContent.add(jpProcessorsWithTitle, c);
+
+	  jpWorkflowComponentsTabContent.add(jpLinksWithTitle, c);
+
+	  c.weighty = 1.0; // ONLY FOR THE LAST ELEMENT
+	  jpWorkflowComponentsTabContent.add(jpOutputsWithTitle, c);
+	}
+
+	return (wrapPreviewTabContentIntoScrollPane(jpWorkflowComponentsTabContent));
+  }
+
+  private JScrollPane createPackItemPreviewTab(Pack p) {
+	JPanel jpPackItemsTabContent = createStandardTabContentPanel();
+	GridBagConstraints c = new GridBagConstraints();
+	jpPackItemsTabContent.setLayout(new GridBagLayout());
+	c.anchor = GridBagConstraints.NORTHWEST;
+
+	// iterate through all internal and external items and add all to the panel
+	if (p.getItems().size() > 0) {
+	  int iCnt = 0;
+	  boolean bNoCommentForPrevItem = false;
+
+	  for (PackItem piCurItem : p.getItems()) {
+		c.gridx = 0;
+		c.gridy = 3 * iCnt;
+		c.weightx = 1.0;
+		c.insets = (bNoCommentForPrevItem ? new Insets(7, 0, 0, 0) : new Insets(0, 0, 0, 0));
+		c.fill = GridBagConstraints.NONE;
+		// item data is stored differently whether the item is internal or
+		// external
+		if (piCurItem.isInternalItem()) {
+		  jpPackItemsTabContent.add(new JClickableLabel(piCurItem.getItem().getTitle(), "preview:"
+			  + piCurItem.getItem().getItemType()
+			  + ":"
+			  + piCurItem.getItem().getURI(), pluginMainComponent.getPreviewBrowser(), new ImageIcon(MyExperimentPerspective.getLocalIconURL(piCurItem.getItem().getItemType()))), c);
+		} else {
+		  jpPackItemsTabContent.add(new JClickableLabel(piCurItem.getTitle(), piCurItem.getLink(), // link should open up directly in the web
+		  // browser
+		  pluginMainComponent.getPreviewBrowser(), new ImageIcon(MyExperimentPerspective.getLocalIconURL(piCurItem.getItemType())), SwingConstants.LEFT, piCurItem.getTitle()
+			  + " (link: " + piCurItem.getLink() + ")"), c);
+		}
+
+		// prepare comment before populating the metadata
+		String strComment = Util.stripAllHTML(piCurItem.getComment());
+		bNoCommentForPrevItem = (strComment == null || strComment.length() == 0);
+
+		// add metadata to the item ..
+		// .. who and when added the item ..
+		JPanel jpWhoAddedTheItem = new JPanel();
+		jpWhoAddedTheItem.setLayout(new GridBagLayout());
+		GridBagConstraints c1 = new GridBagConstraints();
+		c1.anchor = GridBagConstraints.NORTHWEST;
+		jpWhoAddedTheItem.setBorder(BorderFactory.createEmptyBorder());
+		jpWhoAddedTheItem.add(new JLabel("Added by "), c1);
+		jpWhoAddedTheItem.add(new JClickableLabel(piCurItem.getUserWhoAddedTheItem().getName(), "preview:"
+			+ Resource.USER + ":" + piCurItem.getUserWhoAddedTheItem().getURI(), pluginMainComponent.getPreviewBrowser()), c1);
+
+		String strAddedOnDate = MyExperimentClient.formatDate(piCurItem.getCreatedAt());
+		c1.weightx = 1.0;
+		jpWhoAddedTheItem.add(new JLabel(" [" + strAddedOnDate + "]"), c1);
+
+		c.gridx = 0;
+		c.gridy = 3 * iCnt + 1;
+		c.insets = new Insets(0, 25, 0, 0);
+		c.weightx = 1.0;
+		if (bNoCommentForPrevItem && (iCnt + 1 == p.getItems().size()))
+		  c.weighty = 1.0;
+		jpPackItemsTabContent.add(jpWhoAddedTheItem, c);
+
+		// .. and the comment
+		if (!bNoCommentForPrevItem) {
+		  c.gridx = 0;
+		  c.gridy = 3 * iCnt + 2;
+		  c.fill = GridBagConstraints.HORIZONTAL;
+		  c.insets = new Insets(0, 25, 7, 25);
+		  c.weightx = 1.0;
+		  if (iCnt + 1 == p.getItems().size())
+			c.weighty = 1.0; // only if this is the comment for the last item,
+		  // shift all items to the top of the panel
+
+		  DialogTextArea taCommentText = new DialogTextArea("Comment: "
+			  + strComment);
+		  taCommentText.setOpaque(false);
+		  taCommentText.setEditable(false);
+		  taCommentText.setLineWrap(true);
+		  taCommentText.setWrapStyleWord(true);
+		  jpPackItemsTabContent.add(taCommentText, c);
+		}
+
+		// update the item counter
+		iCnt++;
+	  }
+	} else {
+	  c.weighty = 1.0;
+	  c.weightx = 1.0;
+	  jpPackItemsTabContent.add(Util.generateNoneTextLabel("None"), c);
+	}
+
+	return (wrapPreviewTabContentIntoScrollPane(jpPackItemsTabContent));
+  }
+
+  private JScrollPane createTagPreviewTab(List<Tag> lTags) {
+	TagCloudPanel jpTagTabContent = new TagCloudPanel("Resource tag cloud", TagCloudPanel.TAGCLOUD_TYPE_RESOURCE_PREVIEW, pluginMainComponent.getPreviewBrowser(), pluginMainComponent, myExperimentClient, logger);
+	jpTagTabContent.getTagCloudData().clear();
+	jpTagTabContent.getTagCloudData().addAll(lTags);
+	jpTagTabContent.refresh();
+
+	// tag cloud panel itself already has a scroll pane in it; hence the outer
+	// scroll pane
+	// is only used for consistency across the user interface (contents of all
+	// tabs in the
+	// preview window are scroll panes); the preferred size of the tag cloud
+	// panel should
+	// still be adjusted accordingly to the preferred size of the outer scroll
+	// pane
+	JScrollPane spTagTabContent = wrapPreviewTabContentIntoScrollPane(jpTagTabContent);
+	spTagTabContent.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+	spTagTabContent.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
+
+	jpTagTabContent.setPreferredSize(spTagTabContent.getPreferredSize());
+
+	return (spTagTabContent);
+  }
+
+  private JScrollPane createCommentsPreviewTab(List<Comment> comments) {
+	final List<Comment> lComments = comments;
+	final JPanel jpCommentsTabContent = createStandardTabContentPanel();
+
+	if (lComments.size() > 0) {
+	  final GridBagConstraints c = new GridBagConstraints();
+	  jpCommentsTabContent.setLayout(new GridBagLayout());
+	  c.anchor = GridBagConstraints.NORTHWEST;
+
+	  // a placeholder for comments while they are loading
+	  JLabel lLoading = new JLabel("Loading comments...", new ImageIcon(MyExperimentPerspective.getLocalResourceURL("spinner")), SwingConstants.LEFT);
+	  lLoading.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 10));
+	  c.weightx = 1.0;
+	  c.weighty = 1.0;
+	  jpCommentsTabContent.add(lLoading, c);
+
+	  new Thread("Load comments for preview") {
+		@Override
+		public void run() {
+		  myExperimentClient.updateCommentListWithExtraData(lComments);
+
+		  SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+			  // remove 'loading...' placeholder
+			  jpCommentsTabContent.removeAll();
+
+			  int iCnt = 0;
+			  for (Comment comment : lComments) {
+				c.gridx = 0;
+				c.gridy = 2 * iCnt;
+				c.weightx = 0;
+				c.weighty = 0;
+				c.gridwidth = 1;
+				JClickableLabel lCommentAuthor = new JClickableLabel(comment.getUser().getName(), "preview:"
+					+ comment.getUser().getItemType()
+					+ ":"
+					+ comment.getUser().getURI(), pluginMainComponent.getPreviewBrowser(), iconUser);
+				jpCommentsTabContent.add(lCommentAuthor, c);
+
+				c.gridx = 1;
+				c.gridy = 2 * iCnt;
+				c.weightx = 1.0;
+				String strCommentDate = MyExperimentClient.formatDate(comment.getCreatedAt());
+				JLabel lCommentDate = new JLabel(" - [" + strCommentDate + "]");
+				lCommentDate.setBorder(lCommentAuthor.getBorder());
+				jpCommentsTabContent.add(lCommentDate, c);
+
+				c.gridx = 0;
+				c.gridy = 2 * iCnt + 1;
+				c.weightx = 1.0;
+				c.gridwidth = 2;
+				c.fill = GridBagConstraints.HORIZONTAL;
+				if (iCnt + 1 == lComments.size())
+				  c.weighty = 1.0;
+
+				DialogTextArea taCommentText = new DialogTextArea(Util.stripAllHTML(comment.getComment()));
+				taCommentText.setBorder(BorderFactory.createEmptyBorder(0, 25, 10, 25));
+				taCommentText.setOpaque(false);
+				taCommentText.setEditable(false);
+				taCommentText.setLineWrap(true);
+				taCommentText.setWrapStyleWord(true);
+				jpCommentsTabContent.add(taCommentText, c);
+
+				iCnt++;
+			  }
+
+			  jpCommentsTabContent.validate();
+			  jpCommentsTabContent.repaint();
+
+			  // this will ensure that even if there are many comments,
+			  // the comment panel is still shown starting at the top comment
+			  SwingUtilities.invokeLater(new Runnable() {
+				public void run() {
+				  jpCommentsTabContent.scrollRectToVisible(new Rectangle());
+				}
+			  });
+			}
+		  });
+		}
+	  }.start();
+	} else {
+	  jpCommentsTabContent.add(Util.generateNoneTextLabel("None"));
+	}
+
+	JScrollPane spCommentsTabContent = wrapPreviewTabContentIntoScrollPane(jpCommentsTabContent);
+	spCommentsTabContent.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+	return (spCommentsTabContent);
+  }
+
+  private JScrollPane createCreditsPreviewTab(List<Resource> lCreditedUsersOrGroups) {
+	JPanel jpCreditsTabContent = createStandardTabContentPanel();
+
+	if (lCreditedUsersOrGroups.size() > 0) {
+	  for (Resource r : lCreditedUsersOrGroups) {
+		jpCreditsTabContent.add(new JClickableLabel(r.getTitle(), "preview:"
+			+ r.getItemType() + ":" + r.getURI(), pluginMainComponent.getPreviewBrowser(), new ImageIcon(MyExperimentPerspective.getLocalIconURL(r.getItemType()))));
+	  }
+	} else {
+	  jpCreditsTabContent.add(Util.generateNoneTextLabel("None"));
+	}
+
+	return (wrapPreviewTabContentIntoScrollPane(jpCreditsTabContent));
+  }
+
+  private JScrollPane createAttributionsPreviewTab(List<Resource> lAttributions) {
+	JPanel jpAttributionsTabContent = createStandardTabContentPanel();
+
+	if (lAttributions.size() > 0) {
+	  for (Resource r : lAttributions) {
+		jpAttributionsTabContent.add(new JClickableLabel(r.getTitle(), "preview:"
+			+ r.getItemType() + ":" + r.getURI(), pluginMainComponent.getPreviewBrowser(), new ImageIcon(MyExperimentPerspective.getLocalIconURL(r.getItemType()))));
+	  }
+	} else {
+	  jpAttributionsTabContent.add(Util.generateNoneTextLabel("None"));
+	}
+
+	return (wrapPreviewTabContentIntoScrollPane(jpAttributionsTabContent));
+  }
+
+  /**
+   * A standard starting point for all preview window tabs.
+   */
+  private JPanel createStandardTabContentPanel() {
+	JPanel jpTabContentPanel = new JPanel();
+	jpTabContentPanel.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
+	jpTabContentPanel.setLayout(new BoxLayout(jpTabContentPanel, BoxLayout.Y_AXIS));
+
+	return (jpTabContentPanel);
+  }
+
+  private JScrollPane wrapPreviewTabContentIntoScrollPane(JPanel jpTabContentPanel) {
+	// WRAPS TAB CONTENT into it's own SCROLL PANE ..
+	Dimension dPreferredTabSize = new Dimension(ResourcePreviewBrowser.PREFERRED_WIDTH - 50, PREFERRED_LOWER_TABBED_PANE_HEIGHT);
+
+	JScrollPane spTabContent = new JScrollPane(jpTabContentPanel);
+	spTabContent.setBorder(BorderFactory.createEmptyBorder());
+	spTabContent.setPreferredSize(dPreferredTabSize);
+	spTabContent.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+	return (spTabContent);
+  }
+
+  private JPanel wrapTextPaneAndTabbedViewIntoFullPreview(JTextPane tpHTMLPreview, JTabbedPane tpTabbedView) {
+	// WRAPS HTML JTextPane PREVIEW AND A JTabbedPane WITH DETAILS INTO A SINGLE
+	// PREVIEW PANEL
+	JPanel jpFullPreview = new JPanel();
+	jpFullPreview.setBackground(Color.WHITE); // white background for the whole
+	// preview panel
+	jpFullPreview.setLayout(new GridBagLayout());
+	GridBagConstraints c = new GridBagConstraints();
+
+	c.gridx = GridBagConstraints.REMAINDER;
+	c.gridy = 0;
+	c.weighty = 0; // will not change size when the window is resized
+	jpFullPreview.add(tpHTMLPreview, c);
+
+	c.gridx = GridBagConstraints.REMAINDER;
+	c.gridy = 1;
+	c.weighty = 1; // will grow in size when the window is resized..
+	c.fill = GridBagConstraints.VERTICAL; // ..and fill all available space
+	// vertically
+	c.insets = new Insets(20, 0, 5, 0); // a bit of margin at the top & bottom
+	jpFullPreview.add(tpTabbedView, c);
+
+	return (jpFullPreview);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchOptionsPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchOptionsPanel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchOptionsPanel.java
new file mode 100644
index 0000000..b2fd9f5
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchOptionsPanel.java
@@ -0,0 +1,317 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.JTextField;
+import javax.swing.SpinnerNumberModel;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs, Emmanuel Tagarira
+ */
+public class SearchOptionsPanel extends JPanel implements ItemListener, KeyListener {
+  // CONSTANTS
+  protected static final int SEARCH_RESULT_LIMIT_MIN = 1;
+  protected static final int SEARCH_RESULT_LIMIT_INIT = 20;
+  protected static final int SEARCH_RESULT_LIMIT_MAX = 100;
+
+  private final MainComponent pluginMainComponent;
+  private final MyExperimentClient myExperimentClient;
+  private final Logger logger;
+  private final ActionListener clickHandler;
+
+  // COMPONENTS
+  protected JButton bSearch;
+  private JTextField tfSearchQuery;
+  private JCheckBox cbSearchAllTypes;
+  private JCheckBox cbWorkflows;
+  private JCheckBox cbFiles;
+  private JCheckBox cbPacks;
+  private JCheckBox cbUsers;
+  private JCheckBox cbGroups;
+  protected JSpinner jsResultLimit;
+  protected JTextField tfResultLimitTextField;
+
+  // Data
+  ArrayList<JCheckBox> alDataTypeCheckboxes;
+
+  public SearchOptionsPanel(ActionListener actionListener, MainComponent component, MyExperimentClient client, Logger logger) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+	this.clickHandler = actionListener;
+
+	this.initialiseUI();
+
+	// this will hold the collection of all checkboxes that correspond to data types (will be used in item event handling)
+	alDataTypeCheckboxes = new ArrayList<JCheckBox>(Arrays.asList(new JCheckBox[] { cbWorkflows, cbFiles, cbPacks, cbUsers, cbGroups }));
+  }
+
+  private void initialiseUI() {
+	this.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " Search Settings "), BorderFactory.createEmptyBorder(5, 5, 5, 5)));
+
+	this.setLayout(new GridBagLayout());
+	GridBagConstraints c = new GridBagConstraints();
+
+	c.gridx = 0;
+	c.gridy = 0;
+	c.anchor = GridBagConstraints.WEST;
+	this.add(new JLabel("Query"), c);
+
+	c.gridx = 0;
+	c.gridy = 1;
+	c.gridwidth = 4;
+	c.fill = GridBagConstraints.HORIZONTAL;
+	c.weightx = 1.0;
+	tfSearchQuery = new JTextField();
+	tfSearchQuery.setToolTipText("<html>Tips for creating search queries:<br>1) Use wildcards to make more "
+		+ "flexible queries. Asterisk (<b>*</b>) matches any zero or more<br>&nbsp;&nbsp;&nbsp;&nbsp;characters (e.g. "
+		+ "<b><i>Seq*</i></b> would match <b><i>Sequence</i></b>), question mark (<b>?</b>) matches any single<br>"
+		+ "&nbsp;&nbsp;&nbsp;&nbsp;character (e.g. <b><i>Tave?na</i></b> would match <b><i>Taverna</i></b>).<br>"
+		+ "2) Enclose the <b><i>\"search query\"</i></b> in double quotes to make exact phrase matching, otherwise<br>"
+		+ "&nbsp;&nbsp;&nbsp;&nbsp;items that contain any (or all) words in the <b><i>search query</i></b> will be found.</html>");
+	tfSearchQuery.addKeyListener(this);
+	this.add(tfSearchQuery, c);
+
+	c.gridx = 4;
+	c.gridy = 1;
+	c.gridwidth = 1;
+	c.fill = GridBagConstraints.NONE;
+	c.weightx = 0;
+	c.insets = new Insets(0, 5, 0, 0);
+	bSearch = new JButton("Search", WorkbenchIcons.searchIcon);
+	bSearch.addActionListener(this.clickHandler);
+	bSearch.addKeyListener(this);
+	this.add(bSearch, c);
+
+	c.gridx = 0;
+	c.gridy = 2;
+	c.insets = new Insets(10, 0, 3, 0);
+	this.add(new JLabel("Search for..."), c);
+
+	c.gridx = 0;
+	c.gridy = 3;
+	c.gridwidth = 2;
+	c.insets = new Insets(0, 0, 0, 0);
+	cbSearchAllTypes = new JCheckBox("all resource types", true);
+	cbSearchAllTypes.addItemListener(this);
+	cbSearchAllTypes.addKeyListener(this);
+	this.add(cbSearchAllTypes, c);
+
+	c.gridx = 0;
+	c.gridy = 4;
+	c.gridwidth = 1;
+	c.ipady = 0;
+	cbWorkflows = new JCheckBox("workflows", true);
+	cbWorkflows.addItemListener(this);
+	cbWorkflows.addKeyListener(this);
+	this.add(cbWorkflows, c);
+
+	c.gridx = 0;
+	c.gridy = 5;
+	cbFiles = new JCheckBox("files", true);
+	cbFiles.addItemListener(this);
+	cbFiles.addKeyListener(this);
+	this.add(cbFiles, c);
+
+	c.gridx = 0;
+	c.gridy = 6;
+	cbPacks = new JCheckBox("packs", true);
+	cbPacks.addItemListener(this);
+	cbPacks.addKeyListener(this);
+	this.add(cbPacks, c);
+
+	c.gridx = 1;
+	c.gridy = 4;
+	cbUsers = new JCheckBox("users", true);
+	cbUsers.addItemListener(this);
+	cbUsers.addKeyListener(this);
+	this.add(cbUsers, c);
+
+	c.gridx = 1;
+	c.gridy = 5;
+	cbGroups = new JCheckBox("groups", true);
+	cbGroups.addItemListener(this);
+	cbGroups.addKeyListener(this);
+	this.add(cbGroups, c);
+
+	c.gridx = 3;
+	c.gridy = 2;
+	c.insets = new Insets(10, 25, 3, 0);
+	JLabel jlResultLimit = new JLabel("Result limit");
+	this.add(jlResultLimit, c);
+
+	c.gridx = 3;
+	c.gridy = 3;
+	c.insets = new Insets(0, 25, 0, 0);
+	jsResultLimit = new JSpinner(new SpinnerNumberModel(SEARCH_RESULT_LIMIT_INIT, SEARCH_RESULT_LIMIT_MIN, SEARCH_RESULT_LIMIT_MAX, 1));
+	jsResultLimit.setPreferredSize(new Dimension(jlResultLimit.getPreferredSize().width, jsResultLimit.getPreferredSize().height));
+	this.add(jsResultLimit, c);
+
+	// adding KeyListener to JSpinner directly doesn't make sense; need to attach KeyListener to the text field that is associated with spinner
+	tfResultLimitTextField = ((JSpinner.DefaultEditor) this.jsResultLimit.getEditor()).getTextField();
+	tfResultLimitTextField.addKeyListener(this);
+  }
+
+  public String getSearchQuery() {
+	return (this.tfSearchQuery.getText());
+  }
+
+  public void setSearchQuery(String strSearchQuery) {
+	this.tfSearchQuery.setText(strSearchQuery);
+  }
+
+  public void focusSearchQueryField() {
+	this.tfSearchQuery.selectAll();
+	this.tfSearchQuery.requestFocusInWindow();
+  }
+
+  public void setSearchAllResourceTypes(boolean bSearchAllTypes) {
+	this.cbSearchAllTypes.setSelected(bSearchAllTypes);
+  }
+
+  public boolean getSearchWorkflows() {
+	return (this.cbWorkflows.isSelected());
+  }
+
+  public void setSearchWorkflows(boolean bSearchWorkflows) {
+	this.cbWorkflows.setSelected(bSearchWorkflows);
+  }
+
+  public boolean getSearchFiles() {
+	return (this.cbFiles.isSelected());
+  }
+
+  public void setSearchFiles(boolean bSearchFiles) {
+	this.cbFiles.setSelected(bSearchFiles);
+  }
+
+  public boolean getSearchPacks() {
+	return (this.cbPacks.isSelected());
+  }
+
+  public void setSearchPacks(boolean bSearchPacks) {
+	this.cbPacks.setSelected(bSearchPacks);
+  }
+
+  public boolean getSearchUsers() {
+	return (this.cbUsers.isSelected());
+  }
+
+  public void setSearchUsers(boolean bSearchUsers) {
+	this.cbUsers.setSelected(bSearchUsers);
+  }
+
+  public boolean getSearchGroups() {
+	return (this.cbGroups.isSelected());
+  }
+
+  public void setSearchGroups(boolean bSearchGroups) {
+	this.cbGroups.setSelected(bSearchGroups);
+  }
+
+  public int getResultCountLimit() {
+	// JSpinner handles value validation itself, so there is no need to
+	// make our own validation too
+	return (Integer.parseInt(this.jsResultLimit.getValue().toString()));
+  }
+
+  public void setResultCountLimit(int iLimit) {
+	this.jsResultLimit.setValue(iLimit);
+  }
+
+  // this monitors all checkbox clicks and selects / deselects other checkboxes which are relevant
+  public void itemStateChanged(ItemEvent e) {
+	if (e.getItemSelectable().equals(this.cbSearchAllTypes)) {
+	  // "all resource types" clicked - need to select / deselect all data type checkboxes accordingly
+	  for (JCheckBox cb : this.alDataTypeCheckboxes) {
+		cb.removeItemListener(this);
+		cb.setSelected(this.cbSearchAllTypes.isSelected());
+		cb.addItemListener(this);
+	  }
+
+	  // also, enable / disable the search button
+	  this.bSearch.setEnabled(this.cbSearchAllTypes.isSelected());
+	} else if (this.alDataTypeCheckboxes.contains(e.getItemSelectable())) {
+	  // one of the checkboxes for data types was clicked (e.g. workflows, files, etc);
+	  // need to calculate how many of those are currently selected
+	  int iSelectedCnt = 0;
+	  for (JCheckBox cb : this.alDataTypeCheckboxes) {
+		if (cb.isSelected())
+		  iSelectedCnt++;
+	  }
+
+	  // if all are selected, tick "search all types" checkbox; uncheck otherwise
+	  this.cbSearchAllTypes.removeItemListener(this);
+	  this.cbSearchAllTypes.setSelected(iSelectedCnt == this.alDataTypeCheckboxes.size());
+	  this.cbSearchAllTypes.addItemListener(this);
+
+	  // enable search button if at least one data type is selected; disable otherwise
+	  this.bSearch.setEnabled(iSelectedCnt > 0);
+	}
+  }
+
+  public void keyPressed(KeyEvent e) {
+	// ENTER pressed - start search by simulating "search" button click
+	// (only do this if the "search" button was active at that moment)
+	if (e.getKeyCode() == KeyEvent.VK_ENTER
+		&& this.bSearch.isEnabled()
+		&& (Arrays.asList(new JComponent[] { this.tfSearchQuery, this.bSearch, this.cbSearchAllTypes, this.tfResultLimitTextField }).contains(e.getSource()) || this.alDataTypeCheckboxes.contains(e.getSource()))) {
+	  this.clickHandler.actionPerformed(new ActionEvent(this.bSearch, 0, ""));
+	}
+  }
+
+  public void keyReleased(KeyEvent e) {
+	// do nothing
+  }
+
+  public void keyTyped(KeyEvent e) {
+	// do nothing
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchResultsPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchResultsPanel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchResultsPanel.java
new file mode 100644
index 0000000..dbc3b36
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/SearchResultsPanel.java
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class SearchResultsPanel extends JPanel
+{
+  // CONSTANTS
+  public static final String NO_SEARCHES_STATUS = "No searches have been done so far";
+  
+  
+  private MainComponent pluginMainComponent;
+  private MyExperimentClient myExperimentClient;
+  private Logger logger;
+  
+  // COMPONENTS
+  private JLabel lStatusLabel;
+  public JButton bRefresh;
+  public JButton bClear;
+  private JPanel jpResultsBody;
+  private ActionListener alClickHandler;
+  
+  // result data store
+  boolean bNoSearchesMadeYet;
+  String strCurrentSearchTerm;
+  HashMap<Integer, ArrayList<Resource>> hmSearchResults;
+  
+  
+  public SearchResultsPanel(ActionListener buttonClickHandler, MainComponent component, MyExperimentClient client, Logger logger)
+  {
+    super();
+    
+    // set main variables to ensure access to myExperiment, logger and the parent component
+    this.pluginMainComponent = component;
+    this.myExperimentClient = client;
+    this.logger = logger;
+    this.alClickHandler = buttonClickHandler;
+    
+    // initialise the result data collection to an empty one, just in case
+    // someone calls refresh() before setting the real result data
+    bNoSearchesMadeYet = true;
+    hmSearchResults = new HashMap<Integer, ArrayList<Resource>>();
+    
+    this.initialiseUI();
+  }
+
+  
+  
+  private void initialiseUI()
+  {
+    // label to hold the status of search (e.g. result query + count, etc)
+    this.lStatusLabel = new JLabel(NO_SEARCHES_STATUS);
+    this.lStatusLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); // a bit of padding on the left
+    
+    // control buttons for the search results panel
+    JPanel jpButtonsPanel = new JPanel();
+    jpButtonsPanel.setLayout(new BoxLayout(jpButtonsPanel, BoxLayout.LINE_AXIS));
+    this.bClear = new JButton("Clear", WorkbenchIcons.deleteIcon);
+    this.bClear.addActionListener(this.alClickHandler);
+    this.bClear.setToolTipText("Click this button to clear the search results");
+    this.bClear.setEnabled(false);
+    jpButtonsPanel.add(this.bClear);
+    this.bRefresh = new JButton("Refresh", WorkbenchIcons.refreshIcon);
+    this.bRefresh.addActionListener(this.alClickHandler);
+    this.bRefresh.setToolTipText("Click this button to refresh the search results");
+    this.bRefresh.setEnabled(false);
+    jpButtonsPanel.add(this.bRefresh);
+    
+    // status panel containing the status label and control buttons
+    JPanel jpStatusPanel = new JPanel(new BorderLayout());
+    jpStatusPanel.setBorder(BorderFactory.createEtchedBorder());
+    jpStatusPanel.add(this.lStatusLabel, BorderLayout.CENTER);
+    jpStatusPanel.add(jpButtonsPanel, BorderLayout.EAST);
+    
+    // create empty panel for the main search result content
+    this.jpResultsBody = new JPanel();
+    jpResultsBody.setLayout(new BorderLayout());
+    
+    // PUT EVERYTHING TOGETHER
+    this.setLayout(new BorderLayout());
+    this.add(jpStatusPanel, BorderLayout.NORTH);
+    this.add(this.jpResultsBody, BorderLayout.CENTER);
+    
+    // tabbed results view is missing from this method - this is
+    // because if no results will be found in a particular category,
+    // that tab will not be displayed (hence the results view will
+    // need to be generated every time dynamically)
+  }
+  
+  
+  public void setCurrentSearchTerm(String strSearchTerm)
+  {
+    this.strCurrentSearchTerm = strSearchTerm;
+  }
+  
+  public String getCurrentSearchTerm()
+  {
+    return (this.strCurrentSearchTerm);
+  }
+  
+  
+  public void setSearchResultsData(HashMap<Integer, ArrayList<Resource>> hmResults)
+  {
+    this.bNoSearchesMadeYet = false;
+    this.hmSearchResults = hmResults;
+  }
+  
+  
+  public void setStatus(String status)
+  {
+    this.lStatusLabel.setText(status);
+  }
+  
+  public void refresh()
+  {
+    // remove all items from the main results content panel
+    this.jpResultsBody.removeAll();
+    
+    if (!this.bNoSearchesMadeYet) // this will be true if the result collection was never assigned to this result panel
+    {
+      ArrayList<Integer> alResourceTypes = new ArrayList<Integer>(this.hmSearchResults.keySet());
+      Collections.sort(alResourceTypes);  // this will ensure that the tabs are always in the correct order
+      if(alResourceTypes.isEmpty()) {
+        this.jpResultsBody.add(new JLabel("No items to display"), BorderLayout.NORTH);
+        this.repaint();
+      }
+      else {
+        JTabbedPane tpResults = new JTabbedPane();
+        
+        for(int iType : alResourceTypes)
+        {
+          // HACK: for now all item types are suitably turned into plural by simply appending "s"
+          String strTabLabel = Resource.getResourceTypeName(iType) + "s (" + this.hmSearchResults.get(iType).size() + ")";
+          
+          ResourceListPanel jpTabContents = new ResourceListPanel(pluginMainComponent, myExperimentClient, logger);
+          jpTabContents.setListItems(this.hmSearchResults.get(iType));
+          
+          JScrollPane spTabContents = new JScrollPane(jpTabContents);
+          spTabContents.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+          
+          tpResults.add(strTabLabel, spTabContents);
+        }
+        
+        this.jpResultsBody.add(tpResults, BorderLayout.CENTER);
+        this.bClear.setEnabled(true);
+        this.bRefresh.setEnabled(true);
+      }
+    }
+    
+    this.revalidate();
+  }
+  
+  
+  public void clear()
+  {
+    // remove all items from the main results content panel
+    this.jpResultsBody.removeAll();
+    this.repaint();
+    this.revalidate();
+  }
+  
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/ResourceManager.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/ResourceManager.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/ResourceManager.java
new file mode 100644
index 0000000..666cd6c
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/ResourceManager.java
@@ -0,0 +1,326 @@
+package net.sf.taverna.biocatalogue.model;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.image.BufferedImage;
+import java.net.URL;
+import java.util.HashMap;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+
+import net.sf.taverna.t2.activities.rest.ui.servicedescription.RESTActivityIcon;
+import net.sf.taverna.t2.activities.wsdl.servicedescriptions.WSDLActivityIcon;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.BioCataloguePerspective;
+
+/**
+ * This class will be a single point of lookup of all resource files.
+ * (Icons, images, CSS files, etc).
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class ResourceManager
+{
+  // subfolders, where some icons / other resources are kept
+  public static final String FAMFAMFAM_PATH = "famfamfam_silk/";    // free collection of icons
+  public static final String SERVICE_ICONS_PATH = "service_icons/"; // icons related to web services (e.g. service types)
+  public static final String FOLDS_PATH = "folds/";                 // icons for 'folding' menus (like 'Search for...')
+  public static final String TRISTATE_TREE_ICONS_PATH = "tristate_checkbox/";  // icons for the tri-state filtering tree
+  
+  // all known resources to follow
+  public static final int FAVICON = 1;
+  
+  public static final int INFORMATION_ICON_LARGE = 10;
+  
+  public static final int SPINNER_STILL = 20;
+  public static final int SPINNER = 21;
+  public static final int BAR_LOADER_GREY = 25;
+  public static final int BAR_LOADER_GREY_STILL = 26;
+  public static final int BAR_LOADER_ORANGE = 30;
+  public static final int BAR_LOADER_ORANGE_STILL = 31;
+  
+  public static final int FOLD_ICON = 40;
+  public static final int UNFOLD_ICON = 41;
+  public static final int FOLD_ICON_16x16 = 42;
+  public static final int UNFOLD_ICON_16x16 = 43;
+  
+  public static final int SERVICE_TYPE_SOAP_ICON = 50;
+  public static final int SERVICE_TYPE_REST_ICON = 51;
+  public static final int SERVICE_TYPE_MULTITYPE_ICON = 65;
+  public static final int SERVICE_TYPE_UNKNOWN_ICON = 70;
+  
+  public static final int TRISTATE_CHECKBOX_CHECKED_ICON = 80;
+  public static final int TRISTATE_CHECKBOX_PARTIAL_ICON = 82;
+  public static final int TRISTATE_CHECKBOX_UNCHECKED_ICON = 85;
+  public static final int UNCHECKED_ICON = 86;
+  
+  public static final int SERVICE_STATUS_PASSED_ICON = 100;
+  public static final int SERVICE_STATUS_PASSED_ICON_LARGE = 101;
+  public static final int SERVICE_STATUS_WARNING_ICON = 110;
+  public static final int SERVICE_STATUS_WARNING_ICON_LARGE = 111;
+  public static final int SERVICE_STATUS_FAILED_ICON = 120;
+  public static final int SERVICE_STATUS_FAILED_ICON_LARGE = 121;
+  public static final int SERVICE_STATUS_UNCHECKED_ICON = 130;
+  public static final int SERVICE_STATUS_UNCHECKED_ICON_LARGE = 131;
+  public static final int SERVICE_STATUS_UNKNOWN_ICON = 140;
+  
+  public static final int UNKNOWN_RESOURCE_TYPE_ICON = 200;
+  public static final int USER_ICON = 205;
+  public static final int REGISTRY_ICON = 210;
+  public static final int SERVICE_PROVIDER_ICON = 215;
+  public static final int SERVICE_ICON = 220;
+  public static final int SOAP_OPERATION_ICON = 225;
+  public static final int REST_METHOD_ICON = 227;
+  public static final int SERVICE_CATEGORY_ICON = 230;
+  public static final int WSDL_DOCUMENT_ICON = 235;
+  public static final int TAG_ICON = 240;
+  
+  public static final int OPEN_IN_BIOCATALOGUE_ICON = 310;
+  public static final int SEARCH_ICON = 315;
+  public static final int HISTORY_ICON = 320;
+  public static final int REFRESH_ICON = 330;
+  public static final int FAVOURITE_ICON = 335;
+  public static final int TICK_ICON = 340;
+  public static final int CROSS_ICON = 341;
+  public static final int WARNING_ICON = 342;
+  public static final int ERROR_ICON = 343;
+  public static final int SAVE_ICON = 345;
+  public static final int DELETE_ITEM_ICON = 350;
+  public static final int CLEAR_ICON = 355;
+  public static final int LOCKED_ICON = 360;
+  public static final int UNLOCKED_ICON = 365;
+  
+  public static final int BACK_ICON = 370;
+  public static final int FORWARD_ICON = 375;
+  public static final int FILTER_ICON = 380;
+  public static final int PREVIEW_ICON = 385;
+  public static final int SUGGESTION_TO_USER_ICON = 390;
+  public static final int ADD_PROCESSOR_TO_WORKFLOW_ICON = 395;
+  public static final int ADD_PROCESSOR_AS_FAVOURITE_ICON = 396;
+  public static final int EXECUTE_HEALTH_CHECK_ICON = 397;
+  public static final int ADD_ALL_SERVICES_AS_FAVOURITE_ICON = 398;
+
+  public static final int SELECT_ALL_ICON = 400;
+  public static final int DESELECT_ALL_ICON = 405;
+  public static final int EXPAND_ALL_ICON = 410;
+  public static final int COLLAPSE_ALL_ICON = 420;
+  
+  public static final int SORT_BY_NAME_ICON = 450;
+  public static final int SORT_BY_COUNTS_ICON = 455;
+  
+  public static final int STYLES_CSS = 1000;
+  
+  
+  /** 
+   * Simple method to retrieve relative path of a required resource.
+   */
+  public static String getResourceRelPath(int resourceId)
+  {
+    String resPath = "";
+    
+    switch (resourceId) {
+      case FAVICON:                           resPath += "favicon.png";
+                                              break;
+      case INFORMATION_ICON_LARGE:            resPath += "info-sphere-35.png";
+                                              break;
+      case SPINNER_STILL:                     resPath += "ajax-loader-still.gif";
+                                              break;
+      case SPINNER:                           resPath += "ajax-loader.gif";
+                                              break;
+      case BAR_LOADER_GREY:                   resPath += "ajax-loader-grey-bert2.gif";
+                                              break;
+      case BAR_LOADER_GREY_STILL:             resPath += "ajax-loader-grey-bert2-still.png";
+                                              break;
+      case BAR_LOADER_ORANGE:                 resPath += "ajax-loader-orange-bert2.gif";
+                                              break;
+      case BAR_LOADER_ORANGE_STILL:           resPath += "ajax-loader-orange-bert2-still.png";
+                                              break;
+      case FOLD_ICON:                         resPath += FOLDS_PATH + "fold.png";
+                                              break;
+      case UNFOLD_ICON:                       resPath += FOLDS_PATH + "unfold.png";
+      										  break;
+      case FOLD_ICON_16x16:                   resPath += FOLDS_PATH + "fold_16x16.png";
+      										  break;
+      case UNFOLD_ICON_16x16:                 resPath += FOLDS_PATH + "unfold_16x16.png";                                              
+      										  break;
+      case SERVICE_TYPE_SOAP_ICON:            resPath += SERVICE_ICONS_PATH + "service_type_soap.png";
+                                              break;
+      case SERVICE_TYPE_REST_ICON:            resPath += SERVICE_ICONS_PATH + "service_type_rest.png";
+                                              break;
+      case SERVICE_TYPE_MULTITYPE_ICON:       resPath += SERVICE_ICONS_PATH + "service_type_multitype.png";
+                                              break;
+      case SERVICE_TYPE_UNKNOWN_ICON:         resPath += SERVICE_ICONS_PATH + "service_type_unknown.png";
+                                              break;
+      case SERVICE_STATUS_PASSED_ICON:        resPath += FAMFAMFAM_PATH + "accept.png";
+                                              break;
+      case SERVICE_STATUS_PASSED_ICON_LARGE:  resPath += "tick-sphere-35.png";
+                                              break;
+      case SERVICE_STATUS_WARNING_ICON:       resPath += FAMFAMFAM_PATH + "error.png";
+                                              break;
+      case SERVICE_STATUS_WARNING_ICON_LARGE: resPath += "pling-sphere-35.png";
+                                              break;
+      case SERVICE_STATUS_FAILED_ICON:        resPath += FAMFAMFAM_PATH + "exclamation.png";
+                                              break;
+      case SERVICE_STATUS_FAILED_ICON_LARGE:  resPath += "cross-sphere-35.png";
+                                              break;
+      case SERVICE_STATUS_UNCHECKED_ICON:     resPath += FAMFAMFAM_PATH + "help.png";
+                                              break;
+      case SERVICE_STATUS_UNCHECKED_ICON_LARGE: resPath += "query-sphere-35.png";
+                                              break;
+      case SERVICE_STATUS_UNKNOWN_ICON:       resPath += FAMFAMFAM_PATH + "grey_circle.png";
+                                              break;
+      case TRISTATE_CHECKBOX_CHECKED_ICON:    resPath += TRISTATE_TREE_ICONS_PATH + "tristate_checkbox_checked.png";
+                                              break;
+      case TRISTATE_CHECKBOX_PARTIAL_ICON:    resPath += TRISTATE_TREE_ICONS_PATH + "tristate_checkbox_partial.png";
+                                              break;
+      case TRISTATE_CHECKBOX_UNCHECKED_ICON:  resPath += TRISTATE_TREE_ICONS_PATH + "tristate_checkbox_unchecked.png";
+                                              break;
+      case UNCHECKED_ICON:  				  resPath += "unchecked.png";
+      										  break;
+      case UNKNOWN_RESOURCE_TYPE_ICON:        resPath += FAMFAMFAM_PATH + "grey_circle.png";
+                                              break;                                        
+      case USER_ICON:                         resPath += FAMFAMFAM_PATH + "user.png";
+                                              break;
+      case REGISTRY_ICON:                     resPath += FAMFAMFAM_PATH + "remote_resource.png";
+                                              break;
+      case SERVICE_PROVIDER_ICON:             resPath += FAMFAMFAM_PATH + "chart_organisation.png";
+                                              break;
+      case SERVICE_ICON:                      resPath += "favicon.png";
+                                              break;
+      case SOAP_OPERATION_ICON:               resPath += FAMFAMFAM_PATH + "plugin.png";
+                                              break;
+      case REST_METHOD_ICON:                  resPath += FAMFAMFAM_PATH + "plugin.png";
+                                              break;
+      case SERVICE_CATEGORY_ICON:             resPath += FAMFAMFAM_PATH + "text_list_numbers.png";
+                                              break;
+      case TAG_ICON:                          resPath += FAMFAMFAM_PATH + "tag_blue.png";
+                                              break;
+      case WSDL_DOCUMENT_ICON:                resPath += FAMFAMFAM_PATH + "page_white_code.png";
+                                              break;                                        
+      case OPEN_IN_BIOCATALOGUE_ICON:         resPath += FAMFAMFAM_PATH + "magnifier.png";
+                                              break;
+      case SEARCH_ICON:                       resPath += FAMFAMFAM_PATH + "magnifier.png";
+                                              break;
+      case HISTORY_ICON:                      resPath += FAMFAMFAM_PATH + "folder_explore.png";
+                                              break;
+      case REFRESH_ICON:                      resPath += FAMFAMFAM_PATH + "arrow_refresh.png";
+                                              break;
+      case FAVOURITE_ICON:                    resPath += FAMFAMFAM_PATH + "star.png";
+                                              break;
+      case TICK_ICON:                         resPath += FAMFAMFAM_PATH + "tick.png";
+                                              break;
+      case CROSS_ICON:                        resPath += FAMFAMFAM_PATH + "cross.png";
+                                              break;
+      case WARNING_ICON:                      resPath += FAMFAMFAM_PATH + "error.png";
+                                              break;
+      case ERROR_ICON:                        resPath += FAMFAMFAM_PATH + "exclamation.png";
+                                              break;
+      case SAVE_ICON:                         resPath += FAMFAMFAM_PATH + "disk.png";
+                                              break;
+      case DELETE_ITEM_ICON:                  resPath += FAMFAMFAM_PATH + "cross.png";
+                                              break;
+      case CLEAR_ICON:                        resPath += "trash.png";
+                                              break;
+      case LOCKED_ICON:                       resPath += FAMFAMFAM_PATH + "lock.png";
+                                              break;
+      case UNLOCKED_ICON:                     resPath += FAMFAMFAM_PATH + "lock_open.png";
+                                              break;
+      case BACK_ICON:                         resPath += FAMFAMFAM_PATH + "arrow_left.png";
+                                              break;
+      case FORWARD_ICON:                      resPath += FAMFAMFAM_PATH + "arrow_right.png";
+                                              break;
+      case FILTER_ICON:                       resPath += FAMFAMFAM_PATH + "arrow_join (flipped vertically).png";
+                                              break;
+      case PREVIEW_ICON:                      resPath += FAMFAMFAM_PATH + "magnifier.png";
+                                              break;
+      case SUGGESTION_TO_USER_ICON:           resPath += FAMFAMFAM_PATH + "lightbulb.png";
+                                              break;
+      case ADD_PROCESSOR_TO_WORKFLOW_ICON:    resPath += "open_in_BioCatalogue.png";
+                                              break;
+      case ADD_PROCESSOR_AS_FAVOURITE_ICON:   resPath += FAMFAMFAM_PATH + "star.png";
+                                              break;
+      case ADD_ALL_SERVICES_AS_FAVOURITE_ICON:resPath += FAMFAMFAM_PATH + "multiple_star.png";
+      										  break;                          
+      case EXECUTE_HEALTH_CHECK_ICON:         resPath += FAMFAMFAM_PATH + "information.png";
+                                              break;                                        
+      case SELECT_ALL_ICON:                   resPath += FAMFAMFAM_PATH + "tick.png";
+                                              break;
+      case DESELECT_ALL_ICON:                 resPath += FAMFAMFAM_PATH + "cross.png";
+                                              break;
+      case EXPAND_ALL_ICON:                   resPath += FAMFAMFAM_PATH + "text_linespacing.png";
+                                              break;
+      case COLLAPSE_ALL_ICON:                 resPath += FAMFAMFAM_PATH + "text_linespacing (collapse).png";
+                                              break;
+      case SORT_BY_NAME_ICON:                 resPath += FAMFAMFAM_PATH + "style.png";
+                                              break;
+      case SORT_BY_COUNTS_ICON:               resPath += FAMFAMFAM_PATH + "sum.png";
+                                              break;
+      case STYLES_CSS:                        resPath += "styles.css";
+                                              break;
+      default:                                return (null);
+    }
+    
+    return (resPath);
+  }
+  
+  
+  private static URL getResourceLocalURL(int resourceId) {
+    return (BioCataloguePerspective.class.getResource(getResourceRelPath(resourceId)));
+  }
+  
+  private static HashMap<Integer, ImageIcon> iconMap = new HashMap<Integer, ImageIcon>();
+  
+  public static ImageIcon getImageIcon(int iconId)
+  {
+	  ImageIcon result = iconMap.get(iconId);
+	  if (result == null) {
+		  result = new ImageIcon(getResourceLocalURL(iconId));
+		  iconMap.put(iconId, result);
+	  }
+	  return result;
+  }
+  
+  public static ImageIcon getImageIcon(URL resourceLocalURL) {
+    return (new ImageIcon(resourceLocalURL));
+  }
+  
+  
+  public static Icon getIconFromTaverna(int iconId) {
+    switch (iconId) {
+      case SOAP_OPERATION_ICON: return (WSDLActivityIcon.getWSDLIcon());
+      case REST_METHOD_ICON:    return (RESTActivityIcon.getRESTActivityIcon());
+      default:                  return (drawMissingIcon());
+    }
+  }
+  
+  
+  /**
+   * This method would be called by other methods in this class
+   * when they were unable to load requested icon.
+   * 
+   * @return A 16x16 pixel icon that represents a missing icon -
+   *         a red cross. 
+   */
+  private static ImageIcon drawMissingIcon()
+  {
+    int w = 16;
+    int h = 16;
+    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+    GraphicsDevice gd = ge.getDefaultScreenDevice();
+    GraphicsConfiguration gc = gd.getDefaultConfiguration();
+    
+    BufferedImage image = gc.createCompatibleImage(w, h, BufferedImage.TYPE_INT_ARGB);
+    Graphics2D g = image.createGraphics();
+    g.setColor(Color.RED);
+    g.setStroke(new BasicStroke(3, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER));
+    g.drawLine(4, 4, 12, 12);
+    g.drawLine(12, 4, 4, 12);
+    g.dispose();
+    
+    return new ImageIcon(image); 
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/ResourcePreviewContent.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/ResourcePreviewContent.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/ResourcePreviewContent.java
new file mode 100644
index 0000000..7dc1fce
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/ResourcePreviewContent.java
@@ -0,0 +1,38 @@
+package net.sf.taverna.biocatalogue.model;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+
+/**
+ * Helper class to hold all data about the generated preview.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class ResourcePreviewContent
+{
+  private Resource resource;
+  private JComponent jcContent;
+  
+  public ResourcePreviewContent(Resource resource, JComponent content)
+  {
+    this.resource = resource;
+    this.jcContent = content;
+  }
+  
+  public Resource getResource() {
+    return(this.resource);
+  }
+  
+  public JComponent getContent() {
+    return(this.jcContent);
+  }
+  
+  
+  public static ResourcePreviewContent createDummyInstance()
+  {
+    Resource r = new Resource(BioCatalogueClient.API_USERS_URL + "/1", "Dummy user");
+    return (new ResourcePreviewContent(r, new JLabel("dummy content - JLabel")));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapOperationIdentity.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapOperationIdentity.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapOperationIdentity.java
new file mode 100644
index 0000000..df16566
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapOperationIdentity.java
@@ -0,0 +1,77 @@
+package net.sf.taverna.biocatalogue.model;
+
+/**
+ * Identifies a SOAP operation (or "processor" in Taverna terms)
+ * in the most straightforward way - by WSDL location and operation name. 
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class SoapOperationIdentity extends SoapServiceIdentity
+{
+  public static final String ACTION_STRING_SEPARATOR = "===";
+  
+  private final String operationName;
+  private final String description;
+
+  public SoapOperationIdentity(String wsdlLocation, String operationName, String description) {
+    super(wsdlLocation);
+    this.operationName = operationName;
+    this.description = description;
+  }
+  
+  public SoapOperationIdentity(Object errorDetails) {
+    super(errorDetails);
+    this.operationName = null;
+    this.description = null;
+  }
+  
+  public String getOperationName() {
+    return operationName;
+  }
+  
+  public String getDescription() {
+    return description;
+  }
+  
+  
+  /**
+   * @return String that can be placed into an action command (i.e. into JClickableLabel)
+   *         to identify a SOAP operation - WSDL location and operation name are concatenated
+   *         with <code>SoapOperationIdentity.ACTION_STRING_SEPARATOR</code>.
+   */
+  public String toActionString() {
+    return (getWsdlLocation() + ACTION_STRING_SEPARATOR + this.operationName);
+  }
+  
+  
+  /**
+   * @param actionString String that includes WSDL location appended by
+   *                     <code>SoapOperationIdentity.ACTION_STRING_SEPARATOR</code>
+   *                     and by the operation name of a SOAP operations.
+   *                     <br/>
+   *                     The action string may either contain only WSDL location and operation
+   *                     name (which are joined by a specified separator) OR the action string
+   *                     may start from <code>BioCataloguePluginConstants.ACTION_PREVIEW_SOAP_OPERATION_AFTER_LOOKUP</code>. 
+   * @return Instance of this class initialised with the values from the <code>actionString</code>
+   *         or <code>null</code> if an error occurred. 
+   */
+  public static SoapOperationIdentity fromActionString(String actionString)
+  {
+    if (actionString == null) return (null);
+    
+    // remove the prefix if it is present
+    if (actionString.startsWith(BioCataloguePluginConstants.ACTION_PREVIEW_SOAP_OPERATION_AFTER_LOOKUP)) {
+      actionString = actionString.substring(BioCataloguePluginConstants.ACTION_PREVIEW_SOAP_OPERATION_AFTER_LOOKUP.length());
+    }
+    
+    String[] parts = actionString.split(ACTION_STRING_SEPARATOR);
+    if (parts == null || parts.length != 2 ||
+        parts[0].length() == 0 || parts[1].length() == 0)
+    {
+      return (null);
+    }
+    
+    return (new SoapOperationIdentity(parts[0], parts[1], null));
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapOperationPortIdentity.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapOperationPortIdentity.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapOperationPortIdentity.java
new file mode 100644
index 0000000..6910e23
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapOperationPortIdentity.java
@@ -0,0 +1,26 @@
+package net.sf.taverna.biocatalogue.model;
+
+public class SoapOperationPortIdentity extends SoapOperationIdentity
+{
+  private String portName;
+  private boolean isInput;
+  
+  public SoapOperationPortIdentity(String wsdlLocation, String operationName, String portName, boolean isInput) {
+    super(wsdlLocation, operationName, null);
+    this.portName = portName;
+    this.isInput = isInput;
+  }
+  
+  public SoapOperationPortIdentity(Object errorDetails) {
+    super(errorDetails);
+  }
+  
+  public String getPortName() {
+    return portName;
+  }
+  
+  public boolean isInput() {
+    return isInput;
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapProcessorIdentity.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapProcessorIdentity.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapProcessorIdentity.java
new file mode 100644
index 0000000..814a701
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapProcessorIdentity.java
@@ -0,0 +1,27 @@
+package net.sf.taverna.biocatalogue.model;
+
+/**
+ * Identifies a SOAP Processor in Taverna terms. Adds a local name
+ * attribute to the details available in <code>SoapOperationIdentity</code>.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class SoapProcessorIdentity extends SoapOperationIdentity
+{
+  private final String localName;
+
+  public SoapProcessorIdentity(String wsdlLocation, String operationName, String localName) {
+    super(wsdlLocation, operationName, null);
+    this.localName = localName;
+  }
+  
+  public SoapProcessorIdentity(Object errorDetails) {
+    super(errorDetails);
+    this.localName = null;
+  }
+  
+  public String getLocalName() {
+    return localName;
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapServiceIdentity.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapServiceIdentity.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapServiceIdentity.java
new file mode 100644
index 0000000..a015ad2
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/SoapServiceIdentity.java
@@ -0,0 +1,45 @@
+package net.sf.taverna.biocatalogue.model;
+
+/**
+ * Identifies a SOAP service in the most straightforward
+ * way - by WSDL location. 
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class SoapServiceIdentity
+{
+  private final String wsdlLocation;
+  
+  // this variable holds an object that will be displayable
+  private final Object errorDetails;
+
+  public SoapServiceIdentity(String wsdlLocation) {
+    this.wsdlLocation = wsdlLocation;
+    this.errorDetails = null;
+  }
+  
+  public SoapServiceIdentity(Object errorDetails) {
+    this.errorDetails = errorDetails;
+    this.wsdlLocation = null;
+  }
+  
+  public String getWsdlLocation() {
+    return (wsdlLocation);
+  }
+  
+  public boolean hasError() {
+    return (errorDetails != null);
+  }
+  
+  /**
+   * @return Returned object contains an object that may be displayed
+   *         in a JOptionPane or printed (in other words defining a
+   *         sensible way of displaying itself), which has details of
+   *         an error that has occurred which prevented from populating
+   *         this instance with the actual details of their SOAP service.
+   */
+  public Object getErrorDetails() {
+    return (errorDetails);
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Tag.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Tag.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Tag.java
new file mode 100644
index 0000000..e1ca6b9
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Tag.java
@@ -0,0 +1,218 @@
+package net.sf.taverna.biocatalogue.model;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+import org.apache.commons.lang.StringEscapeUtils;
+
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class Tag implements Serializable
+{
+  private static final long serialVersionUID = 784872111173271581L;
+  
+  private String tagURI;          // URI to use on BioCatalogue to fetch all tagged items
+	private String tagNamespace;    // namespace where this tag is defined
+	private String tagDisplayName;  // only the actual tag (for display within the tag cloud)
+  private String fullTagName;     // full tag name - including namespace
+	private int itemCount;          // count of tagged items
+  
+	
+	/**
+	 * Constructs a Tag instance from primitive components.
+	 * All values are set directly, no internal inference made.
+	 * 
+	 * @param tagURI
+	 * @param tagNamespace
+	 * @param tagDisplayName
+	 * @param fullTagName
+	 * @param itemCount
+	 */
+	public Tag(String tagURI, String tagNamespace, String tagDisplayName, String fullTagName, int itemCount)
+	{
+	  this.tagURI = tagURI;
+    this.tagNamespace = tagNamespace;
+    this.tagDisplayName = tagDisplayName;
+    this.setFullTagName(fullTagName);
+    this.itemCount = itemCount;
+	}
+	
+	
+	/**
+	 * Constructs Tag instance from an XML representation of the Tag from BioCatalogue API.
+	 * 
+	 * @param xmlTag
+	 */
+	public Tag(org.biocatalogue.x2009.xml.rest.Tag xmlTag)
+	{
+	  // these values come directly from the XML data obtained via the API 
+	  this.tagURI = xmlTag.getHref();
+	  this.fullTagName = xmlTag.getName();
+	  this.itemCount = xmlTag.getTotalItemsCount().intValue();
+	  
+	  // NB! Namespace and the display name need to be inferred 'manually'.
+	  // First - set the namespace; it's value is taken from the 'namespace'
+	  // attribute of the tag URI.
+    this.tagNamespace = Util.extractURLParameter(this.tagURI, "namespace");
+	  
+	  // Now set the display name; if full tag name is not a part of any ontology,
+	  // display name will be identical to the full name. 
+	  if (this.fullTagName.startsWith("<") && this.fullTagName.endsWith(">")) {
+	    int iStart = this.fullTagName.lastIndexOf('#') + 1;
+	    this.tagDisplayName = this.fullTagName.substring(iStart, this.fullTagName.length() - 1);
+	  }
+	  else {
+	    this.tagDisplayName = this.fullTagName;
+	  }
+	}
+	
+  
+	// *** Various getters and setters ***
+	
+	public void setTagURI(String tagURI) {
+    this.tagURI = tagURI;
+  }
+	
+  public String getTagURI() {
+    return tagURI;
+  }
+  
+  
+  public void setTagNamespace(String tagNamespace) {
+    this.tagNamespace = tagNamespace;
+  }
+  
+  public String getTagNamespace() {
+    return tagNamespace;
+  }
+  
+  
+  public void setTagDisplayName(String tagDisplayName) {
+    this.tagDisplayName = tagDisplayName;
+  }
+  
+  public String getTagDisplayName() {
+	  return tagDisplayName;
+  }
+  
+  
+  public void setFullTagName(String fullTagName) {
+    this.fullTagName = fullTagName;
+  }
+  
+  /**
+   * @return Unique and unambiguous name of this tag on BioCatalogue:<br/>
+   *         <ul>
+   *         <li>for tags with no namespaces, they it is just plain text names;</li>
+   *         <li>for those with namespaces, it will have the following form:<br/>
+   *             "<code>< http://www.mygrid.org.uk/ontology#retrieving ></code>" (without spaces, though), where
+   *             the first part before the '#' symbol is the namespace and the second part
+   *             is the actual tag within that namespace.</li></ul>
+   */
+  public String getFullTagName() {
+    return fullTagName;
+  }
+  
+  
+	public int getItemCount() {
+		return itemCount;
+	}
+	
+	public void setItemCount(int itemCount) {
+		this.itemCount = itemCount;
+	}
+	
+	
+	// *** Tag Comparators ***
+	
+	public static class ReversePopularityComparator implements Comparator<Tag>
+	{
+	  public ReversePopularityComparator() {
+	    super();
+	  }
+	  
+	  public int compare(Tag t1, Tag t2)
+	  {
+	    if (t1.getItemCount() == t2.getItemCount()) {
+	      // in case of the same popularity, compare by full tag names (which are unique)
+	      return (t1.getFullTagName().compareTo(t2.getFullTagName()));
+	    }
+	    else {
+	      // popularity isn't the same; arrange by popularity (more popular first)
+	      return (t2.getItemCount() - t1.getItemCount());
+	    }
+	  }
+	}
+	
+	
+	public static class AlphanumericComparator implements Comparator<Tag>
+  {
+    public AlphanumericComparator() {
+      super();
+    }
+    
+    public int compare(Tag t1, Tag t2) {
+      // full tag names are unique on BioCatalogue
+      return (t1.getFullTagName().compareTo(t2.getFullTagName()));
+    }
+  }
+	
+	public static class AlphabeticalIgnoreCaseComparator implements Comparator<Tag>
+	  {
+	    public AlphabeticalIgnoreCaseComparator() {
+	      super();
+	    }
+	    
+	    public int compare(Tag t1, Tag t2) {
+	      // full tag names are unique on BioCatalogue
+	      return (t1.getTagDisplayName().compareToIgnoreCase(t2.getTagDisplayName()));
+	    }
+	  }
+	
+	/**
+   * This makes sure that things like instanceOf() and remove() in List interface
+   * work properly - this way resources are treated to be the same if they store
+   * identical data, rather than they simply hold the same reference.
+   */
+	public boolean equals(Object other) {
+    // could only be equal to another Tag object, not anything else
+    if (! (other instanceof Tag)) return (false);
+    
+    // 'other' object is a Tag; equality is based on the data stored
+    // in the current and 'other' Tag instances
+    Tag otherTag = (Tag)other;
+    return (this.itemCount == otherTag.itemCount && this.fullTagName.equals(otherTag.fullTagName));
+  }
+	
+  
+  public String toString()
+  {
+    return ("Tag (" + this.fullTagName + ", " + this.itemCount + ")");
+  }
+  
+  
+  /**
+   * This method is used to generate the tooltip to be shown over the tag
+   * in the tagcloud. Shown text will contain the full tag name, namespace
+   * and frequency.
+   * 
+   * @return HTML encoded string ready to be put into the tooltip.
+   */
+  public String getTagCloudTooltip()
+  {
+    StringBuilder tooltip = new StringBuilder("<html>");
+    
+    tooltip.append("&nbsp;<b>" + (this.fullTagName.length() > this.tagDisplayName.length() ? "Full tag" : "Tag") + ": </b>" + StringEscapeUtils.escapeHtml(this.fullTagName));
+    if (this.tagNamespace != null && this.tagNamespace.length() > 0) {
+      tooltip.append("<br>&nbsp;<b>Namespace: </b>" + StringEscapeUtils.escapeHtml(this.tagNamespace));
+    }
+    tooltip.append("<br>&nbsp;<b>Frequency: </b>" + this.itemCount);
+    tooltip.append("</html>");
+    
+    return tooltip.toString();
+  }
+  
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Util.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Util.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Util.java
new file mode 100644
index 0000000..06b30e3
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/Util.java
@@ -0,0 +1,793 @@
+package net.sf.taverna.biocatalogue.model;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.BorderFactory;
+import javax.swing.JLabel;
+
+import net.sf.taverna.raven.appconfig.ApplicationRuntime;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.BioCataloguePerspective;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.log4j.Logger;
+
+/**
+ * Class containing various reusable helper methods.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class Util
+
+
+{
+	
+	private static Logger logger = Logger.getLogger(Util.class);
+
+  
+  /**
+   * Makes sure that one component (for example, a window) is centered horizontally and vertically
+   * relatively to the other component. This is achieved by aligning centers of the two components.
+   * 
+   * This method can be used even if the 'dependentComponent' is larger than the 'mainComponent'. In
+   * this case it is probably only useful for centering windows against each other rather than
+   * components inside a container.
+   * 
+   * Method also makes sure that the dependent component will not be placed above the screen's upper
+   * edge and to the left of the left edge.
+   */
+  public static void centerComponentWithinAnother(Component mainComponent, Component dependentComponent)
+  {
+    int iMainComponentCenterX = (int)Math.round(mainComponent.getLocationOnScreen().getX() + (mainComponent.getWidth() / 2));
+    int iPosX = iMainComponentCenterX - (dependentComponent.getWidth() / 2);
+    if (iPosX < 0) iPosX = 0;
+    
+    int iMainComponentCenterY = (int)Math.round(mainComponent.getLocationOnScreen().getY() + (mainComponent.getHeight() / 2));
+    int iPosY = iMainComponentCenterY - (dependentComponent.getHeight() / 2);
+    if (iPosY < 0) iPosY = 0;
+    
+    dependentComponent.setLocation(iPosX, iPosY);
+  }
+  
+  
+  /**
+   * The parameter is the class name to be processed; class name is likely to be in
+   * the form <class_name>$<integer_value>, where the trailing part starting with
+   * the $ sign indicates the anonymous inner class within the base class. This will
+   * strip out that part of the class name to get the base class name.
+   */
+  public static String getBaseClassName(String strClassName)
+  {
+    // strip out the class name part after the $ sign; return
+    // the original value if the dollar sign wasn't found
+    String strResult = strClassName;
+    
+    int iDollarIdx = strResult.indexOf("$");
+    if (iDollarIdx != -1) strResult = strResult.substring(0, iDollarIdx);
+    
+    return (strResult);
+  }
+  
+  
+  /**
+   * Makes sure that the supplied string is no longer than provided length.
+   */
+  public static String ensureStringLength(String str, int iLength) {
+    if (str.length() > iLength) str = str.substring(0, iLength) + " (...)";
+    return (str);
+  }
+  
+  /**
+   * Makes sure that the supplied string doesn't have any lines (separated by HTML line break tag) longer
+   * than specified; assumes that there are no line breaks in the source line.
+   * 
+   * @param str The string to work with.
+   * @param iLineLength Desired length of each line.
+   * @param bIgnoreBrokenWords True if line breaks are to be inserted exactly each <code>iLineLength</code>
+   *                           symbols (which will most likely cause broken words); false to insert line breaks
+   *                           at the first space after <code>iLineLength</code> symbols since last line break.
+   * @return New string with inserted HTML line breaks.
+   */
+  public static String ensureLineLengthWithinString(String str, int iLineLength, boolean bIgnoreBrokenWords)
+  {
+    StringBuilder out = new StringBuilder(str);
+    
+    // keep inserting line breaks from end of the line till the beginning until all done
+    int iLineBreakPosition = 0;
+    while (iLineBreakPosition >= 0 && iLineBreakPosition < out.length())
+    {
+      // insert line break either exactly at calculated position or 
+      iLineBreakPosition += iLineLength;
+      iLineBreakPosition = (bIgnoreBrokenWords ?
+                            iLineBreakPosition :
+                            out.indexOf(" ", iLineBreakPosition));
+      
+      if (iLineBreakPosition > 0 && iLineBreakPosition < out.length()) {
+        out.insert(iLineBreakPosition, "<br>");
+        iLineBreakPosition += 4;  // -- four is the length of "<br>"
+      }
+    }
+    
+    return (out.toString());
+  }
+  
+  
+  /**
+   * This is a convenience method for calling
+   * {@link Util#pluraliseNoun(String, long, boolean)}
+   * with <code>false</code> as a value for third parameter.
+   */
+  public static String pluraliseNoun(String noun, long count) {
+    return (pluraliseNoun(noun, count, false));
+  }
+  
+  /**
+   * Performs naive pluralisation of the supplied noun.
+   * 
+   * @param noun Noun in a singular form.
+   * @param count Number of occurrences of the item, for which the noun is provided.
+   * @param forceAppendingSByDefault <code>true</code> to make sure that "y" -> "ies"
+   *                                 substitution is <b>not made</b>, but instead "s" is appended
+   *                                 to unmodified root of the noun.
+   * @return Pluralised <code>noun</code>: with appended -s or -y replaced with -ies.
+   */
+  public static String pluraliseNoun(String noun, long count, boolean forceAppendingSByDefault)
+  {
+    if (count % 10 != 1 || count == 11) {
+      if (!forceAppendingSByDefault && noun.endsWith("y")) {
+        return (noun.substring(0, noun.length() - 1) + "ies");  // e.g. ENTRY -> ENTRIES
+      }
+      else {
+        return (noun + "s");  // e.g. SHIP -> SHIPS
+      }
+    }
+    else {
+      // no need to pluralise - count is of the type 21, 31, etc.. 
+      return noun;
+    }
+  }
+  
+  
+  /**
+   * Calculates time difference between two {@link Calendar} instances.
+   * 
+   * @param earlier The "earlier" date.
+   * @param later The "later" date.
+   * @param maxDifferenceMillis The maximum allowed time difference between the two
+   *                            {@link Calendar} instances, in milliseconds. If the calculated
+   *                            difference will exceed <code>maxDifferenceMillis</code>,
+   *                            <code>null</code> will be returned. If this parameter has
+   *                            a value <code>less or equal to zero</code>, any time difference
+   *                            between the {@link Calendar} instances will be permitted.
+   * @return String in the form "XX seconds|minutes|hours|days ago". Proper pluralisation will
+   *         be performed on the name of the time unit. <code>null</code> will be returned in
+   *         cases where one of the {@link Calendar} instances is <code>null</code>, time
+   *         difference between the provided instances is greater than <code>maxDifferenceMillis</code>
+   *         or <code>earlier</code> date is not really earlier than <code>later</code> one.
+   */
+  public static String getAgoString(Calendar earlier, Calendar later, long maxDifferenceMillis)
+  {
+    // one of the dates is missing
+    if (earlier == null || later == null) {
+      return null;
+    }
+    
+    if (earlier.before(later)) {
+      long differenceMillis = later.getTimeInMillis() - earlier.getTimeInMillis();
+      
+      if (maxDifferenceMillis <= 0 || (maxDifferenceMillis > 0 && differenceMillis <= maxDifferenceMillis)) 
+      {
+        long result = 0;
+        String unitName = "";
+        
+        if (differenceMillis < 60 * 1000) {
+          result = differenceMillis / 1000;
+          unitName = "second";
+        }
+        else if (differenceMillis < 60 * 60 * 1000) {
+          result = differenceMillis / (60 * 1000);
+          unitName = "minute";
+        }
+        else if (differenceMillis < 24 * 60 * 60 * 1000) {
+          result = differenceMillis / (60 * 60 * 1000);
+          unitName = "hour"; 
+        }
+        else {
+          result = differenceMillis / (24 * 60 * 60 * 1000);
+          unitName = "day";
+        }
+        
+        return (result + " " + Util.pluraliseNoun(unitName, result, true) + " ago");
+      }
+      else {
+        // the difference is too large - larger than the supplied threshold
+        return null;
+      }
+    }
+    else {
+      // the "later" date is not really later than the "earlier" one
+      return null;
+    }
+  }
+  
+  
+  /**
+   * Joins the set of tokens in the provided list into a single string.
+   * This method is a shorthand for {@link Util#join(List, String, String, String)}.
+   * 
+   * @param tokens List of strings to join.
+   * @param separator Separator to insert between individual strings.
+   * @return String of the form <code>[token1][separator][token2][separator]...[tokenN]</code>
+   */
+  public static String join(List<String> tokens, String separator) {
+    return (join(tokens, null, null, separator));
+  }
+  
+  /**
+   * Joins the set of tokens in the provided list into a single string.
+   * 
+   * Any empty strings or <code>null</code> entries in the <code>tokens</code> list 
+   * will be removed to achieve a better resulting joined string.
+   * 
+   * @param tokens List of strings to join.
+   * @param prefix String to prepend to each token.
+   * @param suffix String to append to each token.
+   * @param separator Separator to insert between individual strings.
+   * @return String of the form <code>[prefix][token1][suffix][separator][prefix][token2][suffix][separator]...[prefix][tokenN][suffix]</code>
+   *         <br/><br/>
+   *         Example: call <code>join(["cat","sat","on","the","mat"], "[", "]", ", ")</code> results in the following output:
+   *                  <code>"[cat], [sat], [on], [the], [mat]"</code>
+   */
+  public static String join(List<String> tokens, String prefix, String suffix, String separator)
+  {
+    if (tokens == null) {
+      // nothing to join
+      return (null);
+    }
+    
+    // list of strings is not empty, but some pre-processing is necessary
+    // to remove any empty strings that may be there
+    for (int i = tokens.size() - 1; i >= 0; i--) {
+      if (tokens.get(i) == null || tokens.get(i).length() == 0) {
+        tokens.remove(i);
+      }
+    }
+    
+    // now start the actual processing, but it may be the case that all strings
+    // were empty and we now have an empty list
+    if (tokens.isEmpty()) {
+      // nothing to join - just return an empty string
+      return ("");
+    }
+    else {
+      // there are some tokens -- perform the joining
+      String effectivePrefix = (prefix == null ? "" : prefix);
+      String effectiveSuffix = (suffix == null ? "" : suffix);
+      String effectiveSeparator = (separator == null ? "" : separator);
+      
+      StringBuilder result = new StringBuilder();
+      for (int i = 0; i < tokens.size(); i++) {
+        result.append(effectivePrefix + tokens.get(i) + effectiveSuffix);
+        result.append(i == tokens.size() - 1 ? "" : effectiveSeparator);
+      }
+      
+      return (result.toString());
+    }
+  }
+  
+  /**
+   * Determines whether the plugin is running as a standalone JFrame or inside Taverna Workbench.
+   * This is a naive test, based only on the fact that Taverna uses Raven ApplicationRuntime.
+   */
+  public static boolean isRunningInTaverna()
+  {
+    try {
+      // ApplicationRuntime class is defined within Taverna API. If this is available,
+      // it should mean that the plugin runs within Taverna.
+      ApplicationRuntime.getInstance();
+      return true;
+    }
+    catch (NoClassDefFoundError e) {
+      return false;
+    }
+  }
+  
+  
+  // === STRIPPING OUT HTML FROM STRINGS ===
+  
+  public static String prepareStringForComponent(String source) {
+	  return "<html>" + StringEscapeUtils.escapeHtml(source) + "</html>";
+  }
+
+
+  /*
+   * === The following section is providing URL handling methods. ===
+   */
+  
+  /**
+   * See: {@link Util#appendStringBeforeParametersOfURL(String, String, boolean)}
+   * 
+   * Assumes the last parameter as false, so that the URL encoding will be done.
+   */
+  public static String appendStringBeforeParametersOfURL(String url, String strToAppend) {
+    return (appendStringBeforeParametersOfURL(url, strToAppend, false));
+  }
+  
+  /**
+   * Tiny helper to strip out all HTML tags. This will not leave any HTML tags
+   * at all (so that the content can be displayed in DialogTextArea - and the
+   * like - components. This helps to present HTML content inside JAVA easier.
+   */
+  public static String stripAllHTML(String source) {
+        // don't do anything if not string is provided
+        if (source == null)
+          return ("");
+   
+        // need to preserve at least all line breaks
+        // (ending and starting paragraph also make a line break)
+        source = source.replaceAll("</p>[\r\n]*<p>", "<br/>");
+        source = source.replaceAll("[\\s]+", " ");
+        source = source.replaceAll("\\<br/?\\>", "\n");
+        source = source.replaceAll("\n ", "\n");
+
+       // strip all HTML
+        source = source.replaceAll("\\<.*?\\>", ""); // any HTML tags
+        source = source.replaceAll("&\\w{1,4};", ""); // this is for things like "&nbsp;", "&gt;", etc
+
+        return (source);
+  }
+
+  
+  /**
+   * Appends given string at the end of the provided URL just before the list of parameters in the url.
+   * 
+   * For example, appending ".xml" to URL "http://www.sandbox.biocatalogue.org/services?tag=blast" will
+   * yield "http://www.sandbox.biocatalogue.org/services.xml?tag=blast".
+   * 
+   * No duplication checking is made - if the URL is already ending (before parameters) with the value of
+   * the string to append, that string will still be appended.
+   * 
+   * @param url URL to append the string to.
+   * @param strToAppend The string to append. The value will be url-encode before appending.
+   * @return New string containing modified <code>url</code> with the <code>strToAppend</code> appended
+   *         before the "query string" of the URL.
+   */
+  public static String appendStringBeforeParametersOfURL(String url, String strToAppend, boolean ignoreURLEncoding)
+  {
+    StringBuilder modifiedURL = new StringBuilder(url);
+    
+    int iPositionToInsertProvidedString = modifiedURL.indexOf("?");
+    if (iPositionToInsertProvidedString == -1) iPositionToInsertProvidedString = modifiedURL.length();
+    
+    String stringToInsert = (ignoreURLEncoding ? strToAppend : Util.urlEncodeQuery(strToAppend));
+    modifiedURL.insert(iPositionToInsertProvidedString, stringToInsert);
+    
+    return (modifiedURL.toString());
+  }
+  
+  
+  /**
+   * This method takes a collection of name-value pairs in the form of a map.
+   * It then adds all parameters from this map to the provided URL. 
+   * 
+   * If any parameter has the same name as was already present in the URL, the new value
+   * will replace the existing one.
+   * 
+   * The implementation of this method is not particularly efficient - it makes a
+   * lot of overhead, but it's fine for non-intensive usage.
+   * 
+   * @param url The URL to add a new parameter to.
+   * @param Map of parameters to add to the URL. Keys and values of the map are the names and values for URL parameters.
+   * @return New string which is the original <code>url</code> with all provided
+   *         parameters (in the form <code>name</code>=<code>value</code> pair) added to it.
+   */
+  public static String appendAllURLParameters(String url, Map<String,String> parameterMap)
+  {
+    if (parameterMap == null || parameterMap.size() == 0) {
+      // nothing to add, return the same URL
+      return (url);
+    }
+    else {
+      // just call an overloaded method which has the main logic
+      // to do this action for each name-value pair in the map
+      String out = url;
+      for (Map.Entry<String,String> anotherParameter : parameterMap.entrySet()) {
+        out = appendURLParameter(out, anotherParameter);
+      }
+      return (out);
+    }
+  }
+  
+  
+  
+  /**
+   * This method takes a string representation of a URL and a name-value pair
+   * of strings - in the form of Map.Entry instance to add to the URL as a new parameter.
+   * 
+   * If parameter with the same name was already present in the URL, the new value
+   * will replace the existing one.
+   * 
+   * @param url The URL to add a new parameter to.
+   * @param Map.Entry instance containing the name & the value for the parameter to add.
+   * @return New string which is the original <code>url</code> with the desired
+   *         parameter (<code>name</code>=<code>value</code> pair) added to it.
+   */
+  public static String appendURLParameter(String url, Map.Entry<String,String> parameter)
+  {
+    if (parameter == null) {
+      // nothing to add, return the same URL
+      return (url);
+    }
+    else {
+      // just call an overloaded method which has the main logic to do this action
+      return (appendURLParameter(url, parameter.getKey(), parameter.getValue()));
+    }
+  }
+  
+  
+  /**
+   * This method takes a string representation of a URL and a name-value pair
+   * of strings to add to the URL as a new parameter.
+   * 
+   * If parameter with the same name was already present in the URL, the new value
+   * will replace the existing one.
+   * 
+   * @param url The URL to add a new parameter to.
+   * @param parameter String array with 2 elements - first element is the name
+   *        of the parameter to add, second - the value of the parameter.
+   * @return New string which is the original <code>url</code> with the desired
+   *         parameter (<code>name</code>=<code>value</code> pair) added to it.
+   */
+  public static String appendURLParameter(String url, String[] parameter)
+  {
+    if (parameter == null || parameter.length != 2) {
+      // nothing to add, return the same URL
+      return (url);
+    }
+    else {
+      // just call an overloaded method which has the main logic to do this action
+      return (appendURLParameter(url, parameter[0], parameter[1]));
+    }
+  }
+  
+  
+  /**
+   * This method takes a string representation of a URL and a name-value pair
+   * of strings to add to the URL as a new parameter.
+   * 
+   * If parameter with the same name was already present in the URL, the new value
+   * will replace the existing one.
+   * 
+   * @param url The URL to add a new parameter to.
+   * @param name Name of the parameter to add.
+   * @param value Value of the parameter to add.
+   * @return New string which is the original <code>url</code> with the desired
+   *         parameter (<code>name</code>=<code>value</code> pair) added to it.
+   */
+  public static String appendURLParameter(String url, String name, String value)
+  {
+    // if name of the parameter is not given, ignore this request
+    // (makes sense to return the same URL as the input in this case -
+    //  as appending "nothing" wouldn't make it any different)
+    if (name == null || name.length() == 0) {
+      return (url);
+    }
+    
+    // do everything in the protected block
+    try
+    {
+      // parse the parameters of the given URL
+      Map<String,String> urlParameters = extractURLParameters(url);
+      if (urlParameters == null) {
+        // there were no parameters in the original URL, create new map
+        urlParameters = new HashMap<String,String>();
+      }
+      
+      // add the new parameter (this will replace a parameter with identical
+      // name if it was already present in the map)
+      urlParameters.put(name, value);
+      
+      // parse the URL string into the URL object to extract original query string
+      URL theURL = new URL(url);
+      String originalQueryString = theURL.getQuery();
+      
+      // prepare the basis for the new URL to return
+      String newUrl = null;
+      if (originalQueryString != null) {
+        // replace the original query string with empty space to
+        // give way for appending the new query string
+        newUrl = url.replace(originalQueryString, "");
+      }
+      else {
+        // there were no parameters in the original URL
+        newUrl = url + "?";  
+      }
+      
+      // append the new query string
+      newUrl += constructURLQueryString(urlParameters);
+      
+      return (newUrl);
+    }
+    catch (Exception e)
+    {
+      logger.error("\nCouldn't append parameter ('" + name + "', '" + value + "') to the URL: " + url, e); 
+      return (null);
+    }
+  }
+  
+  
+  /**
+   * Extracts a value of a specific parameter from the supplied URL.
+   *  
+   * @param url The URL to extract the parameter from.
+   * @param parameterName Name of the URL parameter to extract the value for.
+   * @return Value of the parameter with <code>parameterName</code> in the given <code>url</code>.
+   *         If the parameter with specified name is not found in the given <code>url</code>,
+   *         <code>null</code> is returned instead. 
+   */
+  public static String extractURLParameter(String url, String parameterName)
+  {
+    // both URL and the name of the required parameter must be supplied
+    if (url == null || url.length() == 0 || parameterName == null || parameterName.length() == 0) return null;
+    
+    Map<String,String> urlParameters = extractURLParameters(url);
+    if (urlParameters != null) {
+      // the URL has some parameters; check what's the value of the desired parameter
+      return (urlParameters.get(parameterName));
+    }
+    else {
+      // the URL doesn't contain any parameters
+      return (null);
+    }
+  }
+  
+  
+  /**
+   * Extracts the query string from the provided URL. Parses this query string into
+   * a map of parameters.
+   * 
+   * All parameters (both names and values) will have special characters unescaped
+   * (i.e. decoded from the standard url-encoding) and can be used directly.
+   * 
+   * @param url The string representation of the URL to parse.
+   */
+  public static Map<String,String> extractURLParameters(String url)
+  {
+    try {
+      // extract the query part of the supplied URL
+      URL theURL = new URL(url);
+      String queryString = theURL.getQuery();
+      
+      // prepare storage for output
+      Map<String,String> parameterMap = null;
+      
+      // extract each name-value pair from query string (if any are specified in the URL)
+      if (queryString != null && queryString.length() > 0)
+      {
+        // only initialise if there are some parameters
+        parameterMap = new HashMap<String,String>();
+        
+        for (String parameter : queryString.split("&")) {
+          String[] nameValueArr = parameter.split("=");
+          
+          String name = nameValueArr[0]; // parameter name must always be present
+          String value = (nameValueArr.length == 2 ? nameValueArr[1] : null); // could be that parameter value is not set (e.g. "q=") - insert null then
+          
+          // decode possible special characters
+          name = urlDecodeQuery(name);
+          if (value != null) value = urlDecodeQuery(value);
+          
+          parameterMap.put(name, value);
+        }
+      }
+      
+      return (parameterMap);
+    }
+    catch (MalformedURLException e)
+    {
+      // some problem occurred - report it; can't return any data in this case
+      logger.error("Couldn't parse parameters of a URL: " + url + "; details below:", e);
+      return null;
+    }
+  }
+  
+  
+  /**
+   * This method is the opposite for <code>extractURLParameters(String url)</code>.
+   * It takes a map of parameters, performs URL-encoding of each and assembles them
+   * into a query string.
+   * 
+   * The query string then can be added to the <code>URL</code> object by using standard
+   * Java API. 
+   * 
+   * @param urlParameters Map of parameters to use in query string construction.
+   * @return URL-encoded query string.
+   */
+  public static String constructURLQueryString(Map<String,String> urlParameters)
+  {
+    if (urlParameters != null) {
+      StringBuilder queryString = new StringBuilder();
+      
+      // iterate through all parameters and reconstruct the query string
+      for (Map.Entry<String,String> parameter : urlParameters.entrySet())
+      {
+        if (queryString.length() > 0) queryString.append("&"); // parameter separator
+        queryString.append(urlEncodeQuery(parameter.getKey()) + "=" + urlEncodeQuery(parameter.getValue()));
+      }
+      
+      return (queryString.toString());
+    }
+    else {
+      return (null);
+    }
+  }
+  
+  
+  /**
+   * Prepares the string to serve as a part of url query to the server.
+   * @param query The string that needs URL encoding.
+   * @return URL encoded string that can be inserted into the request URL.
+   */
+  public static String urlEncodeQuery(String query)
+  {
+    // "fast exit" - if null supplied, just return an empty string;
+    // this is because in the URLs we have "q=", rather than "q=null" - this will cater for such cases
+    if (query == null) return ("");
+    
+    // encode the query
+    String strRes = "";
+    try {
+      strRes = URLEncoder.encode(query, "UTF-8");
+    }
+    catch (UnsupportedEncodingException e) {
+      // do nothing
+    }
+    
+    return (strRes);
+  }
+  
+  
+  /**
+   * Decodes a string which came as a part of of URL (e.g. a URL parameter). This converts
+   * codes of escaped special characters back into those special characters.
+   * 
+   * @param query The string that needs URL decoded.
+   * @return Decoded string that will contain all the special characters.
+   */
+  public static String urlDecodeQuery(String query)
+  {
+    String strRes = "";
+    
+    try {
+      strRes = URLDecoder.decode(query, "UTF-8");
+    }
+    catch (UnsupportedEncodingException e) {
+      // do nothing
+    }
+    
+    return (strRes);
+  }
+  
+  
+  /**
+   * This method is "clones" an object supplied as an argument. It uses
+   * serialisation to achieve this (as opposed to manually implementing deep
+   * copying of all referenced objects in the graph of the provided object).
+   * This technique is used to make sure that the new object will be exact
+   * replica, but totally independent of the original one.
+   * 
+   * Note that this code works ~100 times slower than it would do if deep copying
+   * was implemented. However, this will not be used in tight loops (and in loops
+   * at all), so for one-off tasks it is fine.
+   * 
+   * @author Dave Miller<br/>
+   * Original version of the code in this method is taken from
+   * <a href="http://www.javaworld.com/javaworld/javatips/jw-javatip76.html?page=2">
+   *    http://www.javaworld.com/javaworld/javatips/jw-javatip76.html?page=2
+   * </a> [accessed on 25/Feb/2010].
+   * <br/><br/>
+   * 
+   * @author Subhajit Dasgupta<br/>
+   * Example of using an alternative class loader during object de-serialisation
+   * was taken from
+   * <a href="http://blogs.sun.com/adventures/entry/desrializing_objects_custom_class_loaders">
+   *    http://blogs.sun.com/adventures/entry/desrializing_objects_custom_class_loaders
+   * </a> [accessed on 29/Mar/2010].
+   * 
+   * @return Deep copy of the provided object. If deep copying doesn't succeed,
+   *         <code>null</code> is returned.
+   */
+  public static Object deepCopy(Object objectToCopy)
+  {
+    // a "safety net" - a class loader of BioCatalogue perspective may be used in
+    // de-serialisation process to make sure that all classes are recognised
+    // (system class loader may not be able to "see" all BioCatalogue plugin's files,
+    //  but just those in Taverna's /lib folder)
+    final ClassLoader[] customClassLoaders = new ClassLoader[] { BioCataloguePerspective.class.getClassLoader() };
+    
+    try
+    {
+      ObjectOutputStream oos = null;
+      ObjectInputStream ois = null;
+      try
+      {
+         ByteArrayOutputStream bos = new ByteArrayOutputStream();
+         oos = new ObjectOutputStream(bos);
+         
+         // serialise and pass the object
+         oos.writeObject(objectToCopy);
+         oos.flush();
+         
+         // read and return the new object
+         ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
+         ois = new ObjectInputStream(bin) {
+                     /**
+                      * <code>resolveClass()</code> method is overridden to make use of
+                      * custom ClassLoader in the de-serialisation process.
+                      * <br/>
+                      * This is needed to make sure that the ClassLoader of the BioCatalogue
+                      * perspective is used as opposed to the system ClassLoader which will
+                      * only be able to see classes from Taverna's /lib folder.
+                      */
+                     protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException
+                     {
+                       String className = desc.getName();
+                       try {
+                         // attempt to use default class loader
+                         return Class.forName(className);
+                       }
+                       catch (ClassNotFoundException exc)
+                       {
+                         // default class loader was unable to locate a required class -
+                         // attempt to use one of the provided class loaders
+                         for (ClassLoader cl : customClassLoaders) {
+                           try {
+                             return cl.loadClass(className);
+                           } 
+                           catch (ClassNotFoundException e) {
+                             /* do nothing here - there may be other class loaders to try */
+                           }
+                         }
+                         // none of the class loaders was able to recognise the currently
+                         // de-serialised class, so it's indeed an exception
+                         throw new ClassNotFoundException(className + 
+                             " -- neither system, nor alternative class loaders were able to load this class");
+                       }
+                     }
+                   };
+         return ois.readObject();
+      }
+      catch(Exception e)
+      {
+         logger.error("Could not perform deep copy of " + objectToCopy.getClass() + " instance", e);
+      }
+      finally
+      {
+         oos.close();
+         ois.close();
+      }
+    }
+    catch (Exception e) {
+      logger.error("Could not close object streams during deep copy of " + objectToCopy.getClass() + " instance");
+    }
+    
+    // Error occurred - couldn't produce the deep copy...
+    return null;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BeanForPOSTToFilteredIndex.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BeanForPOSTToFilteredIndex.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BeanForPOSTToFilteredIndex.java
new file mode 100644
index 0000000..d879377
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BeanForPOSTToFilteredIndex.java
@@ -0,0 +1,12 @@
+package net.sf.taverna.biocatalogue.model.connectivity;
+
+import java.util.Map;
+
+/**
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class BeanForPOSTToFilteredIndex
+{
+  public Map<String, String[]> filters;
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BeansForJSONLiteAPI.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BeansForJSONLiteAPI.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BeansForJSONLiteAPI.java
new file mode 100644
index 0000000..ea3b391
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BeansForJSONLiteAPI.java
@@ -0,0 +1,84 @@
+package net.sf.taverna.biocatalogue.model.connectivity;
+
+
+/**
+ * Binding beans for GSON library to instantiate objects
+ * from JSON data obtained from the 'Lite' version of the
+ * BioCatalogue JSON API.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class BeansForJSONLiteAPI
+{
+  
+  public static abstract class ResourceIndex
+  {
+    public ResourceIndex() { }
+    public abstract ResourceLinkWithName[] getResources();
+  }
+  
+  
+  public static class SOAPOperationsIndex extends ResourceIndex {
+    public SOAPOperationsIndex() { }
+    public ResourceLinkWithName[] soap_operations;
+    
+    public ResourceLinkWithName[] getResources() {
+      return soap_operations;
+    }
+  }
+  
+  public static class RESTMethodsIndex extends ResourceIndex {
+    public RESTMethodsIndex() { }
+    public ResourceLinkWithName[] rest_methods;
+    
+    public ResourceLinkWithName[] getResources() {
+      return rest_methods;
+    }
+  }
+  
+  public static class ServicesIndex extends ResourceIndex {
+    public ServicesIndex() { }
+    public ResourceLinkWithName[] services;
+    
+    public ResourceLinkWithName[] getResources() {
+      return services;
+    }
+  }
+  
+  public static class ServiceProvidersIndex extends ResourceIndex {
+    public ServiceProvidersIndex() { }
+    public ResourceLinkWithName[] service_providers;
+    
+    public ResourceLinkWithName[] getResources() {
+      return service_providers;
+    }
+  }
+  
+  public static class UsersIndex extends ResourceIndex {
+    public UsersIndex() { }
+    public ResourceLinkWithName[] users;
+    
+    public ResourceLinkWithName[] getResources() {
+      return users;
+    }
+  }
+  
+  
+  
+  public static class ResourceLinkWithName
+  {
+    private ResourceLinkWithName() { }
+    
+    private String resource;
+    private String name;
+    
+    public String getURL() {
+      return (this.resource);
+    }
+    
+    public String getName() {
+      return (this.name);
+    }
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueAPIRequest.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueAPIRequest.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueAPIRequest.java
new file mode 100644
index 0000000..ea55a77
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/biocatalogue/model/connectivity/BioCatalogueAPIRequest.java
@@ -0,0 +1,47 @@
+package net.sf.taverna.biocatalogue.model.connectivity;
+
+/**
+ * A class to wrap BioCatalogue API requests - will include
+ * the type (GET, POST, etc), URL and the data to send
+ * if that's a POST / PUT request. 
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class BioCatalogueAPIRequest
+{
+  public static enum TYPE {
+    GET,
+    POST,
+    PUT,
+    DELETE
+  }
+  
+  
+  private TYPE requestType;
+  private String url;
+  private String data;
+  
+  
+  public BioCatalogueAPIRequest(TYPE requestType, String url, String data) {
+    this.requestType = requestType;
+    this.url = url;
+    this.data = data;
+  }
+  
+  
+  public TYPE getRequestType() {
+    return requestType;
+  }
+  
+  public String getURL(){
+    return url;
+  }
+  public void setURL(String url) {
+    this.url = url;
+  }
+  
+  public String getData(){
+    return data;
+  }
+  
+}


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

Posted by st...@apache.org.
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


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationPanel.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationPanel.java
new file mode 100644
index 0000000..cf7f42a
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationPanel.java
@@ -0,0 +1,214 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JPanel;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.commons.services.ActivityTypeNotFoundException;
+import uk.org.taverna.commons.services.InvalidConfigurationException;
+import uk.org.taverna.commons.services.ServiceRegistry;
+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.port.ActivityPort;
+import uk.org.taverna.scufl2.api.port.InputActivityPort;
+import uk.org.taverna.scufl2.api.port.OutputActivityPort;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * @author alanrw
+ */
+@SuppressWarnings("serial")
+public abstract class ActivityConfigurationPanel extends JPanel {
+	private static final Logger logger = Logger.getLogger(ActivityConfigurationPanel.class);
+	private final static Scufl2Tools scufl2Tools = new Scufl2Tools();
+
+	// protected final URITools uriTools = new URITools();
+	private final Activity activity;
+	private final Configuration configuration;
+	private final List<ActivityPortConfiguration> inputPorts;
+	private final List<ActivityPortConfiguration> outputPorts;
+	protected ObjectNode json;
+
+	public ActivityConfigurationPanel(Activity activity) {
+		this(activity, scufl2Tools.configurationFor(activity,
+				activity.getParent()));
+	}
+
+	public ActivityConfigurationPanel(Activity activity,
+			Configuration configuration) {
+		this.activity = activity;
+		this.configuration = configuration;
+		inputPorts = new ArrayList<>();
+		outputPorts = new ArrayList<>();
+	}
+
+	/**
+	 * Initializes the configuration panel. This method is also used to discard
+	 * any changes and reset the panel to its initial state. Subclasses should
+	 * implement this method to set up the panel and must call
+	 * <tt>super.initialise()</tt> first.
+	 */
+	protected void initialise() {
+		json = configuration.getJson().deepCopy();
+		inputPorts.clear();
+		for (InputActivityPort activityPort : activity.getInputPorts())
+			inputPorts.add(new ActivityPortConfiguration(activityPort));
+		outputPorts.clear();
+		for (OutputActivityPort activityPort : activity.getOutputPorts())
+			outputPorts.add(new ActivityPortConfiguration(activityPort));
+	}
+
+	public abstract boolean checkValues();
+
+	public abstract void noteConfiguration();
+
+	public boolean isConfigurationChanged() {
+		noteConfiguration();
+		if (portsChanged(inputPorts, activity.getInputPorts().size()))
+			return true;
+		if (portsChanged(outputPorts, activity.getOutputPorts().size()))
+			return true;
+		return !json.equals(configuration.getJson());
+	}
+
+	public Configuration getConfiguration() {
+		return configuration;
+	}
+
+	public ObjectNode getJson() {
+		return json;
+	}
+
+	protected void setJson(ObjectNode json) {
+		this.json = json;
+	}
+
+	public void refreshConfiguration() {
+		initialise();
+	}
+
+	public void whenOpened() {
+	}
+
+	public void whenClosed() {
+	}
+
+	/**
+	 * Convenience method for getting simple String property values.
+	 *
+	 * @param name
+	 *            the property name
+	 * @return the property value
+	 */
+	protected String getProperty(String name) {
+		JsonNode jsonNode = json.get(name);
+		if (jsonNode == null)
+			return null;
+		return json.get(name).asText();
+	}
+
+	/**
+	 * Convenience method for setting simple String property values.
+	 *
+	 * @param name
+	 *            the property name
+	 * @param value
+	 *            the property value
+	 */
+	protected void setProperty(String name, String value) {
+		json.put(name, value);
+	}
+
+	public List<ActivityPortConfiguration> getInputPorts() {
+		return inputPorts;
+	}
+
+	public List<ActivityPortConfiguration> getOutputPorts() {
+		return outputPorts;
+	}
+
+	protected void configureInputPorts(ServiceRegistry serviceRegistry) {
+		try {
+			Map<String, InputActivityPort> newInputPorts = new HashMap<>();
+			for (InputActivityPort port : serviceRegistry
+					.getActivityInputPorts(getActivity().getType(), getJson()))
+				newInputPorts.put(port.getName(), port);
+			List<ActivityPortConfiguration> inputPorts = getInputPorts();
+			for (ActivityPortConfiguration portConfig : new ArrayList<>(
+					inputPorts))
+				if (newInputPorts.containsKey(portConfig.getName())) {
+					InputActivityPort port = newInputPorts.remove(portConfig
+							.getName());
+					portConfig.setDepth(port.getDepth());
+				} else
+					inputPorts.remove(portConfig);
+			for (InputActivityPort newPort : newInputPorts.values())
+				inputPorts.add(new ActivityPortConfiguration(newPort.getName(),
+						newPort.getDepth()));
+		} catch (InvalidConfigurationException | ActivityTypeNotFoundException e) {
+			logger.warn("Error configuring input ports", e);
+		}
+	}
+
+	protected void configureOutputPorts(ServiceRegistry serviceRegistry) {
+		try {
+			Map<String, OutputActivityPort> newOutputPorts = new HashMap<>();
+			for (OutputActivityPort port : serviceRegistry
+					.getActivityOutputPorts(getActivity().getType(), getJson()))
+				newOutputPorts.put(port.getName(), port);
+			List<ActivityPortConfiguration> outputPorts = getOutputPorts();
+			for (ActivityPortConfiguration portConfig : new ArrayList<>(
+					outputPorts))
+				if (newOutputPorts.containsKey(portConfig.getName())) {
+					OutputActivityPort port = newOutputPorts.remove(portConfig
+							.getName());
+					portConfig.setDepth(port.getDepth());
+					portConfig.setGranularDepth(port.getGranularDepth());
+				} else
+					outputPorts.remove(portConfig);
+			for (OutputActivityPort newPort : newOutputPorts.values())
+				outputPorts.add(new ActivityPortConfiguration(
+						newPort.getName(), newPort.getDepth()));
+		} catch (InvalidConfigurationException | ActivityTypeNotFoundException e) {
+			logger.warn("Error configuring output ports", e);
+		}
+	}
+
+	private boolean portsChanged(List<ActivityPortConfiguration> portDefinitions, int ports) {
+		int checkedPorts = 0;
+		for (ActivityPortConfiguration portDefinition : portDefinitions) {
+			String portName = portDefinition.getName();
+			int portDepth = portDefinition.getDepth();
+			ActivityPort activityPort = portDefinition.getActivityPort();
+			if (activityPort == null)
+				// new port added
+				return true;
+			if (!activityPort.getName().equals(portName))
+				// port name changed
+				return true;
+			if (!activityPort.getDepth().equals(portDepth))
+				// port depth changed
+				return true;
+			checkedPorts++;
+		}
+		if (checkedPorts < ports)
+			// ports deleted
+			return true;
+		return false;
+	}
+
+	public Activity getActivity() {
+		return activity;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityPortConfiguration.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityPortConfiguration.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityPortConfiguration.java
new file mode 100644
index 0000000..6b23fd5
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityPortConfiguration.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (C) 2013 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.ui.views.contextualviews.activity;
+
+import uk.org.taverna.scufl2.api.port.ActivityPort;
+
+/**
+ *
+ *
+ * @author David Withers
+ */
+public class ActivityPortConfiguration {
+
+	private ActivityPort activityPort;
+
+	private String name;
+
+	private int depth;
+
+	private int granularDepth;
+
+	public ActivityPortConfiguration(ActivityPort activityPort) {
+		this.activityPort = activityPort;
+		name = activityPort.getName();
+		depth = activityPort.getDepth();
+	}
+
+	public ActivityPortConfiguration(String name, int depth) {
+		this(name, depth, depth);
+	}
+
+	public ActivityPortConfiguration(String name, int depth, int granularDepth) {
+		this.name = name;
+		this.depth = depth;
+		this.granularDepth = granularDepth;
+	}
+
+	public ActivityPort getActivityPort() {
+		return activityPort;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public int getDepth() {
+		return depth;
+	}
+
+	public void setDepth(int depth) {
+		this.depth = depth;
+	}
+
+	public int getGranularDepth() {
+		return granularDepth;
+	}
+
+	public void setGranularDepth(int granularDepth) {
+		this.granularDepth = granularDepth;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactory.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactory.java
new file mode 100644
index 0000000..b5d29d7
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactory.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.activity;
+
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+
+/**
+ * Defines a factory class that when associated with a selected object creates a
+ * {@link ContextualView} for that selection.
+ * <p>
+ * This factory acts as an SPI to find {@link ContextualView}s for a given
+ * Activity and other workflow components.
+ * </p>
+ * 
+ * @author Stuart Owen
+ * @author Ian Dunlop
+ * @author Stian Soiland-Reyes
+ * 
+ * 
+ * @param <SelectionType>
+ *            - the selection type this factory is associated with
+ * 
+ * @see ContextualView
+ * @see ContextualViewFactoryRegistry
+ */
+public interface ContextualViewFactory<SelectionType> {
+	/**
+	 * @param selection
+	 *            - the object for which ContextualViews needs to be generated
+	 * @return instance of {@link ContextualView}
+	 */
+	public List<ContextualView> getViews(SelectionType selection);
+
+	/**
+	 * Used by the SPI system to find the correct factory that can handle the
+	 * given object type. 
+	 * 
+	 * @param selection
+	 * @return true if this factory relates to the given selection type
+	 * @see ContextualViewFactoryRegistry
+	 */
+	public boolean canHandle(Object selection);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactoryRegistry.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactoryRegistry.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactoryRegistry.java
new file mode 100644
index 0000000..305f3c0
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactoryRegistry.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (C) 2011 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.ui.views.contextualviews.activity;
+
+import java.util.List;
+
+/**
+ * A registry for discovering ActivityViewFactories for a given object,
+ * like an {@link net.sf.taverna.t2.workflowmodel.processor.activity.Activity}.
+ *
+ * @author David Withers
+ */
+public interface ContextualViewFactoryRegistry {
+	/**
+	 * Discover and return the ContextualViewFactory associated to the provided
+	 * object. This is accomplished by returning the discovered
+	 * {@link ContextualViewFactory#canHandle(Object)} that returns true for
+	 * that Object.
+	 *
+	 * @param object
+	 * @return
+	 * @see ContextualViewFactory#canHandle(Object)
+	 */
+	public <T> List<ContextualViewFactory<? super T>> getViewFactoriesForObject(T object);
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/DependencyConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/DependencyConfigurationPanel.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/DependencyConfigurationPanel.java
new file mode 100644
index 0000000..c28cb55
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/DependencyConfigurationPanel.java
@@ -0,0 +1,293 @@
+/*******************************************************************************
+ * Copyright (C) 2013 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.ui.views.contextualviews.activity;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.Color.RED;
+import static java.awt.GridBagConstraints.FIRST_LINE_START;
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.event.ItemEvent.DESELECTED;
+import static java.awt.event.ItemEvent.SELECTED;
+import static java.util.Arrays.asList;
+import static javax.swing.Box.createRigidArea;
+import static javax.swing.BoxLayout.PAGE_AXIS;
+import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER;
+import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.BoxLayout;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * Component for configuring activities that require dependencies.
+ *
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class DependencyConfigurationPanel extends JPanel {
+	private String classLoaderSharing;
+	private List<String> localDependencies;
+	private File libDir;
+
+	public DependencyConfigurationPanel(String classLoaderSharing,
+			List<String> localDependencies, File libDir) {
+		this.classLoaderSharing = classLoaderSharing;
+		this.localDependencies = localDependencies;
+		this.libDir = libDir;
+		setLayout(new BoxLayout(this, PAGE_AXIS));
+
+		// Create panel with classloading options
+		JPanel classloadingPanel = new ClassloadingPanel();
+		// Create panel for selecting jar files
+		JPanel jarFilesPanel = new JarFilesPanel();
+
+		add(classloadingPanel);
+		add(createRigidArea(new Dimension(0,10)));
+		add(jarFilesPanel);
+		add(createRigidArea(new Dimension(0,10)));
+
+	}
+
+	public String getClassLoaderSharing() {
+		return classLoaderSharing;
+	}
+
+	public List<String> getLocalDependencies() {
+		return localDependencies;
+	}
+
+	// Classloading option 'workflow'
+	private static final String WORKFLOW = "Shared for whole workflow";
+	// Classloading option 'system'
+	private static final String SYSTEM = "System classloader";
+	
+	// Panel containing classloading options
+	private class ClassloadingPanel extends JPanel {
+		// Combobox with classloading options
+		private JComboBox<String> jcbClassloadingOption;
+		// Classloading option descriptions
+		private HashMap<String, String> classloadingDescriptions;
+		// JLabel with classloading option description
+		private JLabel jlClassloadingDescription;
+
+		/*
+		 * Panel containing a list of possible classloading options which users
+		 * can select from
+		 */
+		private ClassloadingPanel() {
+			super(new GridBagLayout());
+			jcbClassloadingOption = new JComboBox<>(new String[] { WORKFLOW,
+					SYSTEM });
+			// Set the current classlaoding option based on the configuration bean
+			if ("workflow".equals(classLoaderSharing)) {
+				jcbClassloadingOption.setSelectedItem(WORKFLOW);
+			} else if ("system".equals(classLoaderSharing)) {
+				jcbClassloadingOption.setSelectedItem(SYSTEM);
+			}
+
+			jcbClassloadingOption.addActionListener(new ActionListener(){
+				// Fires up when combobox selection changes
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					Object selectedItem = jcbClassloadingOption.getSelectedItem();
+					jlClassloadingDescription.setText(classloadingDescriptions
+							.get(selectedItem));
+					if (selectedItem.equals(WORKFLOW))
+						classLoaderSharing = "workflow";
+					else if (selectedItem.equals(SYSTEM))
+						classLoaderSharing = "system";
+				}
+			});
+			//jcbClassloadingOption.setEnabled(false);
+
+			classloadingDescriptions = new HashMap<>();
+			classloadingDescriptions.put(WORKFLOW, "<html><small>"
+					+ "Classes are shared across the whole workflow (with any service<br>"
+					+ "also selecting this option), but are reinitialised for each workflow run.<br>"
+					+ "This might be needed if a service passes objects to another, or <br>"
+					+ "state is shared within static members of loaded classes."
+					+ "</small></html>");
+			classloadingDescriptions.put(SYSTEM, "<html><small><p>"
+					+ "The (global) system classloader is used, any dependencies defined here are<br>"
+					+ "made available globally on the first run. Note that if you are NOT using<br>"
+					+ "the defaulf Taverna BootstrapClassLoader, any settings here will be disregarded."
+					+ "</p><p>"
+					+ "This is mainly useful if you are using JNI-based libraries. Note that <br>"
+					+ "for JNI you also have to specify <code>-Djava.library.path</code> and <br>"
+					+ "probably your operating system's dynamic library search path<br>"
+					+ "<code>LD_LIBRARY_PATH</code> / <code>DYLD_LIBRARY_PATH</code> / <code>PATH</code> </p>"
+					+ "</small></html>");
+
+			/*
+			 * Set the current classlaoding description based on the item
+			 * selected in the combobox.
+			 */
+			jlClassloadingDescription = new JLabel(classloadingDescriptions
+					.get(jcbClassloadingOption.getSelectedItem()));
+
+			// Add components to the ClassloadingPanel
+			GridBagConstraints c = new GridBagConstraints();
+			c.anchor = FIRST_LINE_START;
+			c.fill = HORIZONTAL;
+			c.gridx = 0;
+			c.insets = new Insets(10,0,0,0);
+			add(new JLabel("Classloader persistence"), c);
+			c.insets = new Insets(0,0,0,0);
+			add(jcbClassloadingOption, c);
+			c.insets = new Insets(0,30,0,0);
+			add(jlClassloadingDescription, c);
+		}
+	}
+
+	// Panel for users to add local JAR dependencies (contains a list of jar files which users can select from)
+	private class JarFilesPanel extends JPanel {
+		private JLabel warning = new JLabel(
+				"<html>"
+						+ "<center<font color='red'>"
+						+ "Warning: Depending on local libraries makes this workflow<br>"
+						+ "difficult or impossible to run for other users. Try depending<br>"
+						+ "on artifacts from a public repository if possible.</font></center>"
+						+ "</html>");
+
+		private JarFilesPanel() {
+			super();
+			setMinimumSize(new Dimension(400, 150));
+			setLayout(new BorderLayout());
+			setBorder(new EmptyBorder(0,10,0,10));
+
+			JPanel labelPanel = new JPanel();
+			labelPanel.setLayout(new BoxLayout(labelPanel, PAGE_AXIS));
+			JLabel label = new JLabel("Local JAR files");
+			JLabel libLabel = new JLabel("<html><small>" + libDir.getAbsolutePath()
+					+ "</small></html>");
+			labelPanel.add(label);
+			labelPanel.add(libLabel);
+
+			add(labelPanel, NORTH);
+			add(new JScrollPane(jarFiles(), VERTICAL_SCROLLBAR_AS_NEEDED,
+					HORIZONTAL_SCROLLBAR_NEVER), CENTER);
+
+			warning.setVisible(false);
+			/*
+			 * We'll skip the warning until we actually have support for
+			 * artifacts
+			 */
+			//add(warning);
+			updateWarning();
+		}
+
+		private void updateWarning() {
+			// Show warning if there is any local dependencies
+			warning.setVisible(!localDependencies.isEmpty());
+		}
+
+		public JPanel jarFiles() {
+			JPanel panel = new JPanel();
+			panel.setLayout(new BoxLayout(panel, PAGE_AXIS));
+
+			// List of all jar files in the lib directory
+			List<String> jarFiles = asList(libDir
+					.list(new FileExtFilter(".jar")));
+			/*
+			 * We also add the list of jars that may have been configured
+			 * sometime before but are now not present in the lib directory for
+			 * some reason
+			 */
+			Set<String> missingLocalDeps = new HashSet<>(localDependencies);
+			missingLocalDeps.removeAll(jarFiles);
+			/*
+			 * jarFiles and missingLocalDeps now contain two sets of files that
+			 * do not intersect
+			 */
+			List<String> jarFilesList = new ArrayList<>();
+			// Put them all together
+			jarFilesList.addAll(jarFiles);
+			jarFilesList.addAll(missingLocalDeps);
+			Collections.sort(jarFilesList);
+
+			if (jarFilesList.isEmpty()) {
+				panel.add(new JLabel("<html><small>To depend on a JAR file, "
+					+ "copy it to the above-mentioned folder.</small></html>"));
+				return panel;
+			}
+
+			for (String jarFile : jarFilesList) {
+				JCheckBox checkBox = new JCheckBox(jarFile);
+				// Has it already been selected in some previous configuring?
+				checkBox.setSelected(localDependencies.contains(jarFile));
+				checkBox.addItemListener(new ItemListener() {
+					@Override
+					public void itemStateChanged(ItemEvent e) {
+						JCheckBox box = (JCheckBox) e.getSource();
+						if (e.getStateChange() == SELECTED)
+							localDependencies.add(box.getText());
+						else if (e.getStateChange() == DESELECTED)
+							localDependencies.remove(box.getText());
+						updateWarning();
+					}
+				});
+				panel.add(checkBox);
+				// The jar may not be in the lib directory, so warn the user
+				if (!new File(libDir, jarFile).exists()) {
+					checkBox.setForeground(RED);
+					checkBox.setText(checkBox.getText() + " (missing file!)");
+				}
+			}
+			return panel;
+		}
+	}
+
+	public static class FileExtFilter implements FilenameFilter {
+		final String ext;
+
+		public FileExtFilter(String ext) {
+			this.ext = ext;
+		}
+
+		@Override
+		public boolean accept(File dir, String name) {
+			return name.endsWith(ext);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListConfigurationComponent.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListConfigurationComponent.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListConfigurationComponent.java
new file mode 100644
index 0000000..c0e3aab
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListConfigurationComponent.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.ui.views.contextualviews.activity;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.EAST;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.FlowLayout.RIGHT;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+/**
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public abstract class ListConfigurationComponent<T> extends JPanel {
+	private static final String REMOVE = "Remove";
+	private static final String ADD = "Add";
+
+	private String name;
+	private List<T> items;
+	private JPanel listPanel;
+
+	public ListConfigurationComponent(String name, List<T> items) {
+		this.name = name;
+		setLayout(new BorderLayout());
+
+		listPanel = new JPanel(new ListLayout());
+		JPanel buttonPanel = new JPanel(new FlowLayout(RIGHT));
+		buttonPanel.add(new JButton(createAddAction()));
+
+		add(new JScrollPane(listPanel), CENTER);
+		add(buttonPanel, SOUTH);
+
+		setItems(items);
+	}
+
+	protected void setItems(List<T> items) {
+		this.items = items;
+		listPanel.removeAll();
+		for (T item : items)
+			addItemComponent(item);
+	}
+
+	protected void addItem(T item) {
+		items.add(item);
+		addItemComponent(item);
+	}
+
+	protected void addItemComponent(T item) {
+		JComponent itemPanel = new JPanel(new BorderLayout());
+		itemPanel.add(createItemComponent(item), CENTER);
+		itemPanel.add(new JButton(createRemoveAction(item)), EAST);
+		listPanel.add(itemPanel);
+		listPanel.revalidate();
+		listPanel.repaint();
+	}
+
+	protected void removeItem(T item) {
+		int index = items.indexOf(item);
+		if (index >= 0) {
+			items.remove(index);
+			listPanel.remove(index);
+			listPanel.revalidate();
+			listPanel.repaint();
+		}
+	}
+
+	private Action createRemoveAction(final T item) {
+		return new AbstractAction(REMOVE) {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				removeItem(item);
+			}
+		};
+	}
+
+	private Action createAddAction() {
+		return new AbstractAction(ADD + " " + name) {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				addItem(createDefaultItem());
+			}
+		};
+	}
+
+	protected abstract Component createItemComponent(T item);
+
+	protected abstract T createDefaultItem();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListLayout.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListLayout.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListLayout.java
new file mode 100644
index 0000000..0ce35b5
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListLayout.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.ui.views.contextualviews.activity;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+
+/**
+ * Lays out components vertically using their preferred height and the available
+ * width.
+ * 
+ * @author David Withers
+ */
+public class ListLayout implements LayoutManager {
+	private static final int DEFAULT_GAP = 5;
+	private final int gap;
+
+	public ListLayout() {
+		this(DEFAULT_GAP);
+	}
+
+	public ListLayout(int gap) {
+		this.gap = gap;
+	}
+
+	@Override
+	public void removeLayoutComponent(Component comp) {
+	}
+
+	@Override
+	public void addLayoutComponent(String name, Component comp) {
+	}
+
+	@Override
+	public void layoutContainer(Container parent) {
+		Insets insets = parent.getInsets();
+		int x = insets.left;
+		int y = insets.top;
+		int width = parent.getWidth() - insets.left - insets.right;
+		Component[] components = parent.getComponents();
+		for (int i = 0; i < components.length; i++) {
+			components[i].setLocation(x, y);
+			components[i].setSize(width,
+					components[i].getPreferredSize().height);
+			y = y + gap + components[i].getHeight();
+		}
+	}
+
+	@Override
+	public Dimension minimumLayoutSize(Container parent) {
+		Insets insets = parent.getInsets();
+		int minimumWidth = 0;
+		int minimumHeight = 0;
+		Component[] components = parent.getComponents();
+		for (int i = 0; i < components.length; i++) {
+			Dimension size = components[i].getPreferredSize();
+			if (size.width > minimumWidth)
+				minimumWidth = size.width;
+			minimumHeight = minimumHeight + size.height + gap;
+		}
+		minimumWidth = minimumWidth + insets.left + insets.right;
+		minimumHeight = minimumHeight + insets.top + insets.bottom;
+
+		return new Dimension(minimumWidth, minimumHeight);
+	}
+
+	@Override
+	public Dimension preferredLayoutSize(Container parent) {
+		return minimumLayoutSize(parent);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/MultiPageActivityConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/MultiPageActivityConfigurationPanel.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/MultiPageActivityConfigurationPanel.java
new file mode 100644
index 0000000..19cd180
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/MultiPageActivityConfigurationPanel.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.ui.views.contextualviews.activity;
+
+import static java.awt.BorderLayout.CENTER;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+
+import javax.swing.JTabbedPane;
+
+import uk.org.taverna.scufl2.api.activity.Activity;
+
+/**
+ * Component for configuring activities that have multiple configuration pages.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public abstract class MultiPageActivityConfigurationPanel extends
+		ActivityConfigurationPanel {
+	private JTabbedPane tabbedPane;
+
+	/**
+	 * Constructs a new <code>MultiPageActivityConfigurationPanel</code>.
+	 * 
+	 * @param activity
+	 */
+	public MultiPageActivityConfigurationPanel(Activity activity) {
+		super(activity);
+		setLayout(new BorderLayout());
+		tabbedPane = new JTabbedPane();
+		add(tabbedPane, CENTER);
+	}
+
+	public void addPage(String name, Component component) {
+		tabbedPane.addTab(name, component);
+	}
+
+	public void removePage(String name) {
+		tabbedPane.removeTabAt(tabbedPane.indexOfTab(name));
+	}
+
+	public void removeAllPages() {
+		tabbedPane.removeAll();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ScriptConfigurationComponent.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ScriptConfigurationComponent.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ScriptConfigurationComponent.java
new file mode 100644
index 0000000..8cb7652
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ScriptConfigurationComponent.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.ui.views.contextualviews.activity;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.SOUTH;
+import static java.awt.Color.WHITE;
+import static java.awt.Font.PLAIN;
+import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static net.sf.taverna.t2.lang.ui.FileTools.readStringFromFile;
+import static net.sf.taverna.t2.lang.ui.FileTools.saveStringToFile;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Set;
+
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JTextPane;
+
+import net.sf.taverna.t2.lang.ui.KeywordDocument;
+import net.sf.taverna.t2.lang.ui.LineEnabledTextPanel;
+import net.sf.taverna.t2.lang.ui.LinePainter;
+import net.sf.taverna.t2.lang.ui.NoWrapEditorKit;
+
+/**
+ * Component for configuring activities that have scripts.
+ *
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class ScriptConfigurationComponent extends JPanel {
+	private JTextPane scriptTextArea;
+
+	public ScriptConfigurationComponent(String script, Set<String> keywords,
+			Set<String> ports, final String scriptType,
+			final String fileExtension) {
+		this(script, keywords, ports, scriptType, fileExtension, "");
+	}
+
+	public ScriptConfigurationComponent(String script, Set<String> keywords,
+			Set<String> ports, final String scriptType,
+			final String fileExtension, final String resetScript) {
+		super(new BorderLayout());
+		scriptTextArea = new JTextPane();
+		new LinePainter(scriptTextArea, WHITE);
+
+		final KeywordDocument doc = new KeywordDocument(keywords, ports);
+
+		// NOTE: Due to T2-1145 - always set editor kit BEFORE setDocument
+		scriptTextArea.setEditorKit(new NoWrapEditorKit());
+		scriptTextArea.setFont(new Font("Monospaced", PLAIN, 14));
+		scriptTextArea.setDocument(doc);
+		scriptTextArea.setText(script);
+		scriptTextArea.setCaretPosition(0);
+		scriptTextArea.setPreferredSize(new Dimension(200, 100));
+
+		add(new LineEnabledTextPanel(scriptTextArea), CENTER);
+
+		final JButton checkScriptButton = new JButton("Check script");
+		checkScriptButton.setToolTipText("Check the " + scriptType + " script");
+		checkScriptButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent ex) {
+				showMessageDialog(ScriptConfigurationComponent.this, scriptType
+						+ " script check not implemented", scriptType
+						+ " script check", INFORMATION_MESSAGE);
+			}
+		});
+
+		JButton loadScriptButton = new JButton("Load script");
+		loadScriptButton.setToolTipText("Load a " + scriptType
+				+ " script from a file");
+		loadScriptButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				String newScript = readStringFromFile(
+						ScriptConfigurationComponent.this, "Load " + scriptType
+								+ " script", fileExtension);
+				if (newScript != null) {
+					scriptTextArea.setText(newScript);
+					scriptTextArea.setCaretPosition(0);
+				}
+			}
+		});
+
+		JButton saveRScriptButton = new JButton("Save script");
+		saveRScriptButton.setToolTipText("Save the " + scriptType
+				+ " script to a file");
+		saveRScriptButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				saveStringToFile(ScriptConfigurationComponent.this, "Save "
+						+ scriptType + " script", fileExtension,
+						scriptTextArea.getText());
+			}
+		});
+
+		JButton clearScriptButton = new JButton("Clear script");
+		clearScriptButton.setToolTipText("Clear current script from the edit area");
+		clearScriptButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				if (showConfirmDialog(ScriptConfigurationComponent.this,
+						"Do you really want to clear the script?",
+						"Clearing the script", YES_NO_OPTION) == YES_OPTION)
+					scriptTextArea.setText(resetScript);
+			}
+		});
+
+		JPanel buttonPanel = new JPanel();
+		buttonPanel.setLayout(new FlowLayout());
+		buttonPanel.add(checkScriptButton);
+		buttonPanel.add(loadScriptButton);
+		buttonPanel.add(saveRScriptButton);
+		buttonPanel.add(clearScriptButton);
+
+		add(buttonPanel, SOUTH);
+	}
+
+	public String getScript() {
+		return scriptTextArea.getText();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextField.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextField.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextField.java
new file mode 100644
index 0000000..cf1ff96
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextField.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.ui.views.contextualviews.activity;
+
+import javax.swing.JTextField;
+
+/**
+ * Adds a "<tt>valid</tt>" property to a JTextField.
+ * 
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class ValidatingTextField extends JTextField {
+	private boolean valid = true;
+
+	public ValidatingTextField() {
+	}
+
+	public ValidatingTextField(String text) {
+		super(text);
+	}
+
+	@Override
+	public boolean isValid() {
+		return valid;
+	}
+
+	public void setValid(boolean valid) {
+		if (this.valid != valid) {
+			boolean old = this.valid;
+			this.valid = valid;
+			firePropertyChange("valid", old, valid);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextGroup.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextGroup.java b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextGroup.java
new file mode 100644
index 0000000..7597f7c
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextGroup.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.ui.views.contextualviews.activity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+/**
+ *
+ *
+ * @author David Withers
+ */
+public class ValidatingTextGroup {
+	private Map<ValidatingTextField, DocumentListener> textComponents;
+
+	public ValidatingTextGroup() {
+		textComponents = new HashMap<>();
+	}
+
+	public void addValidTextComponent(ValidatingTextField textComponent) {
+		setUniqueText(textComponent);
+		DocumentListener documentListener = new ValidatorDocumentListener();
+		textComponent.getDocument().addDocumentListener(documentListener);
+		textComponents.put(textComponent, documentListener);
+	}
+
+	public void addTextComponent(ValidatingTextField textComponent) {
+		DocumentListener documentListener = new ValidatorDocumentListener();
+		textComponent.getDocument().addDocumentListener(documentListener);
+		textComponents.put(textComponent, documentListener);
+		validate();
+	}
+
+	public void removeTextComponent(ValidatingTextField textComponent) {
+		textComponent.getDocument().removeDocumentListener(
+				textComponents.remove(textComponent));
+		validate();
+	}
+
+	private void setUniqueText(ValidatingTextField textComponent) {
+		String text = textComponent.getText();
+		if (textExists(text)) {
+			// Remove any existing number suffix
+			String nameTemplate = text.replaceAll("_\\d+$", "_");
+			long i = 1;
+			do {
+				text = nameTemplate + i++;
+			} while (textExists(text));
+
+			textComponent.setText(text);
+		}
+	}
+
+	private void validate() {
+		Map<String, ValidatingTextField> textValues = new HashMap<>();
+		Set<ValidatingTextField> maybeValid = new HashSet<>();
+		for (ValidatingTextField textComponent : textComponents.keySet()) {
+			ValidatingTextField duplicate = textValues.get(textComponent
+					.getText());
+			if (duplicate != null) {
+				duplicate.setValid(false);
+				maybeValid.remove(duplicate);
+				textComponent.setValid(false);
+			} else {
+				textValues.put(textComponent.getText(), textComponent);
+				maybeValid.add(textComponent);
+			}
+		}
+		for (ValidatingTextField textComponent : maybeValid)
+			textComponent.setValid(true);
+	}
+
+	private boolean textExists(String text) {
+		for (ValidatingTextField currentTextComponent : textComponents.keySet())
+			if (text.equals(currentTextComponent.getText()))
+				return true;
+		return false;
+	}
+
+	class ValidatorDocumentListener implements DocumentListener {
+		@Override
+		public void insertUpdate(DocumentEvent e) {
+			validate();
+		}
+
+		@Override
+		public void removeUpdate(DocumentEvent e) {
+			validate();
+		}
+
+		@Override
+		public void changedUpdate(DocumentEvent e) {
+			validate();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI b/taverna-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
new file mode 100644
index 0000000..312f95b
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
@@ -0,0 +1,2 @@
+#net.sf.taverna.t2.workbench.ui.actions.activity.draggable.ActivityDraggerPaletteComponentFactory
+#net.sf.taverna.t2.workbench.ui.views.contextualviews.DragActivitiesToHereComponentFactory

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI b/taverna-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
new file mode 100644
index 0000000..1448a49
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
@@ -0,0 +1,3 @@
+#net.sf.taverna.t2.workbench.ui.actions.activity.draggable.ActivityDraggerPaletteComponent
+#net.sf.taverna.t2.workbench.ui.views.contextualviews.DragActivitiesToHereComponent
+net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualViewComponent

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context-osgi.xml b/taverna-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context-osgi.xml
new file mode 100644
index 0000000..ab22b97
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context-osgi.xml
@@ -0,0 +1,9 @@
+<?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">
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context.xml
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context.xml b/taverna-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context.xml
new file mode 100644
index 0000000..d662d87
--- /dev/null
+++ b/taverna-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context.xml
@@ -0,0 +1,6 @@
+<?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">
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/pom.xml b/taverna-contextual-views-impl/pom.xml
new file mode 100644
index 0000000..c0398e9
--- /dev/null
+++ b/taverna-contextual-views-impl/pom.xml
@@ -0,0 +1,54 @@
+<?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>taverna-contextual-views-impl</artifactId>
+	<packaging>bundle</packaging>
+	<name>Apache Taverna Contextual Views Impl</name>
+	<description>Contextual views for the activities</description>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-contextual-views-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>taverna-selection-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.taverna.language</groupId>
+			<artifactId>taverna-scufl2-api</artifactId>
+			<version>${taverna.language.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>javax.help</groupId>
+			<artifactId>javahelp</artifactId>
+			<version>${javahelp.version}</version>
+		</dependency>
+
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/impl/ContextualViewFactoryRegistryImpl.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/impl/ContextualViewFactoryRegistryImpl.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/impl/ContextualViewFactoryRegistryImpl.java
new file mode 100644
index 0000000..8bac354
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/impl/ContextualViewFactoryRegistryImpl.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+/**
+ * @author Alan R Williams
+ */
+package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry;
+
+/**
+ * An SPI registry for discovering ActivityViewFactories for a given object,
+ * like an {@link net.sf.taverna.t2.workflowmodel.processor.activity.Activity}.
+ * <p>
+ * For {@link ContextualViewFactory factories} to be found, its full qualified
+ * name needs to be defined as a resource file
+ * <code>/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualViewFactory</code>
+ * 
+ * @author Stuart Owen
+ * @author Ian Dunlop
+ * @author Stian Soiland-Reyes
+ * 
+ * @see ContextualViewFactory
+ */
+public class ContextualViewFactoryRegistryImpl implements
+		ContextualViewFactoryRegistry {
+	private List<ContextualViewFactory<?>> contextualViewFactories;
+
+	/**
+	 * Discover and return the ContextualViewFactory associated to the provided
+	 * object. This is accomplished by returning the discovered
+	 * {@link ContextualViewFactory#canHandle(Object)} that returns true for
+	 * that Object.
+	 * 
+	 * @param object
+	 * @return
+	 * @see ContextualViewFactory#canHandle(Object)
+	 */
+	@Override
+	public List<ContextualViewFactory<?>> getViewFactoriesForObject(
+			Object object) {
+		List<ContextualViewFactory<?>> result = new ArrayList<>();
+		for (ContextualViewFactory<?> factory : contextualViewFactories)
+			if (factory.canHandle(object))
+				result.add(factory);
+		return result;
+	}
+
+	public void setContextualViewFactories(
+			List<ContextualViewFactory<?>> contextualViewFactories) {
+		this.contextualViewFactories = contextualViewFactories;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualView.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualView.java
new file mode 100644
index 0000000..018a121
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualView.java
@@ -0,0 +1,263 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.annotated;
+
+import static javax.swing.BoxLayout.Y_AXIS;
+
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.PropertyResourceBundle;
+import java.util.ResourceBundle;
+import java.util.regex.Pattern;
+
+import javax.swing.BoxLayout;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.TitledBorder;
+
+import net.sf.taverna.t2.annotation.Annotated;
+import net.sf.taverna.t2.annotation.AnnotationBeanSPI;
+import net.sf.taverna.t2.lang.ui.DialogTextArea;
+import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+import net.sf.taverna.t2.workbench.edits.Edit;
+import net.sf.taverna.t2.workbench.edits.EditException;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.impl.ContextualViewComponent;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * This is a ContextualView that should be able to display and allow editing of
+ * Annotation information for any Annotated. At the moment it is only used for
+ * Dataflow.
+ * 
+ * @author Alan R Williams
+ */
+@SuppressWarnings("serial")
+class AnnotatedContextualView extends ContextualView {
+	private static final int WORKFLOW_NAME_LENGTH = 20;
+	public static final String VIEW_TITLE = "Annotations";
+	private final static String MISSING_VALUE = "Type here to give details";
+	private final static int DEFAULT_AREA_WIDTH = 60;
+	private final static int DEFAULT_AREA_ROWS = 8;
+
+	private static Logger logger = Logger
+			.getLogger(AnnotatedContextualView.class);
+	private static PropertyResourceBundle prb = (PropertyResourceBundle) ResourceBundle
+			.getBundle("annotatedcontextualview");
+
+	// TODO convert to scufl2
+	// private static AnnotationTools annotationTools = new AnnotationTools();
+
+	/**
+	 * The object to which the Annotations apply
+	 */
+	private Annotated<?> annotated;
+	private SelectionManager selectionManager;
+	private EditManager editManager;
+	private boolean isStandalone = false;
+	private JPanel panel;
+	@SuppressWarnings("unused")
+	private final List<AnnotationBeanSPI> annotationBeans;
+
+	public AnnotatedContextualView(Annotated<?> annotated,
+			EditManager editManager, SelectionManager selectionManager,
+			List<AnnotationBeanSPI> annotationBeans) {
+		super();
+		this.editManager = editManager;
+		this.selectionManager = selectionManager;
+		this.annotationBeans = annotationBeans;
+		this.annotated = annotated;
+
+		initialise();
+		initView();
+	}
+
+	@Override
+	public void refreshView() {
+		initialise();
+	}
+
+	private void initialise() {
+		if (panel == null) {
+			panel = new JPanel();
+			panel.setLayout(new BoxLayout(panel, Y_AXIS));
+		} else
+			panel.removeAll();
+		populatePanel();
+		revalidate();
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		return panel;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return VIEW_TITLE;
+	}
+
+	private Map<String,String> getAnnotations() {
+		// TODO convert to scufl2
+		Map<String, String> result = new HashMap<>();
+		//for (Class<?> c : annotationTools.getAnnotatingClasses(annotated)) {
+		// String name = "";
+		// try {
+		// name = prb.getString(c.getCanonicalName());
+		// } catch (MissingResourceException e) {
+		// name = c.getCanonicalName();
+		// }
+		// String value = annotationTools.getAnnotationString(annotated, c,
+		// MISSING_VALUE);
+		// result.put(name,value);
+		//}
+		return result;
+	}
+	public void populatePanel() {
+		JPanel scrollPanel = new JPanel();
+		scrollPanel.setLayout(new BoxLayout(scrollPanel, Y_AXIS));
+		panel.setBorder(new EmptyBorder(5, 5, 5, 5));
+		Map<String,String>annotations = getAnnotations();
+		for (String name : annotations.keySet()) {
+			JPanel subPanel = new JPanel();
+			subPanel.setBorder(new TitledBorder(name));
+			subPanel.add(createTextArea(String.class, annotations.get(name)));
+			scrollPanel.add(subPanel);
+		}
+		JScrollPane scrollPane = new JScrollPane(scrollPanel);
+		panel.add(scrollPane);
+	}
+
+	private JScrollPane createTextArea(Class<?> c, String value) {
+		DialogTextArea area = new DialogTextArea(value);
+		area.setFocusable(true);
+		area.addFocusListener(new TextAreaFocusListener(area, c));
+		area.setColumns(DEFAULT_AREA_WIDTH);
+		area.setRows(DEFAULT_AREA_ROWS);
+		area.setLineWrap(true);
+		area.setWrapStyleWord(true);
+
+		return new JScrollPane(area);
+	}
+
+	private class TextAreaFocusListener implements FocusListener {
+		String oldValue = null;
+		Class<?> annotationClass;
+		DialogTextArea area = null;
+
+		public TextAreaFocusListener(DialogTextArea area, Class<?> c) {
+			annotationClass = c;
+			oldValue = area.getText();
+			this.area = area;
+		}
+
+		@Override
+		public void focusGained(FocusEvent e) {
+			if (area.getText().equals(MISSING_VALUE))
+				area.setText("");
+		}
+
+		@Override
+		public void focusLost(FocusEvent e) {
+			String currentValue = area.getText();
+			if (currentValue.isEmpty() || currentValue.equals(MISSING_VALUE)) {
+				currentValue = MISSING_VALUE;
+				area.setText(currentValue);
+			}
+			if (!currentValue.equals(oldValue)) {
+				if (currentValue == MISSING_VALUE)
+					currentValue = "";
+				try {
+					WorkflowBundle currentDataflow = selectionManager
+							.getSelectedWorkflowBundle();
+					List<Edit<?>> editList = new ArrayList<>();
+					addWorkflowNameEdits(currentValue, currentDataflow,
+							editList);
+					if (!isStandalone)
+						ContextualViewComponent.selfGenerated = true;
+					editManager.doDataflowEdit(currentDataflow,
+							new CompoundEdit(editList));
+					ContextualViewComponent.selfGenerated = false;
+				} catch (EditException e1) {
+					logger.warn("Can't set annotation", e1);
+				}
+				oldValue = area.getText();
+			}
+		}
+
+		private boolean isTitleAnnotation() {
+			// TODO convert to scufl2
+			return prb.getString(annotationClass.getCanonicalName()).equals(
+					"Title");
+		}
+
+		// TODO convert to scufl2
+		private void addWorkflowNameEdits(String currentValue,
+				WorkflowBundle currentDataflow, List<Edit<?>> editList) {
+			//editList.add(annotationTools.setAnnotationString(annotated,
+			//		annotationClass, currentValue, edits));
+			if (annotated == currentDataflow && isTitleAnnotation()
+					&& !currentValue.isEmpty()) {
+				@SuppressWarnings("unused")
+				String sanitised = sanitiseName(currentValue);
+				//editList.add(edits.getUpdateDataflowNameEdit(currentDataflow,
+				//		sanitised));
+			}
+		}
+	}
+
+	/**
+	 * Checks that the name does not have any characters that are invalid for a
+	 * processor name.
+	 * <p>
+	 * The resulting name must contain only the chars [A-Za-z_0-9].
+	 * 
+	 * @param name
+	 *            the original name
+	 * @return the sanitised name
+	 */
+	private static String sanitiseName(String name) {
+		if (name.length() > WORKFLOW_NAME_LENGTH)
+			name = name.substring(0, WORKFLOW_NAME_LENGTH);
+		if (Pattern.matches("\\w++", name))
+			return name;
+		StringBuilder temp = new StringBuilder();
+		for (char c : name.toCharArray())
+			temp.append(Character.isLetterOrDigit(c) || c == '_' ? c : '_');
+		return temp.toString();
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 500;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualViewFactory.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualViewFactory.java
new file mode 100644
index 0000000..eb18803
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualViewFactory.java
@@ -0,0 +1,43 @@
+package net.sf.taverna.t2.workbench.ui.views.contextualviews.annotated;
+
+import static java.util.Collections.singletonList;
+
+import java.util.List;
+
+import net.sf.taverna.t2.annotation.Annotated;
+import net.sf.taverna.t2.annotation.AnnotationBeanSPI;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import net.sf.taverna.t2.workflowmodel.processor.activity.Activity;
+
+public class AnnotatedContextualViewFactory implements
+		ContextualViewFactory<Annotated<?>> {
+	private EditManager editManager;
+	private List<AnnotationBeanSPI> annotationBeans;
+	private SelectionManager selectionManager;
+
+	@Override
+	public boolean canHandle(Object selection) {
+		return ((selection instanceof Annotated) && !(selection instanceof Activity));
+	}
+
+	@Override
+	public List<ContextualView> getViews(Annotated<?> selection) {
+		return singletonList((ContextualView) new AnnotatedContextualView(
+				selection, editManager, selectionManager, annotationBeans));
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setAnnotationBeans(List<AnnotationBeanSPI> annotationBeans) {
+		this.annotationBeans = annotationBeans;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualView.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualView.java
new file mode 100644
index 0000000..f9308b5
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualView.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.condition;
+
+import java.awt.FlowLayout;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import uk.org.taverna.scufl2.api.core.BlockingControlLink;
+
+/**
+ * Contextual view for dataflow's control (condition) links.
+ * 
+ * @author David Withers
+ */
+class ConditionContextualView extends ContextualView {
+	private static final long serialVersionUID = -894521200616176439L;
+
+	private final BlockingControlLink condition;
+	private JPanel contitionView;
+
+	public ConditionContextualView(BlockingControlLink condition) {
+		this.condition = condition;
+		initView();
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		refreshView();
+		return contitionView;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Control link: " + condition.getBlock().getName()
+				+ " runs after " + condition.getUntilFinished().getName();
+	}
+
+	@Override
+	public void refreshView() {
+		contitionView = new JPanel(new FlowLayout(FlowLayout.LEFT));
+		contitionView.setBorder(new EmptyBorder(5, 5, 5, 5));
+		JLabel label = new JLabel(
+				"<html><body><i>No details available.</i></body><html>");
+		contitionView.add(label);
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 100;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualViewFactory.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualViewFactory.java
new file mode 100644
index 0000000..ea69f1a
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualViewFactory.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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.ui.views.contextualviews.condition;
+
+import static java.util.Arrays.asList;
+
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import net.sf.taverna.t2.workflowmodel.Condition;
+import uk.org.taverna.scufl2.api.core.BlockingControlLink;
+
+/**
+ * A factory of contextual views for dataflow's condition links.
+ * 
+ * @author David Withers
+ * 
+ */
+public class ConditionContextualViewFactory implements
+		ContextualViewFactory<BlockingControlLink> {
+	@Override
+	public boolean canHandle(Object object) {
+		return object instanceof Condition;
+	}
+
+	@Override
+	public List<ContextualView> getViews(BlockingControlLink condition) {
+		return asList(new ContextualView[] { new ConditionContextualView(
+				condition) });
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualView.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualView.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualView.java
new file mode 100644
index 0000000..4a63868
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualView.java
@@ -0,0 +1,108 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflow;
+
+import static net.sf.taverna.t2.lang.ui.HtmlUtils.buildTableOpeningTag;
+import static net.sf.taverna.t2.lang.ui.HtmlUtils.createEditorPane;
+import static net.sf.taverna.t2.lang.ui.HtmlUtils.getHtmlHead;
+import static net.sf.taverna.t2.lang.ui.HtmlUtils.panelForHtml;
+
+import javax.swing.JComponent;
+import javax.swing.JEditorPane;
+
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workflowmodel.Dataflow;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
+
+/**
+ * @author alanrw
+ */
+@SuppressWarnings("serial")
+class DataflowContextualView extends ContextualView {
+	private static int MAX_LENGTH = 50;
+	private static final String ELLIPSIS = "...";
+
+	private Workflow dataflow;
+	private JEditorPane editorPane;
+	private final FileManager fileManager;
+	private final ColourManager colourManager;
+
+	public DataflowContextualView(Workflow dataflow, FileManager fileManager,
+			ColourManager colourManager) {
+		this.dataflow = dataflow;
+		this.fileManager = fileManager;
+		this.colourManager = colourManager;
+		initView();
+	}
+
+	@Override
+	public JComponent getMainFrame() {
+		editorPane = createEditorPane(buildHtml());
+		return panelForHtml(editorPane);
+	}
+
+	private String buildHtml() {
+		StringBuilder html = new StringBuilder(getHtmlHead(getBackgroundColour()));
+		html.append(buildTableOpeningTag());
+
+		html.append("<tr><td colspan=\"2\" align=\"center\"><b>Source</b></td></tr>");
+		String source = "Newly created";
+		if (fileManager.getDataflowSource(dataflow.getParent()) != null)
+			source = fileManager.getDataflowName(dataflow.getParent());
+
+		html.append("<tr><td colspan=\"2\" align=\"center\">").append(source)
+				.append("</td></tr>");
+		if (!dataflow.getInputPorts().isEmpty()) {
+			html.append("<tr><th>Input Port Name</th><th>Depth</th></tr>");
+			for (InputWorkflowPort dip : dataflow.getInputPorts())
+				html.append("<tr><td>")
+						.append(dip.getName())
+						.append("</td><td>")
+						.append(dip.getDepth() < 0 ? "invalid/unpredicted"
+								: dip.getDepth()).append("</td></tr>");
+		}
+		if (!dataflow.getOutputPorts().isEmpty()) {
+			html.append("<tr><th>Output Port Name</th><th>Depth</th></tr>");
+			for (OutputWorkflowPort dop : dataflow.getOutputPorts())
+				html.append("<tr><td>")
+						.append(dop.getName())
+						.append("</td><td>")
+						.append(/*(dop.getDepth() < 0 ?*/ "invalid/unpredicted" /*: dop.getDepth())*/)
+						.append("</td>" + "</tr>");
+		}
+
+		return html.append("</table>").append("</body></html>").toString();
+	}
+
+	public String getBackgroundColour() {
+		return colourManager.getDefaultPropertyMap().get(
+				Dataflow.class.toString());
+	}
+
+	@Override
+	public int getPreferredPosition() {
+		return 100;
+	}
+
+	private String limitName(String fullName) {
+		if (fullName.length() <= MAX_LENGTH)
+			return fullName;
+		return fullName.substring(0, MAX_LENGTH - ELLIPSIS.length()) + ELLIPSIS;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Workflow " + limitName(dataflow.getName());
+	}
+
+	@Override
+	public void refreshView() {
+		editorPane.setText(buildHtml());
+		repaint();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualViewFactory.java b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualViewFactory.java
new file mode 100644
index 0000000..0d7f3c0
--- /dev/null
+++ b/taverna-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualViewFactory.java
@@ -0,0 +1,41 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflow;
+
+import java.util.Arrays;
+import java.util.List;
+
+import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
+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;
+import uk.org.taverna.scufl2.api.core.Workflow;
+
+/**
+ * @author alanrw
+ */
+public class DataflowContextualViewFactory implements
+		ContextualViewFactory<Workflow> {
+	private FileManager fileManager;
+	private ColourManager colourManager;
+
+	@Override
+	public boolean canHandle(Object selection) {
+		return selection instanceof Workflow;
+	}
+
+	@Override
+	public List<ContextualView> getViews(Workflow selection) {
+		return Arrays.asList(new ContextualView[] {
+				new DataflowContextualView(selection, fileManager, colourManager)});
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+	}
+
+	public void setColourManager(ColourManager colourManager) {
+		this.colourManager = colourManager;
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuComponent.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuComponent.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuComponent.java
new file mode 100644
index 0000000..5bc91c4
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuComponent.java
@@ -0,0 +1,277 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.swing.Action;
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBox;
+import javax.swing.JMenu;
+import javax.swing.JToolBar;
+import javax.swing.MenuElement;
+
+/**
+ * A menu component, including sub menus, toolbars, and menu items.
+ * <p>
+ * This is an {@link net.sf.taverna.t2.spi.SPIRegistry SPI}, and implementations
+ * should list their fully qualified classnames in
+ * <tt>META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</tt> to be
+ * discovered by the {@link MenuManager}.
+ * 
+ * @author Stian Soiland-Reyes
+ * @author David Withers
+ */
+public interface MenuComponent {
+	/**
+	 * The {@link Action} describing this menu item, used for creating the UI
+	 * representation of this item.
+	 * <p>
+	 * As a minimum the action should contain a name, and optionally an icon, a
+	 * description and a keyboard shortcut. For {@linkplain MenuType#action
+	 * actions} and {@linkplain MenuType#toggle toggles} the {@link Action}'s
+	 * {@link ActionListener#actionPerformed(ActionEvent)} method is called when
+	 * the item is clicked/selected.
+	 * <p>
+	 * This action is ignored and should be <code>null</code> for items of type
+	 * {@link MenuType#optionGroup} and {@link MenuType#custom}. The action is
+	 * optional for {@linkplain MenuType#toolBar toolbars} and
+	 * {@linkplain MenuType#section sections}, where the action's name would be
+	 * used as a label.
+	 * 
+	 * @return The {@link Action} describing this menu item, or
+	 *         <code>null</code> if the {@link #getType()} is
+	 *         {@link MenuType#section}, {@link MenuType#optionGroup} or
+	 *         {@link MenuType#custom}.
+	 */
+	public Action getAction();
+
+	/**
+	 * Get a custom {@link Component} to be inserted into the parent
+	 * menu/toolbar.
+	 * <p>
+	 * Used instead of creating menu elements from the {@link #getAction()} if
+	 * the {@link #getType()} is {@link MenuType#custom}. This can be used to
+	 * include dynamic menus.
+	 * <p>
+	 * This value is ignored and should be <code>null</code> for all other types
+	 * except {@link MenuType#custom}.
+	 * 
+	 * @return A {@link Component} to be inserted into the parent menu/toolbar.
+	 */
+	public Component getCustomComponent();
+
+	/**
+	 * The {@link URI} to identify this menu item.
+	 * <p>
+	 * This identifier can be used with other menu item's {@link #getParentId()}
+	 * if this item has a {@link #getType()} of {@link MenuType#menu},
+	 * {@link MenuType#toolBar}, {@link MenuType#section} or
+	 * {@link MenuType#optionGroup}.
+	 * <p>
+	 * Leaf menu items of {@link #getType()} {@link MenuType#toggle},
+	 * {@link MenuType#custom} and {@link MenuType#action} don't need an
+	 * identifier as they can't have children, and may return <code>null</code>
+	 * instead. However, a valid identifier might be used to look up the
+	 * MenuItem with {@link MenuManager#getComponentByURI(URI)}
+	 * <p>
+	 * <strong>Note:</strong> To avoid conflicts with other plugins, use a
+	 * unique URI root that is related to the Java package name, for instance
+	 * <code>http://cs.university.ac.uk/myplugin/2008/menu</code>, and use hash
+	 * identifiers for each menu item, for instance
+	 * <code>http://cs.university.ac.uk/myplugin/2008/menu#run</code> for a
+	 * "Run" item. Use flat URI namespaces, don't base a child's URI on the
+	 * parent's URI, as this might make it difficult to relocate the parent
+	 * menu.
+	 * 
+	 * @return The {@link URI} to identify this menu item.
+	 */
+	public URI getId();
+
+	/**
+	 * The {@link URI} of the parent menu item, as returned by the parent's
+	 * {@link #getId()}.
+	 * <p>
+	 * If this is the {@link DefaultMenuBar#DEFAULT_MENU_BAR}, then this menu
+	 * item will be one of the top level menus of the main application window,
+	 * like "File" or "Edit", and must have {@link #getType()}
+	 * {@link MenuType#menu}.
+	 * <p>
+	 * This value should be <code>null</code> if this item is of
+	 * {@link #getType()} {@link MenuType#toolBar}, and could be
+	 * <code>null</code> if this is an independent root menu of type
+	 * {@link MenuType#menu} (to be used outside the main window).
+	 * <p>
+	 * <strong>Note:</strong> To avoid compile time and runtime dependency on
+	 * the parent menu item, always construct this URI directly using
+	 * {@link URI#create(String)}.
+	 * 
+	 * @return The {@link URI} of the parent menu item.
+	 */
+	public URI getParentId();
+
+	/**
+	 * A hint on how to position this item below the parent.
+	 * <p>
+	 * Menu items within the same parent menu/group/toolBar are ordered
+	 * according to this position hint. If several items have the same position
+	 * hint, their internal order is undefined, although generally it will be
+	 * the order in which they were loaded.
+	 * <p>
+	 * <strong>Tip:</strong> Number the position hints in BASIC style, such as
+	 * 10, 20, etc. so that plugins can use position hint such as 19 or 21 to be
+	 * immediately before or after your item.
+	 * 
+	 * @return A position hint
+	 */
+	public int getPositionHint();
+
+	/**
+	 * The {@link MenuType type} of menu item.
+	 * <p>
+	 * In the simple case of a "File -> New" menu structure, the "File" menu
+	 * item has a type of {@link MenuType#menu}, while the "New" has a type of
+	 * {@link MenuType#action}.
+	 * <p>
+	 * The menu item can only have children (i.e., items with
+	 * {@link #getParentId()} equalling to this item's {@link #getId()}) if the
+	 * type is not a leaf type, i.e., not {@link MenuType#toggle} or
+	 * {@link MenuType#action}.
+	 * 
+	 * @return A {@link MenuType} to specify the role of this menu item.
+	 */
+	public MenuType getType();
+
+	/**
+	 * True if this menu component is to be included in the menu/toolbar.
+	 * 
+	 * @return True is this menu component is to be included
+	 */
+	public boolean isEnabled();
+
+	/**
+	 * The type of menu item, such as {@link #action}, {@link #menu} or
+	 * {@link #toolBar}.
+	 * <p>
+	 * Some types are {@linkplain #isParentType() parent types} - that means
+	 * URIs to menu components of that type can be used as a
+	 * {@linkplain MenuComponent#getParentId() parent id}.
+	 * 
+	 * @author Stian Soiland-Reyes
+	 * 
+	 */
+	public static enum MenuType {
+		/**
+		 * A normal {@link Action} as part of a {@link #menu}, {@link #toolBar},
+		 * {@link #section} or {@link #optionGroup}. Such menu items are leaf
+		 * nodes, which no {@link MenuComponent}s can have this as it's
+		 * {@link MenuComponent#getParentId()}. The action's
+		 * {@link ActionListener#actionPerformed(ActionEvent)} will be called
+		 * when choosing/clicking the menu item from the menu or toolBar.
+		 */
+		action,
+		/**
+		 * Provide a customised {@link MenuElement} from
+		 * {@link MenuComponent#getCustomComponent()} that is to be used instead
+		 * of creating an element from {@link MenuComponent#getAction()}.
+		 */
+		custom,
+		/**
+		 * A group containing mutually exclusive choices (as {@link #action}s),
+		 * to be grouped in a {@link ButtonGroup}, separated using
+		 * {@link JMenu#addSeparator()} or {@link JToolBar#addSeparator()} when
+		 * needed. The {@link MenuComponent#getAction()} is ignored and should
+		 * be <code>null</code>.
+		 */
+		optionGroup,
+		/**
+		 * A section of menu items within {@link #menu} or {@link #toolBar}.
+		 * Sections are separated using {@link JMenu#addSeparator()} or
+		 * {@link JToolBar#addSeparator()} when needed. The
+		 * {@link MenuComponent#getAction()} is ignored and should be
+		 * <code>null</code>.
+		 */
+		section,
+		/**
+		 * A (sub)menu that contain other menu items, including deeper
+		 * {@link #menu}s. The {@link Action} from
+		 * {@link MenuComponent#getAction()} is used to find the name, icon,
+		 * etc., for the sub-menu, while its
+		 * {@link ActionListener#actionPerformed(ActionEvent)} method is
+		 * ignored. The {@link DefaultMenuBar} is the default top level menu,
+		 * although others can be created with <code>null</code> as their
+		 * parent.
+		 */
+		menu,
+		/**
+		 * A boolean toggle action, the action will be shown as a
+		 * {@link JCheckBox} on a menu or toolBar. Such menu items are leaf
+		 * nodes, which no {@link MenuComponent}s can have this as it's
+		 * {@link MenuComponent#getParentId()}. The action's
+		 * {@link ActionListener#actionPerformed(ActionEvent)} will be called
+		 * when toggling the action.
+		 */
+		toggle,
+		/**
+		 * A toolBar containing {@link #optionGroup}s, {@link #toggle}s or
+		 * {@link #action}s. The toolBar can be shown as a {@link JToolBar}. The
+		 * {@link MenuComponent#getAction()} and
+		 * {@link MenuComponent#getParentId()} are ignored and should be
+		 * <code>null</code>.
+		 */
+		toolBar;
+
+		private static final Set<MenuType> parentTypes = defineParentTypes();
+
+		/**
+		 * True if the menu type is a parent type such as {@link #optionGroup},
+		 * {@link #section}, {@link #menu} or {@link #toolBar}. If the type of a
+		 * menu component is a a parent type it can (should) have children,
+		 * i.e., the children has a {@link MenuComponent#getParentId()} that
+		 * equals the parent's {@link MenuComponent#getId()}.
+		 * 
+		 * @return True if the menu type is a parent type.
+		 */
+		public boolean isParentType() {
+			return parentTypes.contains(this);
+		}
+
+		/**
+		 * Create the set of {@link MenuType}s that {@link #isParentType()}
+		 * would return <code>true</code> for.
+		 * 
+		 * @return A {@link Set} of {@link MenuType}s.
+		 */
+		private static Set<MenuType> defineParentTypes() {
+			HashSet<MenuType> types = new HashSet<>();
+			types.add(optionGroup);
+			types.add(section);
+			types.add(menu);
+			types.add(toolBar);
+			return types;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuManager.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuManager.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuManager.java
new file mode 100644
index 0000000..cca1bf0
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuManager.java
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * 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.ui.menu;
+
+import java.awt.Component;
+import java.lang.ref.WeakReference;
+import java.net.URI;
+import java.util.List;
+
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JToolBar;
+
+import uk.org.taverna.scufl2.api.core.Workflow;
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.ui.menu.MenuComponent.MenuType;
+import net.sf.taverna.t2.ui.menu.MenuManager.MenuManagerEvent;
+
+/**
+ * Create {@link JMenuBar}s and {@link JToolBar}s based on SPI instances of
+ * {@link MenuComponent}.
+ * <p>
+ * Elements of menus are discovered automatically using an {@link SPIRegistry}.
+ * The elements specify their internal relationship through
+ * {@link MenuComponent#getParentId()} and
+ * {@link MenuComponent#getPositionHint()}. {@link MenuComponent#getType()}
+ * specifies how the component is to be rendered or grouped.
+ * <p>
+ * The menu manager is {@link Observable}, you can
+ * {@linkplain #addObserver(Observer) add an observer} to be notified when the
+ * menus have changed, i.e. when {@link #update()} has been called, for instance
+ * when the {@link SPIRegistry} (which the menu manager observes) has been
+ * updated due to a plugin installation.
+ * <p>
+ * {@link #createMenuBar()} creates the default menu bar, ie. the menu bar
+ * containing all the items with {@link DefaultMenuBar#DEFAULT_MENU_BAR} as
+ * their parent. Alternate menu bars can be created using
+ * {@link #createMenuBar(URI)}.
+ * <p>
+ * Similary {@link #createToolBar()} creates the default tool bar, containing
+ * the items that has {@link DefaultToolBar#DEFAULT_TOOL_BAR} as their parent.
+ * Alternate toolbars can be created using {@link #createToolBar(URI)}.
+ * <p>
+ * The menu manager keeps weak references to the created (published) menu bars
+ * and tool bars, and will attempt to update them when {@link #update()} is
+ * called.
+ * <p>
+ * See the package level documentation for more information about how to specify
+ * menu elements.
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public interface MenuManager extends Observable<MenuManagerEvent> {
+	/**
+	 * Add the items from the list of menu items to the parent menu with
+	 * expansion sub-menus if needed.
+	 * <p>
+	 * If the list contains more than <tt>maxItemsInMenu</tt> items, a series of
+	 * sub-menus will be created and added to the parentMenu instead, each
+	 * containing a maximum of <tt>maxItemsInMenu</tt> items. (Note that if
+	 * menuItems contains more than <tt>maxItemsInMenu*maxItemsInMenu</tt>
+	 * items, there might be more than <tt>maxItemsInMenu</tt> sub-menus added
+	 * to the parent).
+	 * <p>
+	 * The sub-menus are titled according to the {@link JMenuItem#getText()} of
+	 * the first and last menu item it contains - assuming that they are already
+	 * sorted.
+	 * <p>
+	 * The optional {@link ComponentFactory} headerItemFactory, if not
+	 * <code>null</code>, will be invoked to create a header item that will be
+	 * inserted on top of the sub-menus. This item does not count towards
+	 * <tt>maxItemsInMenu</tt>.
+	 * <p>
+	 * Note that this is a utility method that does not mandate the use of the
+	 * {@link MenuManager} structure for the menu.
+	 * 
+	 * @param menuItems
+	 *            {@link JMenuItem}s to be inserted
+	 * @param parentMenu
+	 *            Menu to insert items to
+	 * @param maxItemsInMenu
+	 *            Maximum number of items in parent menu or created sub-menus
+	 * @param headerItemFactory
+	 *            If not <code>null</code>, a {@link ComponentFactory} to create
+	 *            a header item to insert at top of created sub-menus
+	 */
+	abstract void addMenuItemsWithExpansion(List<JMenuItem> menuItems,
+			JMenu parentMenu, int maxItemsInMenu,
+			ComponentFactory headerItemFactory);
+
+	/**
+	 * Create a contextual menu for a selected object.
+	 * <p>
+	 * Items for the contextual menues are discovered in a similar to fashion as
+	 * with {@link #createMenuBar()}, but using {@link DefaultContextualMenu} as
+	 * the root.
+	 * <p>
+	 * Additionally, items implementing {@link ContextualMenuComponent} will be
+	 * {@linkplain ContextualMenuComponent#setContextualSelection(Object, Object, Component)
+	 * informed} about what is the current selection, as passed to this method.
+	 * <p>
+	 * Thus, the items can choose if they want to be
+	 * {@link MenuComponent#isEnabled() visible} or not for a given selection,
+	 * and return an action that is bound it to the selection.
+	 * 
+	 * @param parent
+	 *            The parent object of the selected object, for instance a
+	 *            {@link Workflow}.
+	 * @param selection
+	 *            The selected object which actions in the contextual menu
+	 *            relate to, for instance a {@link Processor}
+	 * @param relativeToComponent
+	 *            A UI component which the returned {@link JPopupMenu} (and it's
+	 *            actions) is to be relative to, for instance as a parent of
+	 *            pop-up dialogues.
+	 * @return An empty or populated {@link JPopupMenu} depending on the
+	 *         selected objects.
+	 */
+	abstract JPopupMenu createContextMenu(Object parent, Object selection,
+			Component relativeToComponent);
+
+	/**
+	 * Create the {@link JMenuBar} containing menu elements defining
+	 * {@link DefaultMenuBar#DEFAULT_MENU_BAR} as their
+	 * {@linkplain MenuComponent#getParentId() parent}.
+	 * <p>
+	 * A {@linkplain WeakReference weak reference} is kept in the menu manager
+	 * to update the menubar if {@link #update()} is called (manually or
+	 * automatically when the SPI is updated).
+	 * 
+	 * @return A {@link JMenuBar} populated with the items belonging to the
+	 *         default menu bar
+	 */
+	abstract JMenuBar createMenuBar();
+
+	/**
+	 * Create the {@link JMenuBar} containing menu elements defining the given
+	 * <code>id</code> as their {@linkplain MenuComponent#getParentId() parent}.
+	 * <p>
+	 * Note that the parent itself also needs to exist as a registered SPI
+	 * instance og {@link MenuComponent#getType()} equal to
+	 * {@link MenuType#menu}, for instance by subclassing {@link AbstractMenu}.
+	 * <p>
+	 * A {@linkplain WeakReference weak reference} is kept in the menu manager
+	 * to update the menubar if {@link #update()} is called (manually or
+	 * automatically when the SPI is updated).
+	 * 
+	 * @param id
+	 *            The {@link URI} identifying the menu bar
+	 * @return A {@link JMenuBar} populated with the items belonging to the
+	 *         given parent id.
+	 */
+	abstract JMenuBar createMenuBar(URI id);
+
+	/**
+	 * Create the {@link JToolBar} containing elements defining
+	 * {@link DefaultToolBar#DEFAULT_TOOL_BAR} as their
+	 * {@linkplain MenuComponent#getParentId() parent}.
+	 * <p>
+	 * A {@linkplain WeakReference weak reference} is kept in the menu manager
+	 * to update the toolbar if {@link #update()} is called (manually or
+	 * automatically when the SPI is updated).
+	 * 
+	 * @return A {@link JToolBar} populated with the items belonging to the
+	 *         default tool bar
+	 */
+	abstract JToolBar createToolBar();
+
+	/**
+	 * Create the {@link JToolBar} containing menu elements defining the given
+	 * <code>id</code> as their {@linkplain MenuComponent#getParentId() parent}.
+	 * <p>
+	 * Note that the parent itself also needs to exist as a registered SPI
+	 * instance of {@link MenuComponent#getType()} equal to
+	 * {@link MenuType#toolBar}, for instance by subclassing
+	 * {@link AbstractToolBar}.
+	 * <p>
+	 * A {@linkplain WeakReference weak reference} is kept in the menu manager
+	 * to update the toolbar if {@link #update()} is called (manually or
+	 * automatically when the SPI is updated).
+	 * 
+	 * @param id
+	 *            The {@link URI} identifying the tool bar
+	 * @return A {@link JToolBar} populated with the items belonging to the
+	 *         given parent id.
+	 */
+	abstract JToolBar createToolBar(URI id);
+
+	/**
+	 * Get a menu item identified by the given URI.
+	 * <p>
+	 * Return the UI {@link Component} last created for a {@link MenuComponent},
+	 * through {@link #createMenuBar()}, {@link #createMenuBar(URI)},
+	 * {@link #createToolBar()} or {@link #createToolBar(URI)}.
+	 * <p>
+	 * For instance, if {@link #createMenuBar()} created a menu bar containing a
+	 * "File" menu with {@link MenuComponent#getId() getId()} ==
+	 * <code>http://example.com/menu#file</code>, calling:
+	 * 
+	 * <pre>
+	 * Component fileMenu = getComponentByURI(URI
+	 * 		.create(&quot;http://example.com/menu#file&quot;));
+	 * </pre>
+	 * 
+	 * would return the {@link JMenu} last created for "File". Note that "last
+	 * created" could mean both the last call to {@link #createMenuBar()} and
+	 * last call to {@link #update()} - which could have happened because the
+	 * SPI registry was updated. To be notified when
+	 * {@link #getComponentByURI(URI)} might return a new Component because the
+	 * menues have been reconstructed, {@linkplain #addObserver(Observer) add an
+	 * observer} to the MenuManager.
+	 * <p>
+	 * If the URI is unknown, has not yet been rendered as a {@link Component},
+	 * or the Component is no longer in use outside the menu manager's
+	 * {@linkplain WeakReference weak references}, <code>null</code> is returned
+	 * instead.
+	 * 
+	 * @see #getURIByComponent(Component)
+	 * @param id
+	 *            {@link URI} of menu item as returned by
+	 *            {@link MenuComponent#getId()}
+	 * @return {@link Component} as previously generated by
+	 *         {@link #createMenuBar()}/{@link #createToolBar()}, or
+	 *         <code>null</code> if the URI is unknown, or if the
+	 *         {@link Component} no longer exists.
+	 */
+	public abstract Component getComponentByURI(URI id);
+
+	/**
+	 * Get the URI of the {@link MenuComponent} this menu/toolbar
+	 * {@link Component} was created from.
+	 * <p>
+	 * If the component was created by the MenuManager, through
+	 * {@link #createMenuBar()}, {@link #createMenuBar(URI)},
+	 * {@link #createToolBar()} or {@link #createToolBar(URI)}, the URI
+	 * identifying the defining {@link MenuComponent} is returned. This will be
+	 * the same URI as returned by {@link MenuComponent#getId()}.
+	 * <p>
+	 * Note that if {@link #update()} has been invoked, the {@link MenuManager}
+	 * might have rebuilt the menu structure and replaced the components since
+	 * the given <code>component</code> was created. The newest
+	 * {@link Component} for the given URI can be retrieved using
+	 * {@link #getComponentByURI(URI)}.
+	 * <p>
+	 * If the component is unknown, <code>null</code> is returned instead.
+	 * 
+	 * @see #getComponentByURI(URI)
+	 * @param component
+	 *            {@link Component} that was previously created by the
+	 *            {@link MenuManager}
+	 * @return {@link URI} identifying the menu component, as returned by
+	 *         {@link MenuComponent#getId()}, or <code>null</code> if the
+	 *         component is unknown.
+	 */
+	abstract URI getURIByComponent(Component component);
+
+	/**
+	 * Update and rebuild the menu structure.
+	 * <p>
+	 * Rebuild menu structure as defined by the {@link MenuComponent}s retrieved
+	 * from the MenuComponent {@link SPIRegistry}.
+	 * <p>
+	 * Rebuilds previously published menubars and toolbars created with
+	 * {@link #createMenuBar()}, {@link #createMenuBar(URI)},
+	 * {@link #createToolBar()} and {@link #createToolBar(URI)}. Note that the
+	 * rebuild will do a removeAll() on the menubar/toolbar, so all components
+	 * will be reconstructed. You can use {@link #getComponentByURI(URI)} to
+	 * look up individual components within the menu and toolbars.
+	 * <p>
+	 * Note that the menu manager is observing the {@link SPIRegistry}, so if a
+	 * plugin gets installed and the SPI registry is updated, this update method
+	 * will be called by the SPI registry observer.
+	 * <p>
+	 * If there are several concurrent calls to {@link #update()}, the calls
+	 * from the other thread will return immediately, while the first thread to
+	 * get the synchronization lock on the menu manager will do the actual
+	 * update. If you want to ensure that {@link #update()} does not return
+	 * before the update has been performed fully, synchronize on the menu
+	 * manager:
+	 * 
+	 * <pre>
+	 * MenuManager menuManager = MenuManager.getInstance();
+	 * synchronized (menuManager) {
+	 * 	menuManager.update();
+	 * }
+	 * doSomethingAfterUpdateFinished();
+	 * </pre>
+	 */
+	abstract void update();
+
+	/**
+	 * Abstract class for events sent to {@linkplain Observer observers} of the
+	 * menu manager.
+	 * 
+	 * @see UpdatedMenuManagerEvent
+	 * @author Stian Soiland-Reyes
+	 */
+	static abstract class MenuManagerEvent {
+	}
+
+	/**
+	 * Event sent to observers registered by
+	 * {@link MenuManager#addObserver(Observer)} when the menus have been
+	 * updated, i.e. when {@link MenuManager#update()} has been called.
+	 */
+	static class UpdatedMenuManagerEvent extends MenuManagerEvent {
+	}
+
+	/**
+	 * A factory for making {@link Component}s, in particular for making headers
+	 * (like {@link JLabel}s) for
+	 * {@link MenuManager#addMenuItemsWithExpansion(List, JMenu, int, ComponentFactory)}
+	 */
+	interface ComponentFactory {
+		public Component makeComponent();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/package-info.java
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/package-info.java b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/package-info.java
new file mode 100644
index 0000000..4c86db5
--- /dev/null
+++ b/taverna-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/package-info.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+/**
+ * An {@link net.sf.taverna.t2.spi.SPIRegistry SPI} based system for creating
+ * {@link javax.swing.JMenuBar menues} and {@link javax.swing.JToolBar toolbars}.
+ * <p>
+ * Each element of a menu and/or toolbar is created by making an SPI
+ * implementation class of {@link net.sf.taverna.t2.ui.menu.MenuComponent} and listing the fully qualified
+ * class name in the SPI description resource file
+ * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code>
+ * </p>
+ * <p>
+ * The {@link net.sf.taverna.t2.ui.menu.MenuManager} discovers all menu components using an SPI registry,
+ * and builds the {@link javax.swing.JMenuBar menu bar} using
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#createMenuBar()} or the
+ * {@link javax.swing.JToolBar toolbar} using
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#createToolBar()}.
+ * </p>
+ * <p>
+ * This allows plugins to provide actions (menu items) and submenues that can be
+ * inserted at any points in the generated menu. All parts of the menues are
+ * described through a parent/child relationship using
+ * {@link net.sf.taverna.t2.ui.menu.MenuComponent#getId()} and {@link net.sf.taverna.t2.ui.menu.MenuComponent#getParentId()}. The
+ * components are identified using {@link java.net.URI}s to avoid compile time
+ * dependencies, so a plugin can for instance add something to the existing
+ * "Edit" menu without depending on the actual implementation of the
+ * {@link net.sf.taverna.t2.ui.menu.MenuComponent} describing "Edit", as long as it refers to the same
+ * URI. The use of URIs instead of pure strings is to encourage the use of
+ * unique identifiers, for instance plugins should use an URI base that is
+ * derived from their package name to avoid collision with other plugins.
+ * </p>
+ * <p>
+ * A set of abstract classes, with a common parent {@link net.sf.taverna.t2.ui.menu.AbstractMenuItem},
+ * make it more convenient to create simple SPI implementations. Two default top
+ * level implementations {@link net.sf.taverna.t2.ui.menu.DefaultMenuBar} and {@link net.sf.taverna.t2.ui.menu.DefaultToolBar} can
+ * be used as parents for items that are to be included in
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#createMenuBar()} and {@link net.sf.taverna.t2.ui.menu.MenuManager#createToolBar()},
+ * but it's possible to have other parents - such menu trees would have to be
+ * created by providing the URI of the top level parent to
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#createMenuBar(URI)} or
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#createToolBar(URI)}.
+ * </p> 
+ * <p>
+ * In the simplest form a menu structure can be built by subclassing
+ * {@link net.sf.taverna.t2.ui.menu.AbstractMenu} and {@link net.sf.taverna.t2.ui.menu.AbstractMenuAction}, but more complex menus
+ * can be built by including submenus (AbstractMenu with an AbstractMenu as a
+ * parent), grouping similar actions in a {@link net.sf.taverna.t2.ui.menu.AbstractMenuSection section},
+ * or making {@link net.sf.taverna.t2.ui.menu.AbstractMenuToggle toggle actions} and
+ * {@link net.sf.taverna.t2.ui.menu.AbstractMenuOptionGroup option groups}. You can add arbitrary "real"
+ * {@link javax.swing.JMenuBar} / {@link javax.swing.JToolBar} compatible items
+ * (such as {@link javax.swing.JMenu}s, {@link javax.swing.JMenuItem}s and
+ * {@link javax.swing.JButton}s) using
+ * {@link net.sf.taverna.t2.ui.menu.AbstractMenuCustom custom menu items}.
+ * </p>
+ * 
+ * <p>
+ * Example showing how <code>File-&gt;Open</code> could be implemented using
+ * two SPI implementations net.sf.taverna.t2.ui.perspectives.hello.FileMenu and
+ * net.sf.taverna.t2.ui.perspectives.hello.FileOpenAction:
+ * </p>
+ * 
+ * <pre>
+ * package net.sf.taverna.t2.ui.perspectives.hello;
+ * 
+ * import java.net.URI;
+ * 
+ * import net.sf.taverna.t2.ui.menu.AbstractMenu;
+ * import net.sf.taverna.t2.ui.menu.DefaultMenuBar;
+ * 
+ * public class FileMenu extends AbstractMenu {
+ * 
+ * 	private static final URI FILE_URI = URI
+ * 			.create(&quot;http://taverna.sf.net/2008/t2workbench/test#file&quot;);
+ * 
+ * 	public FileMenu() {
+ * 		super(DefaultMenuBar.DEFAULT_MENU_BAR, 10, FILE_URI, &quot;File&quot;);
+ * 	}
+ * 
+ * }
+ * </pre>
+ * <pre>
+ * package net.sf.taverna.t2.ui.perspectives.hello;
+ * 
+ * import java.awt.event.ActionEvent;
+ * import java.net.URI;
+ * 
+ * import javax.swing.AbstractAction;
+ * import javax.swing.Action;
+ * import javax.swing.JOptionPane;
+ * 
+ * import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+ * 
+ * public class FileOpenAction extends AbstractMenuAction {
+ *     public FileOpenAction() {
+ *         super(URI.create(&quot;http://taverna.sf.net/2008/t2workbench/test#file&quot;),
+ *                 20);
+ *     }
+ * 
+ *     &#064;Override
+ *     public Action createAction() {
+ *         return new AbstractAction(&quot;Open&quot;) {
+ *             public void actionPerformed(ActionEvent arg0) {
+ *                 JOptionPane.showMessageDialog(null, &quot;Open&quot;);
+ *             }
+ *         };
+ *     }
+ * }
+ * </pre>
+ * 
+ * <p>
+ * The implementation of the {@link net.sf.taverna.t2.ui.menu.MenuManager} itself is discovered by an
+ * internal SPI registry through {@link net.sf.taverna.t2.ui.menu.MenuManager#getInstance()}. The menu
+ * manager is observing the SPI registry, so that any updates to the registry
+ * from installing plugins etc. are reflected in an automatic rebuild of the
+ * menus. This update can also be triggered manually by calling
+ * {@link net.sf.taverna.t2.ui.menu.MenuManager#update()}.
+ * </p>
+ * 
+ * @author Stian Soiland-Reyes
+ * 
+ */
+package net.sf.taverna.t2.ui.menu;
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-menu-api/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..c137386
--- /dev/null
+++ b/taverna-menu-api/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,4 @@
+net.sf.taverna.t2.ui.menu.DefaultMenuBar
+net.sf.taverna.t2.ui.menu.DefaultToolBar
+net.sf.taverna.t2.ui.menu.DefaultContextualMenu
+ 

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context-osgi.xml b/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context-osgi.xml
new file mode 100644
index 0000000..b27e860
--- /dev/null
+++ b/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context-osgi.xml
@@ -0,0 +1,13 @@
+<?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="DefaultMenuBar" auto-export="interfaces" />
+	<service ref="DefaultToolBar" auto-export="interfaces" />
+	<service ref="DefaultContextualMenu" auto-export="interfaces" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context.xml b/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context.xml
new file mode 100644
index 0000000..734dbbe
--- /dev/null
+++ b/taverna-menu-api/src/main/resources/META-INF/spring/menu-api-context.xml
@@ -0,0 +1,10 @@
+<?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="DefaultMenuBar" class="net.sf.taverna.t2.ui.menu.DefaultMenuBar" />
+	<bean id="DefaultToolBar" class="net.sf.taverna.t2.ui.menu.DefaultToolBar" />
+	<bean id="DefaultContextualMenu" class="net.sf.taverna.t2.ui.menu.DefaultContextualMenu" />
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/pom.xml b/taverna-menu-impl/pom.xml
new file mode 100644
index 0000000..b9898ed
--- /dev/null
+++ b/taverna-menu-impl/pom.xml
@@ -0,0 +1,82 @@
+<?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>menu-impl</artifactId>
+	<packaging>bundle</packaging>
+	<name>Menu generation implementation</name>
+	<description>The main workbench ui</description>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<configuration>
+					<instructions>
+						<Embed-Dependency>javahelp</Embed-Dependency>
+						<Import-Package>org.jdesktop.jdic.browser;resolution:=optional,*</Import-Package>
+					</instructions>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>menu-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.lang</groupId>
+			<artifactId>ui</artifactId>
+			<version>${t2.lang.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>uk.org.taverna.configuration</groupId>
+			<artifactId>taverna-app-configuration-api</artifactId>
+			<version>${taverna.configuration.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>javax.help</groupId>
+			<artifactId>javahelp</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/ui/menu/impl/MenuManagerImpl.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/ui/menu/impl/MenuManagerImpl.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/ui/menu/impl/MenuManagerImpl.java
new file mode 100644
index 0000000..ee1fd3d
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/ui/menu/impl/MenuManagerImpl.java
@@ -0,0 +1,880 @@
+/*******************************************************************************
+ * 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.ui.menu.impl;
+
+import static java.lang.Math.min;
+import static javax.help.CSH.setHelpIDString;
+import static javax.swing.Action.NAME;
+import static javax.swing.Action.SHORT_DESCRIPTION;
+import static net.sf.taverna.t2.lang.ui.ShadedLabel.GREEN;
+import static net.sf.taverna.t2.ui.menu.AbstractMenuSection.SECTION_COLOR;
+import static net.sf.taverna.t2.ui.menu.DefaultContextualMenu.DEFAULT_CONTEXT_MENU;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.lang.ref.WeakReference;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.WeakHashMap;
+
+import javax.swing.AbstractButton;
+import javax.swing.Action;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.JToggleButton;
+import javax.swing.JToolBar;
+import javax.swing.border.EmptyBorder;
+
+import net.sf.taverna.t2.lang.observer.MultiCaster;
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.lang.ui.ShadedLabel;
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.ui.menu.AbstractMenuOptionGroup;
+import net.sf.taverna.t2.ui.menu.ContextualMenuComponent;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.menu.DesignOnlyAction;
+import net.sf.taverna.t2.ui.menu.DesignOrResultsAction;
+import net.sf.taverna.t2.ui.menu.MenuComponent;
+import net.sf.taverna.t2.ui.menu.MenuComponent.MenuType;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.PerspectiveSelectionEvent;
+import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Implementation of {@link MenuManager}.
+ *
+ * @author Stian Soiland-Reyes
+ */
+public class MenuManagerImpl implements MenuManager {
+	private static Logger logger = Logger.getLogger(MenuManagerImpl.class);
+
+	private boolean needsUpdate;
+	/**
+	 * Cache used by {@link #getURIByComponent(Component)}
+	 */
+	private WeakHashMap<Component, URI> componentToUri;
+	/**
+	 * {@link MenuElementComparator} used for sorting menu components from the
+	 * SPI registry.
+	 */
+	private MenuElementComparator menuElementComparator = new MenuElementComparator();
+	/**
+	 * Map of {@link URI} to it's discovered children. Populated by
+	 * {@link #findChildren()}.
+	 */
+	private HashMap<URI, List<MenuComponent>> menuElementTree;
+	/**
+	 * Multicaster to distribute messages to {@link Observer}s of this menu
+	 * manager.
+	 */
+	private MultiCaster<MenuManagerEvent> multiCaster;
+	/**
+	 * Lock for {@link #update()}
+	 */
+	private final Object updateLock = new Object();
+	/**
+	 * True if {@link #doUpdate()} is running, subsequents call to
+	 * {@link #update()} will return immediately.
+	 */
+	private boolean updating;
+	/**
+	 * Cache used by {@link #getComponentByURI(URI)}
+	 */
+	private Map<URI, WeakReference<Component>> uriToComponent;
+	/**
+	 * Map from {@link URI} to defining {@link MenuComponent}. Children are in
+	 * {@link #menuElementTree}.
+	 */
+	private Map<URI, MenuComponent> uriToMenuElement;
+	// Note: Not reset by #resetCollections()
+	private Map<URI, List<WeakReference<Component>>> uriToPublishedComponents = new HashMap<>();
+	private List<MenuComponent> menuComponents = new ArrayList<>();
+
+	/**
+	 * Construct the MenuManagerImpl. Observes the SPI registry and does an
+	 * initial {@link #update()}.
+	 */
+	public MenuManagerImpl() {
+		multiCaster = new MultiCaster<>(this);
+		needsUpdate = true;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addMenuItemsWithExpansion(List<JMenuItem> menuItems,
+			JMenu parentMenu, int maxItemsInMenu,
+			ComponentFactory headerItemFactory) {
+		if (menuItems.size() <= maxItemsInMenu) {
+			// Just add them directly
+			for (JMenuItem menuItem : menuItems)
+				parentMenu.add(menuItem);
+			return;
+		}
+		int index = 0;
+		while (index < menuItems.size()) {
+			int toIndex = min(menuItems.size(), index + maxItemsInMenu);
+			if (toIndex == menuItems.size() - 1)
+				// Don't leave a single item left for the last subMenu
+				toIndex--;
+			List<JMenuItem> subList = menuItems.subList(index, toIndex);
+			JMenuItem firstItem = subList.get(0);
+			JMenuItem lastItem = subList.get(subList.size() - 1);
+			JMenu subMenu = new JMenu(firstItem.getText() + " ... "
+					+ lastItem.getText());
+			if (headerItemFactory != null)
+				subMenu.add(headerItemFactory.makeComponent());
+			for (JMenuItem menuItem : subList)
+				subMenu.add(menuItem);
+			parentMenu.add(subMenu);
+			index = toIndex;
+		}
+	}
+
+	@Override
+	public void addObserver(Observer<MenuManagerEvent> observer) {
+		multiCaster.addObserver(observer);
+	}
+
+	@Override
+	public JPopupMenu createContextMenu(Object parent, Object selection,
+			Component relativeToComponent) {
+		ContextualSelection contextualSelection = new ContextualSelection(
+				parent, selection, relativeToComponent);
+		JPopupMenu popupMenu = new JPopupMenu();
+		populateContextMenu(popupMenu, DEFAULT_CONTEXT_MENU,
+				contextualSelection);
+		registerComponent(DEFAULT_CONTEXT_MENU, popupMenu, true);
+		return popupMenu;
+	}
+
+	@Override
+	public JMenuBar createMenuBar() {
+		return createMenuBar(DEFAULT_MENU_BAR);
+	}
+
+	@Override
+	public JMenuBar createMenuBar(URI id) {
+		JMenuBar menuBar = new JMenuBar();
+		if (needsUpdate)
+			update();
+		populateMenuBar(menuBar, id);
+		registerComponent(id, menuBar, true);
+		return menuBar;
+	}
+
+	@Override
+	public JToolBar createToolBar() {
+		return createToolBar(DEFAULT_TOOL_BAR);
+	}
+
+	@Override
+	public JToolBar createToolBar(URI id) {
+		JToolBar toolbar = new JToolBar();
+		if (needsUpdate)
+			update();
+		populateToolBar(toolbar, id);
+		registerComponent(id, toolbar, true);
+		return toolbar;
+	}
+
+	@Override
+	public synchronized Component getComponentByURI(URI id) {
+		WeakReference<Component> componentRef = uriToComponent.get(id);
+		if (componentRef == null)
+			return null;
+		// Might also be null it reference has gone dead
+		return componentRef.get();
+	}
+
+	@Override
+	public List<Observer<MenuManagerEvent>> getObservers() {
+		return multiCaster.getObservers();
+	}
+
+	@Override
+	public synchronized URI getURIByComponent(Component component) {
+		return componentToUri.get(component);
+	}
+
+	@Override
+	public void removeObserver(Observer<MenuManagerEvent> observer) {
+		multiCaster.removeObserver(observer);
+	}
+
+	@Override
+	public void update() {
+		synchronized (updateLock) {
+			if (updating && !needsUpdate)
+				return;
+			updating = true;
+		}
+		try {
+			doUpdate();
+		} finally {
+			synchronized (updateLock) {
+				updating = false;
+				needsUpdate = false;
+			}
+		}
+	}
+
+	public void update(Object service, Map<?, ?> properties) {
+		needsUpdate = true;
+		update();
+	}
+
+	/**
+	 * Add a {@link JMenu} to the list of components as described by the menu
+	 * component. If there are no children, the menu is not added.
+	 *
+	 * @param components
+	 *            List of components where to add the created {@link JMenu}
+	 * @param menuComponent
+	 *            The {@link MenuComponent} definition for this menu
+	 * @param isToolbar
+	 *            True if the list of components is to be added to a toolbar
+	 */
+	private void addMenu(List<Component> components,
+			MenuComponent menuComponent, MenuOptions menuOptions) {
+		URI menuId = menuComponent.getId();
+		if (menuOptions.isToolbar()) {
+			logger.warn("Can't have menu " + menuComponent
+					+ " within toolBar element");
+			return;
+		}
+		MenuOptions childOptions = new MenuOptions(menuOptions);
+		List<Component> subComponents = makeComponents(menuId, childOptions);
+		if (subComponents.isEmpty()) {
+			logger.warn("No sub components found for menu " + menuId);
+			return;
+		}
+
+		JMenu menu = new JMenu(menuComponent.getAction());
+		for (Component menuItem : subComponents)
+			if (menuItem == null)
+				menu.addSeparator();
+			else
+				menu.add(menuItem);
+		registerComponent(menuId, menu);
+		components.add(menu);
+	}
+
+	/**
+	 * Add <code>null</code> to the list of components, meaning that a separator
+	 * is to be created. Subsequent separators are ignored, and if there are no
+	 * components on the list already no separator will be added.
+	 * 
+	 * @param components
+	 *            List of components
+	 */
+	private void addNullSeparator(List<Component> components) {
+		if (components.isEmpty())
+			// Don't start with a separator
+			return;
+		if (components.get(components.size() - 1) == null)
+			// Already a separator in last position
+			return;
+		components.add(null);
+	}
+
+	/**
+	 * Add an {@link AbstractMenuOptionGroup option group} to the list of
+	 * components
+	 *
+	 * @param components
+	 *            List of components where to add the created {@link JMenu}
+	 * @param optionGroupId
+	 *            The {@link URI} identifying the option group
+	 * @param isToolbar
+	 *            True if the option group is to be added to a toolbar
+	 */
+	private void addOptionGroup(List<Component> components, URI optionGroupId,
+			MenuOptions menuOptions) {
+		MenuOptions childOptions = new MenuOptions(menuOptions);
+		childOptions.setOptionGroup(true);
+
+		List<Component> buttons = makeComponents(optionGroupId, childOptions);
+		addNullSeparator(components);
+		if (buttons.isEmpty()) {
+			logger.warn("No sub components found for option group "
+					+ optionGroupId);
+			return;
+		}
+		ButtonGroup buttonGroup = new ButtonGroup();
+
+		for (Component button : buttons) {
+			if (button instanceof AbstractButton)
+				buttonGroup.add((AbstractButton) button);
+			else
+				logger.warn("Component of button group " + optionGroupId
+						+ " is not an AbstractButton: " + button);
+			if (button == null) {
+				logger.warn("Separator found within button group");
+				addNullSeparator(components);
+			} else
+				components.add(button);
+		}
+		addNullSeparator(components);
+	}
+
+	/**
+	 * Add a section to a list of components.
+	 *
+	 * @param components
+	 *            List of components
+	 * @param sectionId
+	 *            The {@link URI} identifying the section
+	 * @param menuOptions
+	 *            {@link MenuOptions options} for creating the menu
+	 */
+	private void addSection(List<Component> components, URI sectionId,
+			MenuOptions menuOptions) {
+		List<Component> childComponents = makeComponents(sectionId, menuOptions);
+
+		MenuComponent sectionDef = uriToMenuElement.get(sectionId);
+		addNullSeparator(components);
+		if (childComponents.isEmpty()) {
+			logger.warn("No sub components found for section " + sectionId);
+			return;
+		}
+		Action sectionAction = sectionDef.getAction();
+		if (sectionAction != null) {
+			String sectionLabel = (String) sectionAction.getValue(NAME);
+			if (sectionLabel != null) {
+				// No separators before the label
+				stripTrailingNullSeparator(components);
+				Color labelColor = (Color) sectionAction.getValue(SECTION_COLOR);
+				if (labelColor == null)
+					labelColor = GREEN;
+				ShadedLabel label = new ShadedLabel(sectionLabel, labelColor);
+				components.add(label);
+			}
+		}
+		for (Component childComponent : childComponents)
+			if (childComponent == null) {
+				logger.warn("Separator found within section " + sectionId);
+				addNullSeparator(components);
+			} else
+				components.add(childComponent);
+		addNullSeparator(components);
+	}
+
+	/**
+	 * Remove the last <code>null</code> separator from the list of components
+	 * if it's present.
+	 *
+	 * @param components
+	 *            List of components
+	 */
+	private void stripTrailingNullSeparator(List<Component> components) {
+		if (!components.isEmpty()) {
+			int lastIndex = components.size() - 1;
+			if (components.get(lastIndex) == null)
+				components.remove(lastIndex);
+		}
+	}
+
+	/**
+	 * Perform the actual update, called by {@link #update()}. Reset all the
+	 * collections, refresh from SPI, modify any previously published components
+	 * and notify any observers.
+	 */
+	protected synchronized void doUpdate() {
+		resetCollections();
+		findChildren();
+		updatePublishedComponents();
+		multiCaster.notify(new UpdatedMenuManagerEvent());
+	}
+
+	/**
+	 * Find all children for all known menu components. Populates
+	 * {@link #uriToMenuElement}.
+	 *
+	 */
+	protected void findChildren() {
+		for (MenuComponent menuElement : menuComponents) {
+			uriToMenuElement.put(menuElement.getId(), menuElement);
+			logger.debug("Found menu element " + menuElement.getId() + " "
+					+ menuElement);
+			if (menuElement.getParentId() == null)
+				continue;
+			List<MenuComponent> siblings = menuElementTree.get(menuElement
+					.getParentId());
+			if (siblings == null) {
+				siblings = new ArrayList<>();
+				synchronized (menuElementTree) {
+					menuElementTree.put(menuElement.getParentId(), siblings);
+				}
+			}
+			siblings.add(menuElement);
+		}
+//		if (uriToMenuElement.isEmpty()) {
+//			logger.error("No menu elements found, check classpath/Raven/SPI");
+//		}
+	}
+
+	/**
+	 * Get the children which have the given URI specified as their parent, or
+	 * an empty list if no children exist.
+	 *
+	 * @param id
+	 *            The {@link URI} of the parent
+	 * @return The {@link List} of {@link MenuComponent} which have the given
+	 *         parent
+	 */
+	protected List<MenuComponent> getChildren(URI id) {
+		List<MenuComponent> children = null;
+		synchronized (menuElementTree) {
+			children = menuElementTree.get(id);
+			if (children != null)
+				children = new ArrayList<>(children);
+		}
+		if (children == null)
+			children = Collections.<MenuComponent> emptyList();
+		else
+			Collections.sort(children, menuElementComparator);
+		return children;
+	}
+
+	/**
+	 * Make the list of Swing {@link Component}s that are the children of the
+	 * given {@link URI}.
+	 *
+	 * @param id
+	 *            The {@link URI} of the parent which children are to be made
+	 * @param menuOptions
+	 *            Options of the created menu, for instance
+	 *            {@link MenuOptions#isToolbar()}.
+	 * @return A {@link List} of {@link Component}s that can be added to a
+	 *         {@link JMenuBar}, {@link JMenu} or {@link JToolBar}.
+	 */
+	protected List<Component> makeComponents(URI id, MenuOptions menuOptions) {
+		List<Component> components = new ArrayList<>();
+		for (MenuComponent childElement : getChildren(id)) {
+			if (childElement instanceof ContextualMenuComponent)
+				((ContextualMenuComponent) childElement)
+						.setContextualSelection(menuOptions
+								.getContextualSelection());
+			/*
+			 * Important - check this AFTER setContextualSelection so the item
+			 * can change it's enabled-state if needed.
+			 */
+			if (!childElement.isEnabled())
+				continue;
+			MenuType type = childElement.getType();
+			Action action = childElement.getAction();
+			URI childId = childElement.getId();
+			if (type.equals(MenuType.action)) {
+				if (action == null) {
+					logger.warn("Skipping invalid action " + childId + " for "
+							+ id);
+					continue;
+				}
+
+				Component actionComponent;
+				if (menuOptions.isOptionGroup()) {
+					if (menuOptions.isToolbar()) {
+						actionComponent = new JToggleButton(action);
+						toolbarizeButton((AbstractButton) actionComponent);
+					} else
+						actionComponent = new JRadioButtonMenuItem(action);
+				} else {
+					if (menuOptions.isToolbar()) {
+						actionComponent = new JButton(action);
+						toolbarizeButton((AbstractButton) actionComponent);
+					} else
+						actionComponent = new JMenuItem(action);
+				}
+				registerComponent(childId, actionComponent);
+				components.add(actionComponent);
+			} else if (type.equals(MenuType.toggle)) {
+				if (action == null) {
+					logger.warn("Skipping invalid toggle " + childId + " for "
+							+ id);
+					continue;
+				}
+				Component toggleComponent;
+				if (menuOptions.isToolbar())
+					toggleComponent = new JToggleButton(action);
+				else
+					toggleComponent = new JCheckBoxMenuItem(action);
+				registerComponent(childId, toggleComponent);
+				components.add(toggleComponent);
+			} else if (type.equals(MenuType.custom)) {
+				Component customComponent = childElement.getCustomComponent();
+				if (customComponent == null) {
+					logger.warn("Skipping null custom component " + childId
+							+ " for " + id);
+					continue;
+				}
+				registerComponent(childId, customComponent);
+				components.add(customComponent);
+			} else if (type.equals(MenuType.optionGroup))
+				addOptionGroup(components, childId, menuOptions);
+			else if (type.equals(MenuType.section))
+				addSection(components, childId, menuOptions);
+			else if (type.equals(MenuType.menu))
+				addMenu(components, childElement, menuOptions);
+			else {
+				logger.warn("Skipping invalid/unknown type " + type + " for "
+						+ id);
+				continue;
+			}
+		}
+		stripTrailingNullSeparator(components);
+		return components;
+	}
+
+	/**
+	 * Fill the specified menu bar with the menu elements that have the given
+	 * URI as their parent.
+	 * <p>
+	 * Existing elements on the menu bar will be removed.
+	 *
+	 * @param menuBar
+	 *            The {@link JMenuBar} to update
+	 * @param id
+	 *            The {@link URI} of the menu bar
+	 */
+	protected void populateMenuBar(JMenuBar menuBar, URI id) {
+		menuBar.removeAll();
+		MenuComponent menuDef = uriToMenuElement.get(id);
+		if (menuDef == null)
+			throw new IllegalArgumentException("Unknown menuBar " + id);
+		if (!menuDef.getType().equals(MenuType.menu))
+			throw new IllegalArgumentException("Element " + id
+					+ " is not a menu, but a " + menuDef.getType());
+		MenuOptions menuOptions = new MenuOptions();
+		for (Component component : makeComponents(id, menuOptions))
+			if (component == null)
+				logger.warn("Ignoring separator in menu bar " + id);
+			else
+				menuBar.add(component);
+	}
+
+	/**
+	 * Fill the specified menu bar with the menu elements that have the given
+	 * URI as their parent.
+	 * <p>
+	 * Existing elements on the menu bar will be removed.
+	 *
+	 * @param popupMenu
+	 *            The {@link JPopupMenu} to update
+	 * @param id
+	 *            The {@link URI} of the menu bar
+	 * @param contextualSelection
+	 *            The current selection for the context menu
+	 */
+	protected void populateContextMenu(JPopupMenu popupMenu, URI id,
+			ContextualSelection contextualSelection) {
+		popupMenu.removeAll();
+		MenuComponent menuDef = uriToMenuElement.get(id);
+		if (menuDef == null)
+			throw new IllegalArgumentException("Unknown menuBar " + id);
+		if (!menuDef.getType().equals(MenuType.menu))
+			throw new IllegalArgumentException("Element " + id
+					+ " is not a menu, but a " + menuDef.getType());
+		MenuOptions menuOptions = new MenuOptions();
+		menuOptions.setContextualSelection(contextualSelection);
+		for (Component component : makeComponents(id, menuOptions))
+			if (component == null)
+				popupMenu.addSeparator();
+			else
+				popupMenu.add(component);
+	}
+
+	/**
+	 * Fill the specified tool bar with the elements that have the given URI as
+	 * their parent.
+	 * <p>
+	 * Existing elements on the tool bar will be removed.
+	 *
+	 * @param toolbar
+	 *            The {@link JToolBar} to update
+	 * @param id
+	 *            The {@link URI} of the tool bar
+	 */
+	protected void populateToolBar(JToolBar toolbar, URI id) {
+		toolbar.removeAll();
+		MenuComponent toolbarDef = uriToMenuElement.get(id);
+		if (toolbarDef == null)
+			throw new IllegalArgumentException("Unknown toolBar " + id);
+		if (!toolbarDef.getType().equals(MenuType.toolBar))
+			throw new IllegalArgumentException("Element " + id
+					+ " is not a toolBar, but a " + toolbarDef.getType());
+		if (toolbarDef.getAction() != null) {
+			String name = (String) toolbarDef.getAction().getValue(Action.NAME);
+			toolbar.setName(name);
+		} else
+			toolbar.setName("");
+		MenuOptions menuOptions = new MenuOptions();
+		menuOptions.setToolbar(true);
+		for (Component component : makeComponents(id, menuOptions)) {
+			if (component == null) {
+				toolbar.addSeparator();
+				continue;
+			}
+			if (component instanceof JButton) {
+				JButton toolbarButton = (JButton) component;
+				toolbarButton.putClientProperty("hideActionText", true);
+			}
+			toolbar.add(component);
+		}
+	}
+
+	/**
+	 * Register a component that has been created. Such a component can be
+	 * resolved through {@link #getComponentByURI(URI)}.
+	 *
+	 * @param id
+	 *            The {@link URI} that defined the component
+	 * @param component
+	 *            The {@link Component} that was created.
+	 */
+	protected synchronized void registerComponent(URI id, Component component) {
+		registerComponent(id, component, false);
+	}
+
+	/**
+	 * Register a component that has been created. Such a component can be
+	 * resolved through {@link #getComponentByURI(URI)}.
+	 *
+	 * @param id
+	 *            The {@link URI} that defined the component
+	 * @param component
+	 *            The {@link Component} that was created.
+	 * @param published
+	 *            <code>true</code> if the component has been published through
+	 *            {@link #createMenuBar()} or similar, and is to be
+	 *            automatically updated by later calls to {@link #update()}.
+	 */
+	protected synchronized void registerComponent(URI id, Component component,
+			boolean published) {
+		uriToComponent.put(id, new WeakReference<>(component));
+		componentToUri.put(component, id);
+		if (published) {
+			List<WeakReference<Component>> publishedComponents = uriToPublishedComponents
+					.get(id);
+			if (publishedComponents == null) {
+				publishedComponents = new ArrayList<>();
+				uriToPublishedComponents.put(id, publishedComponents);
+			}
+			publishedComponents.add(new WeakReference<>(component));
+		}
+		setHelpStringForComponent(component, id);
+	}
+
+	/**
+	 * Reset all collections
+	 *
+	 */
+	protected synchronized void resetCollections() {
+		menuElementTree = new HashMap<>();
+		componentToUri = new WeakHashMap<>();
+		uriToMenuElement = new HashMap<>();
+		uriToComponent = new HashMap<>();
+	}
+
+	/**
+	 * Set javax.help string to identify the component for later references to
+	 * the help document. Note that the component (ie. the
+	 * {@link AbstractMenuAction} must have an ID for an registration to take
+	 * place.
+	 *
+	 * @param component
+	 *            The {@link Component} to set help string for
+	 * @param componentId
+	 *            The {@link URI} to be used as identifier
+	 */
+	protected void setHelpStringForComponent(Component component,
+			URI componentId) {
+		if (componentId != null) {
+			String helpId = componentId.toASCIIString();
+			setHelpIDString(component, helpId);
+		}
+	}
+
+	/**
+	 * Make an {@link AbstractButton} be configured in a "toolbar-like" way, for
+	 * instance showing only the icon.
+	 *
+	 * @param actionButton
+	 *            Button to toolbarise
+	 */
+	protected void toolbarizeButton(AbstractButton actionButton) {
+		Action action = actionButton.getAction();
+		if (action.getValue(SHORT_DESCRIPTION) == null)
+			action.putValue(SHORT_DESCRIPTION, action.getValue(NAME));
+		actionButton.setBorder(new EmptyBorder(0, 2, 0, 2));
+		// actionButton.setHorizontalTextPosition(JButton.CENTER);
+		// actionButton.setVerticalTextPosition(JButton.BOTTOM);
+		if (action.getValue(Action.SMALL_ICON) != null) {
+			// Don't show the text
+			actionButton.putClientProperty("hideActionText", true);
+			// Since hideActionText seems to be broken in Java 5 and/or OS X
+			actionButton.setText(null);
+		}
+	}
+
+	/**
+	 * Update all components that have been published using
+	 * {@link #createMenuBar()} and similar. Content of such components will be
+	 * removed and replaced by fresh components.
+	 */
+	protected void updatePublishedComponents() {
+		for (Entry<URI, List<WeakReference<Component>>> entry : uriToPublishedComponents
+				.entrySet())
+			for (WeakReference<Component> reference : entry.getValue()) {
+				URI id = entry.getKey();
+				Component component = reference.get();
+				if (component == null)
+					continue;
+				if (component instanceof JToolBar)
+					populateToolBar((JToolBar) component, id);
+				else if (component instanceof JMenuBar)
+					populateMenuBar((JMenuBar) component, id);
+				else
+					logger.warn("Could not update published component " + id
+							+ ": " + component.getClass());
+			}
+	}
+
+	public void setMenuComponents(List<MenuComponent> menuComponents) {
+		this.menuComponents = menuComponents;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		selectionManager.addObserver(new SelectionManagerObserver());
+	}
+
+	/**
+	 * {@link Comparator} that can order {@link MenuComponent}s by their
+	 * {@link MenuComponent#getPositionHint()}.
+	 */
+	protected static class MenuElementComparator implements
+			Comparator<MenuComponent> {
+		@Override
+		public int compare(MenuComponent a, MenuComponent b) {
+			return a.getPositionHint() - b.getPositionHint();
+		}
+	}
+
+	/**
+	 * Various options for
+	 * {@link MenuManagerImpl#makeComponents(URI, MenuOptions)} and friends.
+	 *
+	 * @author Stian Soiland-Reyes
+	 */
+	public static class MenuOptions {
+		private boolean isToolbar = false;
+		private boolean isOptionGroup = false;
+		private ContextualSelection contextualSelection = null;
+
+		public ContextualSelection getContextualSelection() {
+			return contextualSelection;
+		}
+
+		public void setContextualSelection(
+				ContextualSelection contextualSelection) {
+			this.contextualSelection = contextualSelection;
+		}
+
+		public MenuOptions(MenuOptions original) {
+			this.isOptionGroup = original.isOptionGroup();
+			this.isToolbar = original.isToolbar();
+			this.contextualSelection = original.getContextualSelection();
+		}
+
+		public MenuOptions() {
+		}
+
+		@Override
+		protected MenuOptions clone() {
+			return new MenuOptions(this);
+		}
+
+		public boolean isToolbar() {
+			return isToolbar;
+		}
+
+		public void setToolbar(boolean isToolbar) {
+			this.isToolbar = isToolbar;
+		}
+
+		public boolean isOptionGroup() {
+			return isOptionGroup;
+		}
+
+		public void setOptionGroup(boolean isOptionGroup) {
+			this.isOptionGroup = isOptionGroup;
+		}
+	}
+
+	private final class SelectionManagerObserver extends
+			SwingAwareObserver<SelectionManagerEvent> {
+		private static final String DESIGN_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.design.DesignPerspective";
+		private static final String RESULTS_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.results.ResultsPerspective";
+
+		@Override
+		public void notifySwing(Observable<SelectionManagerEvent> sender,
+				SelectionManagerEvent message) {
+			if (!(message instanceof PerspectiveSelectionEvent))
+				return;
+			handlePerspectiveSelect((PerspectiveSelectionEvent) message);
+		}
+
+		private void handlePerspectiveSelect(PerspectiveSelectionEvent event) {
+			String perspectiveID = event.getSelectedPerspective().getID();
+			boolean isDesign = DESIGN_PERSPECTIVE_ID.equals(perspectiveID);
+			boolean isResults = RESULTS_PERSPECTIVE_ID.equals(perspectiveID);
+
+			for (MenuComponent menuComponent : menuComponents)
+				if (!(menuComponent instanceof ContextualMenuComponent)) {
+					Action action = menuComponent.getAction();
+					if (action instanceof DesignOnlyAction)
+						action.setEnabled(isDesign);
+					else if (action instanceof DesignOrResultsAction)
+						action.setEnabled(isDesign || isResults);
+				}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/AdvancedMenu.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/AdvancedMenu.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/AdvancedMenu.java
new file mode 100644
index 0000000..9a2f37b
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/AdvancedMenu.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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.ui.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_A;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenu;
+
+public class AdvancedMenu extends AbstractMenu {
+	public static final URI ADVANCED_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#advanced");
+
+	public AdvancedMenu() {
+		super(DEFAULT_MENU_BAR, 1000, ADVANCED_URI, makeAction());
+	}
+
+	public static DummyAction makeAction() {
+		DummyAction action = new DummyAction("Advanced");
+		action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_A));
+		return action;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/EditMenu.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/EditMenu.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/EditMenu.java
new file mode 100644
index 0000000..a15237c
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/EditMenu.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * 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.ui.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_E;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenu;
+
+public class EditMenu extends AbstractMenu {
+	public EditMenu() {
+		super(DEFAULT_MENU_BAR, 20, URI
+				.create("http://taverna.sf.net/2008/t2workbench/menu#edit"),
+				makeAction());
+	}
+
+	public static DummyAction makeAction() {
+		DummyAction action = new DummyAction("Edit");
+		action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_E));
+		return action;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FeedbackMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FeedbackMenuAction.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FeedbackMenuAction.java
new file mode 100644
index 0000000..6b6eb7c
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FeedbackMenuAction.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * 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.ui.impl.menu;
+
+import static java.awt.Desktop.getDesktop;
+import static net.sf.taverna.t2.workbench.ui.impl.menu.HelpMenu.HELP_URI;
+
+import java.awt.event.ActionEvent;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+import org.apache.log4j.Logger;
+
+/**
+ * MenuItem for feedback
+ * 
+ * @author alanrw
+ */
+public class FeedbackMenuAction extends AbstractMenuAction {
+	private static Logger logger = Logger.getLogger(FeedbackMenuAction.class);
+
+	private static String FEEDBACK_URL = "http://www.taverna.org.uk/about/contact-us/feedback/";
+
+	public FeedbackMenuAction() {
+		super(HELP_URI, 20);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new FeedbackAction();
+	}
+
+	@SuppressWarnings("serial")
+	private final class FeedbackAction extends AbstractAction {
+		private FeedbackAction() {
+			super("Contact us");
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			try {
+				getDesktop().browse(new URI(FEEDBACK_URL));
+			} catch (IOException e1) {
+				logger.error("Unable to open URL", e1);
+			} catch (URISyntaxException e1) {
+				logger.error("Invalid URL syntax", e1);
+			}
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FileMenu.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FileMenu.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FileMenu.java
new file mode 100644
index 0000000..61f963b
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FileMenu.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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.ui.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_F;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenu;
+
+/**
+ * File menu
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class FileMenu extends AbstractMenu {
+	public FileMenu() {
+		super(DEFAULT_MENU_BAR, 10, URI
+				.create("http://taverna.sf.net/2008/t2workbench/menu#file"),
+				makeAction());
+	}
+
+	public static DummyAction makeAction() {
+		DummyAction action = new DummyAction("File");
+		action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_F));
+		return action;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/HelpMenu.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/HelpMenu.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/HelpMenu.java
new file mode 100644
index 0000000..c4169cc
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/HelpMenu.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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.ui.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_H;
+import static javax.swing.Action.MNEMONIC_KEY;
+import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenu;
+
+public class HelpMenu extends AbstractMenu {
+	public static final URI HELP_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#help");
+
+	public HelpMenu() {
+		super(DEFAULT_MENU_BAR, 1024, HELP_URI, makeAction());
+	}
+
+	public static DummyAction makeAction() {
+		DummyAction action = new DummyAction("Help");
+		action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_H));
+		return action;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/OnlineHelpMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/OnlineHelpMenuAction.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/OnlineHelpMenuAction.java
new file mode 100644
index 0000000..d091c8e
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/OnlineHelpMenuAction.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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.ui.impl.menu;
+
+import static java.awt.event.KeyEvent.VK_F1;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.helper.Helper.displayDefaultHelp;
+import static net.sf.taverna.t2.workbench.ui.impl.menu.HelpMenu.HELP_URI;
+
+import java.awt.AWTEvent;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+/**
+ * MenuItem for help
+ * 
+ * @author alanrw
+ */
+public class OnlineHelpMenuAction extends AbstractMenuAction {
+	public OnlineHelpMenuAction() {
+		super(HELP_URI, 10);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new OnlineHelpAction();
+	}
+
+	@SuppressWarnings("serial")
+	private final class OnlineHelpAction extends AbstractAction {
+		private OnlineHelpAction() {
+			super("Online help");
+			putValue(ACCELERATOR_KEY, getKeyStroke(VK_F1, 0));
+
+		}
+
+		/**
+		 * When selected, use the Helper to display the default help.
+		 */
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			displayDefaultHelp((AWTEvent) e);
+			// TODO change helper to bean?
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ShowLogsAndDataMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ShowLogsAndDataMenuAction.java b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ShowLogsAndDataMenuAction.java
new file mode 100644
index 0000000..308d51d
--- /dev/null
+++ b/taverna-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ShowLogsAndDataMenuAction.java
@@ -0,0 +1,89 @@
+package net.sf.taverna.t2.workbench.ui.impl.menu;
+
+import static java.lang.Runtime.getRuntime;
+import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
+import static javax.swing.JOptionPane.showInputDialog;
+import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow;
+import static net.sf.taverna.t2.workbench.ui.impl.menu.AdvancedMenu.ADVANCED_URI;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+public class ShowLogsAndDataMenuAction extends AbstractMenuAction {
+	private static final String OPEN = "open";
+	private static final String EXPLORER = "explorer";
+	// TODO Consider using xdg-open instead of gnome-open
+	private static final String GNOME_OPEN = "gnome-open";
+	private static final String WINDOWS = "Windows";
+	private static final String MAC_OS_X = "Mac OS X";
+
+	private ApplicationConfiguration applicationConfiguration;
+
+	public ShowLogsAndDataMenuAction() {
+		super(ADVANCED_URI, 200);
+	}
+
+	private static Logger logger = Logger.getLogger(ShowLogsAndDataMenuAction.class);
+
+	@Override
+	protected Action createAction() {
+		return new AbstractAction("Show logs and data folder") {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				File logsAndDataDir = applicationConfiguration.getApplicationHomeDir();
+				showDirectory(logsAndDataDir, "Taverna logs and data folder");
+			}
+		};
+	}
+
+	public static void showDirectory(File dir, String title) {
+		String path = dir.getAbsolutePath();
+		String os = System.getProperty("os.name");
+		String cmd;
+		boolean isWindows = false;
+		if (os.equals(MAC_OS_X))
+			cmd = OPEN;
+		else if (os.startsWith(WINDOWS)) {
+			cmd = EXPLORER;
+			isWindows = true;
+		} else
+			// Assume Unix - best option is gnome-open
+			cmd = GNOME_OPEN;
+
+		String[] cmdArray = new String[2];
+		cmdArray[0] = cmd;
+		cmdArray[1] = path;
+		try {
+			Process exec = getRuntime().exec(cmdArray);
+			Thread.sleep(300);
+			exec.getErrorStream().close();
+			exec.getInputStream().close();
+			exec.getOutputStream().close();
+			exec.waitFor();
+			if (exec.exitValue() == 0 || isWindows && exec.exitValue() == 1)
+				// explorer.exe thinks 1 means success
+				return;
+			logger.warn("Exit value from " + cmd + " " + path + ": " + exec.exitValue());
+		} catch (Exception ex) {
+			logger.warn("Could not call " + cmd + " " + path, ex);
+		}
+		// Fall-back - just show a dialogue with the path
+		showInputDialog(getMainWindow(), "Copy path from below:", title,
+				INFORMATION_MESSAGE, null, null, path);
+	}
+
+	public void setApplicationConfiguration(ApplicationConfiguration applicationConfiguration) {
+		this.applicationConfiguration = applicationConfiguration;
+	}
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/Integration.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/Integration.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/Integration.java
new file mode 100644
index 0000000..ec792d4
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/Integration.java
@@ -0,0 +1,518 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.log4j.Logger;
+import org.biocatalogue.x2009.xml.rest.ResourceLink;
+import org.biocatalogue.x2009.xml.rest.RestMethod;
+import org.biocatalogue.x2009.xml.rest.RestService;
+import org.biocatalogue.x2009.xml.rest.Service;
+import org.biocatalogue.x2009.xml.rest.ServiceTechnologyType;
+import org.biocatalogue.x2009.xml.rest.SoapOperation;
+import org.biocatalogue.x2009.xml.rest.SoapService;
+import org.biocatalogue.x2009.xml.rest.Service.Variants;
+
+import net.sf.taverna.biocatalogue.model.HTTPMethodInterpreter;
+import net.sf.taverna.biocatalogue.model.HTTPMethodInterpreter.UnsupportedHTTPMethodException;
+import net.sf.taverna.biocatalogue.model.Resource;
+import net.sf.taverna.biocatalogue.model.Resource.TYPE;
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.biocatalogue.model.SoapOperationPortIdentity;
+import net.sf.taverna.biocatalogue.model.SoapProcessorIdentity;
+import net.sf.taverna.biocatalogue.model.Util;
+import net.sf.taverna.t2.activities.rest.RESTActivity;
+import net.sf.taverna.t2.activities.rest.RESTActivity.HTTP_METHOD;
+import net.sf.taverna.t2.activities.wsdl.WSDLActivity;
+import net.sf.taverna.t2.activities.wsdl.servicedescriptions.WSDLServiceDescription;
+import net.sf.taverna.t2.ui.menu.ContextualSelection;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.service_panel.BioCatalogueRESTServiceProvider;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.service_panel.BioCatalogueWSDLOperationServiceProvider;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.service_panel.RESTFromBioCatalogueServiceDescription;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.service_panel.WSDLOperationFromBioCatalogueServiceDescription;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.ui.workflowview.WorkflowView;
+import net.sf.taverna.t2.workflowmodel.Dataflow;
+import net.sf.taverna.t2.workflowmodel.Port;
+import net.sf.taverna.t2.workflowmodel.Processor;
+import net.sf.taverna.t2.workflowmodel.processor.activity.Activity;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityInputPort;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityOutputPort;
+import net.sf.taverna.t2.workflowmodel.utils.Tools;
+
+/**
+ * This class contains helpers for deeper integration with Taverna UI.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class Integration
+{
+  private static final Logger logger = Logger.getLogger(Integration.class);
+  
+  
+  // deny instantiation of this class
+  private Integration() { }
+  
+  
+  /**
+   * Adds a processor to the current workflow.
+   * The processor is specified by WSDL location and the operation name.
+   * 
+   * @param processorResource Resource to add to the current workflow.
+   * @return Outcome of inserting the processor into the current workflow as a
+   *         HTML-formatted string (with no opening and closing HTML tags).
+   */
+  public static JComponent insertProcessorIntoCurrentWorkflow(ResourceLink processorResource)
+  {
+    // check if this type of resource can be added to workflow diagram
+    TYPE resourceType = Resource.getResourceTypeFromResourceURL(processorResource.getHref());
+    if (resourceType.isSuitableForAddingToWorkflowDiagram()) {
+      switch (resourceType) {
+        case SOAPOperation:
+          SoapOperation soapOp = (SoapOperation) processorResource;
+          try {
+            SoapService soapService = BioCatalogueClient.getInstance().
+                                        getBioCatalogueSoapService(soapOp.getAncestors().getSoapService().getHref());
+            
+            try {
+              WSDLServiceDescription myServiceDescription = new WSDLServiceDescription();
+              myServiceDescription.setOperation(soapOp.getName());
+              myServiceDescription.setUse("literal"); // or "encoded"
+              myServiceDescription.setStyle("document"); // or "rpc"
+              myServiceDescription.setURI(new URI(soapService.getWsdlLocation()));
+              myServiceDescription.setDescription(StringEscapeUtils.escapeHtml(soapService.getDescription()));  // TODO - not sure where this is used
+              
+              if (WorkflowView.importServiceDescription(myServiceDescription, false) != null) {
+                return (new JLabel("Selected " + TYPE.SOAPOperation.getTypeName() + " was successfully added to the current workflow",
+                                 ResourceManager.getImageIcon(ResourceManager.TICK_ICON), JLabel.CENTER));
+              }
+              else {
+                return (new JLabel("<html><center>Taverna was unable to add selected " + TYPE.SOAPOperation.getTypeName() + 
+                    " as a service to the current workflow.<br>This could be because the service is currently not accessible.</center></html>",
+                    ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+              }
+            }
+            catch (URISyntaxException e)
+            {
+              logger.error("Couldn't add " + TYPE.SOAPOperation + " to the current workflow", e);
+              return (new JLabel("<html>Could not add the selected " + TYPE.SOAPOperation.getTypeName() + " to the current workflow.<br>" +
+                                    		"Log file will containt additional details about this error.</html>",
+                                    		ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+            }
+            
+          }
+          catch (Exception e) {
+            logger.error("Failed to fetch required details to add this " + TYPE.SOAPOperation + " into the current workflow.", e);
+            return (new JLabel("<html>Failed to fetch required details to add this<br>" +
+                                      TYPE.SOAPOperation.getTypeName() + " into the current workflow.</html>",
+                                      ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+          }
+          
+        case RESTMethod:
+          // received object may only contain limited data, therefore need to fetch full details first
+          try {
+            RestMethod restMethod = BioCatalogueClient.getInstance().
+                                                getBioCatalogueRestMethod(processorResource.getHref());
+            
+            // actual import of the service into the workflow
+            RESTFromBioCatalogueServiceDescription restServiceDescription = createRESTServiceDescriptionFromRESTMethod(restMethod);
+            WorkflowView.importServiceDescription(restServiceDescription, false);
+            
+            // prepare result of the operation to be shown in the the waiting dialog window
+            String warnings = extractWarningsFromRESTServiceDescription(restServiceDescription, false);
+            JLabel outcomes = new JLabel("<html>Selected " + TYPE.RESTMethod.getTypeName() + " was successfully added to the current workflow" + warnings + "</html>",
+                                         ResourceManager.getImageIcon(warnings.length() > 0 ? ResourceManager.WARNING_ICON : ResourceManager.TICK_ICON),
+                                         JLabel.CENTER);
+            outcomes.setIconTextGap(20);
+            return (outcomes);
+          }
+          catch (UnsupportedHTTPMethodException e) {
+            logger.error(e);
+            return (new JLabel(e.getMessage(), ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+          }
+          catch (Exception e) {
+            logger.error("Failed to fetch required details to add this " + TYPE.RESTMethod + " as a service to the current workflow.", e);
+            return (new JLabel("<html>Failed to fetch required details to add this " + TYPE.RESTMethod.getTypeName() + "<br>" +
+            		                      "as a service to the current workflow.</html>",
+                                      ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+          }
+        
+        // type not currently supported, but maybe in the future?
+        default: return (new JLabel("Adding " + resourceType.getCollectionName() + " to the current workflow is not yet possible",
+                                     ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+      }
+    }
+    
+    // definitely not supported type
+    return (new JLabel("<html>It is not possible to add resources of the provided type<br>" +
+                              "into the current workflow.</html>",
+                              ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+  }
+  
+  
+  /**
+   * 
+   * @param processorResource
+   * @return Outcome of inserting the processor into the current workflow as a
+   *         HTML-formatted string (with no opening and closing HTML tags).
+   */
+  public static JComponent insertProcesorIntoServicePanel(ResourceLink processorResource)
+  {
+    // check if this type of resource can be added to Service Panel
+    TYPE resourceType = Resource.getResourceTypeFromResourceURL(processorResource.getHref());
+    if (resourceType.isSuitableForAddingToServicePanel()) {
+      switch (resourceType) {
+        case SOAPOperation:
+          SoapOperation soapOp = (SoapOperation) processorResource;
+          try {
+            SoapService soapService = BioCatalogueClient.getInstance().
+                                        getBioCatalogueSoapService(soapOp.getAncestors().getSoapService().getHref());
+            SoapOperationIdentity soapOpId = new SoapOperationIdentity(soapService.getWsdlLocation(), soapOp.getName(), StringEscapeUtils.escapeHtml(soapOp.getDescription()));
+            WSDLOperationFromBioCatalogueServiceDescription wsdlOperationDescription = new WSDLOperationFromBioCatalogueServiceDescription(soapOpId);
+            BioCatalogueWSDLOperationServiceProvider.registerWSDLOperation(wsdlOperationDescription, null);
+            
+            return (new JLabel("Selected SOAP operation has been successfully added to the Service Panel.", 
+                               ResourceManager.getImageIcon(ResourceManager.TICK_ICON), JLabel.CENTER));
+          }
+          catch (Exception e) {
+            logger.error("Failed to fetch required details to add this SOAP service into the Service Panel.", e);
+            return (new JLabel("Failed to fetch required details to add this " +
+                               "SOAP service into the Service Panel.", ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+          }
+          
+        case RESTMethod:
+          try {
+            // received object may only contain limited data, therefore need to fetch full details first
+            RestMethod restMethod = BioCatalogueClient.getInstance().
+                                                  getBioCatalogueRestMethod(processorResource.getHref());
+            RESTFromBioCatalogueServiceDescription restServiceDescription = createRESTServiceDescriptionFromRESTMethod(restMethod);
+            
+            // actual insertion of the REST method into Service Panel
+            BioCatalogueRESTServiceProvider.registerNewRESTMethod(restServiceDescription, null);
+            
+            // prepare result of the operation to be shown in the the waiting dialog window
+            String warnings = extractWarningsFromRESTServiceDescription(restServiceDescription, true);
+            JLabel outcomes = new JLabel("<html>Selected REST method has been successfully added to the Service Panel" + warnings + "</html>", 
+                                         ResourceManager.getImageIcon(warnings.length() > 0 ? ResourceManager.WARNING_ICON : ResourceManager.TICK_ICON),
+                                         JLabel.CENTER);
+            outcomes.setIconTextGap(20);
+            return (outcomes);
+          }
+          catch (UnsupportedHTTPMethodException e) {
+            logger.error(e);
+            return (new JLabel(e.getMessage(), ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+          }
+          catch (Exception e) {
+            logger.error("Failed to fetch required details to add this REST service into the Service Panel.", e);
+            return (new JLabel("Failed to fetch required details to add this " +
+                "REST service into the Service Panel.", ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+          }
+        
+        // type not currently supported, but maybe in the future?
+        default: return (new JLabel("Adding " + resourceType.getCollectionName() + " to the Service Panel is not yet possible",
+            ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+      }
+    }
+    
+    // definitely not supported type
+    return (new JLabel("<html>It is not possible to add resources of the provided type<br>" +
+                              "into the Service Panel.</html>",
+                              ResourceManager.getImageIcon(ResourceManager.ERROR_ICON), JLabel.CENTER));
+  }
+  
+  /**
+   * Inserts all operations of the given parent SOAP or REST Web service resource link
+   * into Service Panel. Works for SOAP operations only at the moment.
+   * 
+   * @return Outcome of inserting operations into Service Panel as a
+   *         HTML-formatted string (with no opening and closing HTML tags).
+   */
+  public static JComponent insertAllOperationsIntoServicePanel(ResourceLink serviceResource)
+  {
+		// Check if this type of resource is a parent SOAP Web service
+		// whose operations can be added to the Service Panel
+		TYPE resourceType = Resource
+				.getResourceTypeFromResourceURL(serviceResource.getHref());
+		
+		Service service = null;
+		if (resourceType == TYPE.SOAPOperation) {
+			SoapService soapService = ((SoapOperation) serviceResource)
+					.getAncestors().getSoapService();
+
+			// Get the WSDL URL of the SOAP service
+			String wsdlURL = soapService.getWsdlLocation();
+
+			// Import this WSDL into Service panel - it will add all
+			// of
+			// its operations
+			if (BioCatalogueWSDLOperationServiceProvider.registerWSDLService(
+					wsdlURL, null)) {
+				return (new JLabel(
+						"Operation(s) of the SOAP service have been successfully added to the Service Panel.",
+						ResourceManager.getImageIcon(ResourceManager.TICK_ICON),
+						JLabel.CENTER));
+			} else {
+				return (new JLabel(
+						"Failed to insert the operations of the SOAP service "
+								+ " to the Service Panel.", ResourceManager
+								.getImageIcon(ResourceManager.ERROR_ICON),
+						JLabel.CENTER));
+			}
+		} else if (resourceType == TYPE.RESTMethod) {
+			RestService restService = ((RestMethod) serviceResource)
+			.getAncestors().getRestService();
+			for (RestMethod method : restService.getMethods().getRestMethodList()) {
+				
+			}
+		}
+
+		return (new JLabel(
+				"<html>It is not possible to add resources of the provided type<br>"
+						+ "into the Service Panel.</html>", ResourceManager
+						.getImageIcon(ResourceManager.ERROR_ICON),
+				JLabel.CENTER));
+	}
+
+  /**
+   * Instantiates a {@link RESTFromBioCatalogueServiceDescription} object from the {@link RestMethod}
+   * XML data obtained from BioCatalogue API.
+   * 
+   * @param restMethod
+   * @return
+   */
+  public static RESTFromBioCatalogueServiceDescription createRESTServiceDescriptionFromRESTMethod(RestMethod restMethod) throws UnsupportedHTTPMethodException
+  {
+    // if the type of the HTTP method is not supported, an exception will be throws
+    HTTP_METHOD httpMethod = HTTPMethodInterpreter.getHTTPMethodForRESTActivity(restMethod.getHttpMethodType());
+    
+    RESTFromBioCatalogueServiceDescription restServiceDescription = new RESTFromBioCatalogueServiceDescription();
+    restServiceDescription.setServiceName(Resource.getDisplayNameForResource(restMethod));
+    restServiceDescription.setDescription(StringEscapeUtils.escapeHtml(restMethod.getDescription()));
+    restServiceDescription.setHttpMethod(httpMethod);
+    restServiceDescription.setURLSignature(restMethod.getUrlTemplate());
+    
+    int outputRepresentationCount = restMethod.getOutputs().getRepresentations().getRestRepresentationList().size();
+    if (outputRepresentationCount > 0) {
+      if (outputRepresentationCount > 1) {
+        restServiceDescription.getDataWarnings().add(RESTFromBioCatalogueServiceDescription.AMBIGUOUS_ACCEPT_HEADER_VALUE);
+      }
+      restServiceDescription.setAcceptHeaderValue(restMethod.getOutputs().getRepresentations().getRestRepresentationList().get(0).getContentType());
+    }
+    else {
+      restServiceDescription.getDataWarnings().add(RESTFromBioCatalogueServiceDescription.DEFAULT_ACCEPT_HEADER_VALUE);
+    }
+    
+    int inputRepresentationCount = restMethod.getInputs().getRepresentations().getRestRepresentationList().size();
+    if (inputRepresentationCount > 0) {
+      if (inputRepresentationCount > 1) {
+        restServiceDescription.getDataWarnings().add(RESTFromBioCatalogueServiceDescription.AMBIGUOUS_CONTENT_TYPE_HEADER_VALUE);
+      }
+      restServiceDescription.setOutgoingContentType(restMethod.getInputs().getRepresentations().getRestRepresentationList().get(0).getContentType());
+    }
+    else if (RESTActivity.hasMessageBodyInputPort(httpMethod)) {
+      restServiceDescription.getDataWarnings().add(RESTFromBioCatalogueServiceDescription.DEFAULT_CONTENT_TYPE_HEADER_VALUE);
+    }
+    
+    return (restServiceDescription);
+  }
+  
+  
+  /**
+   * @param restServiceDescription {@link RESTFromBioCatalogueServiceDescription} to process.
+   * @param addingToServicePanel <code>true</code> indicates that the warning messages
+   *                             will assume that the processor is added to the service panel;
+   *                             <code>false</code> would mean that the processor is added to
+   *                             the current workflow.
+   * @return An HTML-formatted string (with no opening-closing HTML tags) that lists
+   *         any warnings that have been recorded during the {@link RESTFromBioCatalogueServiceDescription}
+   *         object creation. Empty string will be returned if there are no warnings.
+   */
+  public static String extractWarningsFromRESTServiceDescription(RESTFromBioCatalogueServiceDescription restServiceDescription,
+      boolean addingToServicePanel)
+  {
+    String messageSuffix = addingToServicePanel ?
+                           " once you add it into the workflow" :
+                           "";
+    
+    String warnings = "";
+    if (restServiceDescription.getDataWarnings().contains(RESTFromBioCatalogueServiceDescription.AMBIGUOUS_ACCEPT_HEADER_VALUE)) {
+        warnings += "<br><br>Service Catalogue description of this REST method contains more than one<br>" +
+                            "representation of the method's outputs - the first one was used.<br>" +
+                            "Please check value of the 'Accept' header in the configuration<br>" +
+                            "of the imported service" + messageSuffix + ".";
+    }
+    else if (restServiceDescription.getDataWarnings().contains(RESTFromBioCatalogueServiceDescription.DEFAULT_ACCEPT_HEADER_VALUE)) {
+      warnings += "<br><br>Service Catalogue description of this REST method does not contain any<br>" +
+                          "representations of the method's outputs - default value was used.<br>" +
+                          "Please check value of the 'Accept' header in the configuration<br>" +
+                          "of the imported service" + messageSuffix + ".";
+    }
+    
+    if (restServiceDescription.getDataWarnings().contains(RESTFromBioCatalogueServiceDescription.AMBIGUOUS_CONTENT_TYPE_HEADER_VALUE)) {
+        warnings += "<br><br>Service Catalogue description of this REST method contains more than one<br>" +
+                            "representation of the method's input data - the first one was used.<br>" +
+                            "Please check value of the 'Content-Type' header in the configuration<br>" +
+                            "of the imported service" + messageSuffix + ".";
+    }
+    else if (restServiceDescription.getDataWarnings().contains(RESTFromBioCatalogueServiceDescription.DEFAULT_CONTENT_TYPE_HEADER_VALUE)) {
+      warnings += "<br><br>Service Catalogue description of this REST method does not contain any<br>" +
+                          "representations of the method's input data - default value was used.<br>" +
+                          "Please check value of the 'Content-Type' header in the configuration<br>" +
+                          "of the imported service" + messageSuffix + ".";
+    }
+    
+    if (warnings.length() > 0) {
+      warnings = "<br><br>WARNINGS:" + warnings;
+    }
+    
+    return (warnings);
+  }
+  
+  
+  
+  /**
+   * @param activityPort Probably comes from contextual selection - must be either
+   *         ActivityInputPort or ActivityOutputPort.
+   * @return SOAP input / output port details (WSDL location, operation name, port name) from
+   *         ActivityInputPort/ActivityOutputPort which is obtained from contextual selection in the Dataflow.
+   */
+  public static <T extends Port> SoapOperationPortIdentity extractSoapOperationPortDetailsFromActivityInputOutputPort(T activityPort)
+  {
+    // check that we have the correct instance of Port here - either ActivityInputPort or ActivityOutputPort
+    boolean hasInputPort;
+    if (activityPort instanceof ActivityInputPort) {
+      hasInputPort = true;
+    }
+    else if (activityPort instanceof ActivityOutputPort) {
+      hasInputPort = false;
+    }
+    else {
+      // ERROR - wrong type supplied
+      return new SoapOperationPortIdentity("Activity port from the contextual selection was not of correct type. Impossible to create preview.");
+    }
+    
+    // get parent processor details
+    Dataflow currentDataflow = FileManager.getInstance().getCurrentDataflow();
+    Collection<Processor> processors = null;
+    if (hasInputPort) {
+      processors = Tools.getProcessorsWithActivityInputPort(currentDataflow, (ActivityInputPort)activityPort);
+    }
+    else {
+      processors = Tools.getProcessorsWithActivityOutputPort(currentDataflow, (ActivityOutputPort)activityPort);
+    }
+    
+    // TODO - doesn't take into account that it's possible to have several
+    SoapOperationIdentity soapOperationDetails = extractSoapOperationDetailsFromProcessor(processors.toArray(new Processor[]{})[0]);
+    
+    // if no error happened, add port details and return
+    if (!soapOperationDetails.hasError()) {
+      return (new SoapOperationPortIdentity(soapOperationDetails.getWsdlLocation(),
+                                             soapOperationDetails.getOperationName(),
+                                             activityPort.getName(), hasInputPort));
+    }
+    else {
+      // error...
+      return (new SoapOperationPortIdentity(soapOperationDetails.getErrorDetails()));
+    }
+  }
+  
+  
+  /**
+   * Uses contextual selection to extract WSDL location and operation name of the
+   * currently selected processor within the Design view of current workflow. 
+   * 
+   * @param contextualSelection Selection that was made in the Design view.
+   * @return Details of the SOAP operation that acts as a processor wrapped into
+   *         this single instance. If any problems occurred while performing
+   *         contextual selection analysis, these are also recorded into the same
+   *         instance - before using the returned value the caller must check
+   *         <code>SoapOperationIdentity.hasError()</code> value.
+   */
+  public static SoapOperationIdentity extractSoapOperationDetailsFromProcessorContextualSelection(ContextualSelection contextualSelection)
+  {
+    if (!(contextualSelection.getSelection() instanceof Processor)) {
+      return (new SoapOperationIdentity("ERROR: It is only possible to extract " +
+      		"SOAP operation details from the service."));
+    }
+    
+    // now we know it's a Processor
+    Processor processor = (Processor)contextualSelection.getSelection();
+    return (extractSoapOperationDetailsFromProcessor(processor));
+  }
+  
+  
+  /**
+   * Worker method for <code>extractSoapOperationDetailsFromProcessorContextualSelection()</code>.
+   * 
+   * @param processor
+   * @return
+   */
+  public static SoapOperationIdentity extractSoapOperationDetailsFromProcessor(Processor processor)
+  {
+    List<? extends Activity> activityList = (List<? extends Activity>) processor.getActivityList();
+    
+    if (activityList == null || activityList.size() == 0) {
+      return (new SoapOperationIdentity("ERROR: Selected processor doesn't have any activities - " +
+          "impossible to extract SOAP operation details."));
+    }
+    else {
+      // take only the first activity - TODO: figure out what should be done here...
+      Activity activity = activityList.get(0);
+      if (activity instanceof WSDLActivity) {
+        WSDLActivity a = (WSDLActivity)activity;
+        return (new SoapOperationIdentity(a.getConfiguration().getWsdl(), a.getConfiguration().getOperation(), null));
+      }
+      else {
+        return (new SoapOperationIdentity("Service Catalogue integration only works with WSDL Activities at the moment"));
+      }
+    }
+  }
+  
+  
+  /**
+   * @param contextualSelection
+   * @return A list of all WSDL activities (the only supported processors by BioCatalogue plugin for now).
+   */
+  public static List<SoapProcessorIdentity> extractSupportedProcessorsFromDataflow(ContextualSelection contextualSelection)
+  {
+    // check that there was a correct contextual selection
+    if (!(contextualSelection.getSelection() instanceof Dataflow)) {
+      logger.error("It is only possible to extract supported services from the workflow.");
+      return (new ArrayList<SoapProcessorIdentity>());
+    }
+    
+    // first extract all processors
+    Dataflow dataflow = (Dataflow)contextualSelection.getSelection();
+    List<? extends Processor> allProcessors = dataflow.getEntities(Processor.class);
+    
+    // now filter out any processors that are not WSDL activities
+    List<SoapProcessorIdentity> supportedProcessors = new ArrayList<SoapProcessorIdentity>();
+    for (Processor proc : allProcessors) {
+      List<? extends Activity> activityList = (List<? extends Activity>) proc.getActivityList();
+      if (activityList != null && activityList.size() > 0) {
+        // take only the first activity - TODO: figure out what should be done here...
+        Activity activity = activityList.get(0);
+        if (activity instanceof WSDLActivity) {
+          WSDLActivity a = (WSDLActivity)activity;
+          supportedProcessors.add(new SoapProcessorIdentity(a.getConfiguration().getWsdl(),
+                                                            a.getConfiguration().getOperation(),
+                                                            proc.getLocalName()));
+        }
+      }
+    }
+    
+    // return all found processors
+    return (supportedProcessors);
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfiguration.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfiguration.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfiguration.java
new file mode 100644
index 0000000..3985ac5
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfiguration.java
@@ -0,0 +1,68 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import uk.org.taverna.configuration.AbstractConfigurable;
+
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+
+/**
+ *
+ *
+ * @author Sergejs Aleksejevs
+ */
+public class BioCataloguePluginConfiguration extends AbstractConfigurable
+{
+  public static final String SERVICE_CATALOGUE_BASE_URL = "ServiceCatalogue_Base_URL";
+  public static final String SOAP_OPERATIONS_IN_SERVICE_PANEL = "SOAP_Operations_in_Service_Panel";
+  public static final String REST_METHODS_IN_SERVICE_PANEL = "REST_Methods_in_Service_Panel";
+
+
+  private static class Singleton {
+    private static BioCataloguePluginConfiguration instance = new BioCataloguePluginConfiguration();
+  }
+
+  // private static Logger logger = Logger.getLogger(MyExperimentConfiguration.class);
+
+  private Map<String, String> defaultPropertyMap;
+
+
+  public static BioCataloguePluginConfiguration getInstance() {
+    return Singleton.instance;
+  }
+
+  public String getCategory() {
+    return "general";
+  }
+
+  public Map<String,String> getDefaultPropertyMap() {
+    if (defaultPropertyMap == null) {
+      defaultPropertyMap = new HashMap<String,String>();
+      defaultPropertyMap.put(SERVICE_CATALOGUE_BASE_URL, BioCatalogueClient.DEFAULT_API_LIVE_SERVER_BASE_URL);
+    }
+    return defaultPropertyMap;
+  }
+
+  public String getDisplayName() {
+    return "Service catalogue";
+  }
+
+  public String getFilePrefix() {
+    return "ServiceCatalogue";
+  }
+
+  public String getUUID() {
+    return "4daac25c-bd56-4f90-b909-1e49babe5197";
+  }
+
+
+  /**
+   * Just a "proxy" method - {@link AbstractConfigurable#store()}
+   * is not visible to the users of instances of this class otherwise.
+   */
+  public void store() {
+    super.store();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfigurationPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfigurationPanel.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfigurationPanel.java
new file mode 100644
index 0000000..a83a223
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfigurationPanel.java
@@ -0,0 +1,448 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.config;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProxySelector;
+import java.net.URL;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+//import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.log4j.Logger;
+import org.jdom.Attribute;
+import org.jdom.Document;
+import org.jdom.input.SAXBuilder;
+
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
+
+
+/**
+ * 
+ * @author Sergejs Aleksejevs
+ */
+@SuppressWarnings("serial")
+public class BioCataloguePluginConfigurationPanel extends JPanel
+{
+	public static final String APPLICATION_XML_MIME_TYPE = "application/xml";
+
+	public static String PROXY_HOST = "http.proxyHost";
+	public static String PROXY_PORT = "http.proxyPort";
+	public static String PROXY_USERNAME = "http.proxyUser";
+	public static String PROXY_PASSWORD = "http.proxyPassword";
+	
+	// 1.0.0b and higher until the first digit changes, as according to "Semantic Versioning" 
+	// from http://www.biocatalogue.org/wiki/doku.php?id=public:api:changelog
+	// "Major version X (X.y.z | X > 0) MUST be incremented if any backwards 
+	// incompatible changes are introduced to the public API. It MAY include minor and patch level changes."
+	public static String[] MIN_SUPPORTED_BIOCATALOGUE_API_VERSION = {"1", "1", "0"}; // major, minor and patch versions
+	public static String API_VERSION = "apiVersion";
+
+	private BioCataloguePluginConfiguration configuration = 
+                          BioCataloguePluginConfiguration.getInstance();
+  
+  
+	// UI elements
+	JTextField tfBioCatalogueAPIBaseURL;
+
+	private Logger logger = Logger.getLogger(BioCataloguePluginConfigurationPanel.class);  
+  
+	public BioCataloguePluginConfigurationPanel() {
+		initialiseUI();
+		resetFields();
+	}
+  
+  private void initialiseUI()
+  {
+    this.setLayout(new GridBagLayout());
+    GridBagConstraints c = new GridBagConstraints();
+    c.fill = GridBagConstraints.HORIZONTAL;
+    c.anchor = GridBagConstraints.NORTHWEST;
+    c.weightx = 1.0;
+    
+    c.gridx = 0;
+    c.gridy = 0;
+    JTextArea taDescription = new JTextArea("Configure the Service Catalogue integration functionality");
+    taDescription.setFont(taDescription.getFont().deriveFont(Font.PLAIN, 11));
+    taDescription.setLineWrap(true);
+    taDescription.setWrapStyleWord(true);
+    taDescription.setEditable(false);
+    taDescription.setFocusable(false);
+    taDescription.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+    this.add(taDescription, c);
+    
+    
+    c.gridy++;
+    c.insets = new Insets(20, 0, 0, 0);
+    JLabel jlBioCatalogueAPIBaseURL = new JLabel("Base URL of the Service Catalogue instance to connect to:");
+    this.add(jlBioCatalogueAPIBaseURL, c);
+    
+    c.gridy++;
+    c.insets = new Insets(0, 0, 0, 0);
+    tfBioCatalogueAPIBaseURL = new JTextField();
+    this.add(tfBioCatalogueAPIBaseURL, c);
+    
+    
+    c.gridy++;
+    c.insets = new Insets(30, 0, 0, 0);
+    // We are not removing BioCatalogue services from its config panel any more - 
+    // they are being handled by the Taverna's Service Registry
+//    JButton bForgetStoredServices = new JButton("Forget services added to Service Panel by BioCatalogue Plugin");
+//    bForgetStoredServices.addActionListener(new ActionListener() {
+//      public void actionPerformed(ActionEvent e)
+//      {
+//        int response = JOptionPane.showConfirmDialog(null, // no way T2ConfigurationFrame instance can be obtained to be used as a parent...
+//                                       "Are you sure you want to clear all SOAP operations and REST methods\n" +
+//                                       "that were added to the Service Panel by the BioCatalogue Plugin?\n\n" +
+//                                       "This action is permanent is cannot be undone.\n\n" +
+//                                       "Do you want to proceed?", "BioCatalogue Plugin", JOptionPane.YES_NO_OPTION);
+//        
+//        if (response == JOptionPane.YES_OPTION)
+//        {
+//          BioCatalogueServiceProvider.clearRegisteredServices();
+//          JOptionPane.showMessageDialog(null,  // no way T2ConfigurationFrame instance can be obtained to be used as a parent...
+//                          "Stored services have been successfully cleared, but will remain\n" +
+//                          "being shown in Service Panel during this session.\n\n" +
+//                          "They will not appear in the Service Panel after you restart Taverna.",
+//                          "BioCatalogue Plugin", JOptionPane.INFORMATION_MESSAGE);
+//        }
+//      }
+//    });
+//    this.add(bForgetStoredServices, c);
+    
+    
+    JButton bLoadDefaults = new JButton("Load Defaults");
+    bLoadDefaults.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        loadDefaults();
+      }
+    });
+    
+    JButton bReset = new JButton("Reset");
+    bReset.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        resetFields();
+      }
+    });
+    
+    JButton bApply = new JButton("Apply");
+    bApply.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        applyChanges();
+      }
+    });
+    
+    JPanel jpActionButtons = new JPanel();
+    jpActionButtons.add(bLoadDefaults);
+    jpActionButtons.add(bReset);
+    jpActionButtons.add(bApply);
+    c.insets = new Insets(30, 0, 0, 0);
+    c.gridy++;
+    c.weighty = 1.0;
+    this.add(jpActionButtons, c);
+  }
+  
+  
+  /**
+   * Resets all fields to the last saved configuration.
+   */
+  private void resetFields() {
+    tfBioCatalogueAPIBaseURL.setText(configuration.getProperty(BioCataloguePluginConfiguration.SERVICE_CATALOGUE_BASE_URL));
+  }
+  
+  /**
+   * Resets all fields to the default values.
+   */
+  private void loadDefaults() {
+    tfBioCatalogueAPIBaseURL.setText(configuration.getDefaultProperty(BioCataloguePluginConfiguration.SERVICE_CATALOGUE_BASE_URL));
+  }
+  
+  /**
+   * Saves recent changes to the configuration parameter map.
+   * Some input validation is performed as well.
+   */
+	private void applyChanges() {
+		// --- BioCatalogue BASE URL ---
+
+		String candidateBaseURL = tfBioCatalogueAPIBaseURL.getText();
+		if (candidateBaseURL.length() == 0) {
+			JOptionPane.showMessageDialog(this,
+					"Service Catalogue base URL must not be blank",
+					"Service Catalogue Configuration", JOptionPane.WARNING_MESSAGE);
+			tfBioCatalogueAPIBaseURL.requestFocusInWindow();
+			return;
+		} else {
+			try {
+				new URL(candidateBaseURL);
+			} catch (MalformedURLException e) {
+				JOptionPane
+						.showMessageDialog(
+								this,
+								"Currently set Service Catalogue instance URL is not valid\n." +
+								"Please check the URL and try again.",
+								"Service Catalogue Configuration",
+								JOptionPane.WARNING_MESSAGE);
+				tfBioCatalogueAPIBaseURL.selectAll();
+				tfBioCatalogueAPIBaseURL.requestFocusInWindow();
+				return;
+			}
+
+			// check if the base URL has changed from the last saved state
+			if (!candidateBaseURL
+					.equals(configuration
+							.getProperty(BioCataloguePluginConfiguration.SERVICE_CATALOGUE_BASE_URL))) {
+					// Perform various checks on the new URL
+
+				// Do a GET with "Accept" header set to "application/xml"
+				// We are expecting a 200 OK and an XML doc in return that
+				// contains the BioCataogue version number element.
+				DefaultHttpClient httpClient = new DefaultHttpClient();
+				
+				// Set the proxy settings, if any
+				if (System.getProperty(PROXY_HOST) != null
+						&& !System.getProperty(PROXY_HOST).equals("")) {
+					// Instruct HttpClient to use the standard
+					// JRE proxy selector to obtain proxy information
+					ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
+							httpClient.getConnectionManager().getSchemeRegistry(), ProxySelector
+									.getDefault());
+					httpClient.setRoutePlanner(routePlanner);
+					// Do we need to authenticate the user to the proxy?
+					if (System.getProperty(PROXY_USERNAME) != null
+							&& !System.getProperty(PROXY_USERNAME).equals("")) {
+						// Add the proxy username and password to the list of credentials
+						httpClient.getCredentialsProvider().setCredentials(
+								new AuthScope(System.getProperty(PROXY_HOST),Integer.parseInt(System.getProperty(PROXY_PORT))),
+								new UsernamePasswordCredentials(System.getProperty(PROXY_USERNAME), System.getProperty(PROXY_PASSWORD)));
+					}
+				}
+				
+				HttpGet httpGet = new HttpGet(candidateBaseURL);
+				httpGet.setHeader("Accept", APPLICATION_XML_MIME_TYPE);
+
+				// Execute the request
+				HttpContext localContext = new BasicHttpContext();
+				HttpResponse httpResponse;
+				try {
+					httpResponse = httpClient.execute(httpGet, localContext);
+				} catch (Exception ex1) {
+					logger.error("Service Catalogue preferences configuration: Failed to do "
+							+ httpGet.getRequestLine(), ex1);
+					// Warn the user
+					JOptionPane.showMessageDialog(this,
+							"Failed to connect to the URL of the Service Catalogue instance.\n"
+									+ "Please check the URL and try again.",
+							"Service Catalogue Configuration",
+							JOptionPane.INFORMATION_MESSAGE);
+					
+					// Release resource
+					httpClient.getConnectionManager().shutdown();
+					
+					tfBioCatalogueAPIBaseURL.requestFocusInWindow();
+					return;
+				}
+
+				if (httpResponse.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK) { // HTTP/1.1 200 OK
+					HttpEntity httpEntity = httpResponse.getEntity();
+					String contentType = httpEntity.getContentType().getValue()
+							.toLowerCase().trim();
+					logger
+							.info("Service Catalogue preferences configuration: Got 200 OK when testing the Service Catalogue instance by doing "
+									+ httpResponse.getStatusLine()
+									+ ". Content type of response "
+									+ contentType);
+					if (contentType.startsWith(APPLICATION_XML_MIME_TYPE)) {
+						String value = null;
+						Document doc = null;
+						try {
+							value = readResponseBodyAsString(httpEntity)
+									.trim();
+							// Try to read this string into an XML document
+							SAXBuilder builder = new SAXBuilder();
+							byte[] bytes = value.getBytes("UTF-8");
+							doc = builder.build(new ByteArrayInputStream(bytes));
+						} catch (Exception ex2) {
+							logger.error("Service Catalogue preferences configuration: Failed to build an XML document from the response.", ex2);
+							// Warn the user
+							JOptionPane.showMessageDialog(this,
+									"Failed to get the expected response body when testing the Service Catalogue instance.\n"
+											+ "The URL is probably wrong. Please check it and try again.",
+									"Service Catalogue Configuration",
+									JOptionPane.INFORMATION_MESSAGE);
+							tfBioCatalogueAPIBaseURL.requestFocusInWindow();
+							return;
+						}
+						finally{
+							// Release resource
+							httpClient.getConnectionManager().shutdown();
+						}
+						// Get the version element from the XML document
+						Attribute apiVersionAttribute = doc.getRootElement().getAttribute(API_VERSION);
+						if (apiVersionAttribute != null){
+							String apiVersion = apiVersionAttribute.getValue();
+							String versions[] = apiVersion.split("[.]");
+							String majorVersion = versions[0];
+							String minorVersion = versions[1];
+							try {
+							//String patchVersion = versions[2]; // we are not comparing the patch versions
+							String supportedMajorVersion = MIN_SUPPORTED_BIOCATALOGUE_API_VERSION[0];
+							String supportedMinorVersion = MIN_SUPPORTED_BIOCATALOGUE_API_VERSION[1];
+							Integer iSupportedMajorVersion = Integer.parseInt(supportedMajorVersion);
+							Integer iMajorVersion = Integer.parseInt(majorVersion);
+							Integer iSupportedMinorVersion = Integer.parseInt(supportedMinorVersion);
+							Integer iMinorVersion = Integer.parseInt(minorVersion);
+							if (!(iSupportedMajorVersion == iMajorVersion && 
+									iSupportedMinorVersion <= iMinorVersion)){
+								// Warn the user
+								JOptionPane
+										.showMessageDialog(
+												this,
+												"The version of the Service Catalogue instance you are trying to connect to is not supported.\n"
+														+ "Please change the URL and try again.",
+												"Service Catalogue Configuration",
+												JOptionPane.INFORMATION_MESSAGE);
+								tfBioCatalogueAPIBaseURL.requestFocusInWindow();
+								return;		
+							}
+							} catch (Exception e) {
+								logger.error(e);
+							}
+						} // if null - we'll try to do our best to connect to BioCatalogue anyway
+					} else {
+						logger
+								.error("Service Catalogue preferences configuration: Failed to get the expected response content type when testing the Service Catalogue instance. "
+										+ httpGet.getRequestLine()
+										+ " returned content type '"
+										+ contentType
+										+ "'; expected response content type is 'application/xml'.");
+						// Warn the user
+						JOptionPane
+								.showMessageDialog(
+										this,
+										"Failed to get the expected response content type when testing the Service Catalogue instance.\n"
+										+ "The URL is probably wrong. Please check it and try again.",
+										"Service Catalogue Plugin",
+										JOptionPane.INFORMATION_MESSAGE);
+						tfBioCatalogueAPIBaseURL.requestFocusInWindow();
+						return;
+					}
+				}
+				else{
+					logger
+							.error("Service Catalogue preferences configuration: Failed to get the expected response status code when testing the Service Catalogue instance. "
+									+ httpGet.getRequestLine()
+									+ " returned the status code "
+									+ httpResponse.getStatusLine()
+											.getStatusCode() + "; expected status code is 200 OK.");
+					// Warn the user
+					JOptionPane
+							.showMessageDialog(
+									this,
+									"Failed to get the expected response status code when testing the Service Catalogue instance.\n"
+									+ "The URL is probably wrong. Please check it and try again.",
+									"Service Catalogue Configuration",
+									JOptionPane.INFORMATION_MESSAGE);
+					tfBioCatalogueAPIBaseURL.requestFocusInWindow();
+					return;					
+				}
+
+				// Warn the user of the changes in the BioCatalogue base URL
+				JOptionPane
+						.showMessageDialog(
+								this,
+								"You have updated the Service Catalogue base URL.\n"
+										+ "This does not take effect until you restart Taverna.",
+										"Service catalogue Configuration",
+								JOptionPane.INFORMATION_MESSAGE);
+
+			}
+
+			// the new base URL seems to be valid - can save it into config
+			// settings
+			configuration.setProperty(
+					BioCataloguePluginConfiguration.SERVICE_CATALOGUE_BASE_URL,
+					candidateBaseURL);
+
+/*			// also update the base URL in the BioCatalogueClient
+			BioCatalogueClient.getInstance()
+					.setBaseURL(candidateBaseURL);*/
+		}
+
+	}
+  
+  
+  /**
+   * For testing only.
+   */
+  public static void main(String[] args) {
+    JFrame theFrame = new JFrame();
+    theFrame.add(new BioCataloguePluginConfigurationPanel());
+    theFrame.pack();
+    theFrame.setLocationRelativeTo(null);
+    theFrame.setVisible(true);
+  }
+
+	/**
+	 * Worker method that extracts the content of the received HTTP message as a
+	 * string. It also makes use of the charset that is specified in the
+	 * Content-Type header of the received data to read it appropriately.
+	 * 
+	 * @param entity
+	 * @return
+	 * @throws IOException
+	 */
+	// Taken from HTTPRequestHandler in rest-activity by Sergejs Aleksejevs
+	private static String readResponseBodyAsString(HttpEntity entity)
+			throws IOException {
+		// get charset name
+		String charset = null;
+		String contentType = entity.getContentType().getValue().toLowerCase();
+
+		String[] contentTypeParts = contentType.split(";");
+		for (String contentTypePart : contentTypeParts) {
+			contentTypePart = contentTypePart.trim();
+			if (contentTypePart.startsWith("charset=")) {
+				charset = contentTypePart.substring("charset=".length());
+			}
+		}
+
+		// read the data line by line
+		StringBuilder responseBodyString = new StringBuilder();
+		BufferedReader reader = new BufferedReader(new InputStreamReader(entity
+				.getContent(), charset));
+
+		String str;
+		while ((str = reader.readLine()) != null) {
+			responseBodyString.append(str + "\n");
+		}
+
+		return (responseBodyString.toString());
+	}
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfigurationUIFactory.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfigurationUIFactory.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfigurationUIFactory.java
new file mode 100644
index 0000000..f5baa70
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/config/BioCataloguePluginConfigurationUIFactory.java
@@ -0,0 +1,27 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.config;
+
+import javax.swing.JPanel;
+
+import uk.org.taverna.configuration.Configurable;
+import uk.org.taverna.configuration.ConfigurationUIFactory;
+
+/**
+ *
+ * @author Sergejs Aleksejevs
+ */
+public class BioCataloguePluginConfigurationUIFactory implements ConfigurationUIFactory
+{
+
+  public boolean canHandle(String uuid) {
+    return uuid.equals(getConfigurable().getUUID());
+  }
+
+  public Configurable getConfigurable() {
+    return BioCataloguePluginConfiguration.getInstance();
+  }
+
+  public JPanel getConfigurationPanel() {
+    return new BioCataloguePluginConfigurationPanel();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginInputPortContextViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginInputPortContextViewFactory.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginInputPortContextViewFactory.java
new file mode 100644
index 0000000..d067cce
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginInputPortContextViewFactory.java
@@ -0,0 +1,45 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.contextual_views;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import net.sf.taverna.biocatalogue.model.SoapOperationPortIdentity;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.Integration;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityInputPort;
+
+
+public class BioCataloguePluginInputPortContextViewFactory implements
+		ContextualViewFactory<ActivityInputPort> {
+
+	public boolean canHandle(Object selection)
+	{
+		// TODO - HACK: this would stop showing the contextual view in case of any error,
+    //        not just in case of unsupported contextual selection; this needs to be
+    //        changed, so that useful error messages are still displayed in the
+    //        contextual view
+    if (selection instanceof ActivityInputPort)
+    {
+      SoapOperationPortIdentity portDetails = Integration.
+          extractSoapOperationPortDetailsFromActivityInputOutputPort((ActivityInputPort)selection);
+      boolean canHandleSelection = !portDetails.hasError();
+      if (!canHandleSelection) {
+        Logger.getLogger(BioCataloguePluginProcessorContextViewFactory.class).debug(
+            "Input port contextual view not shown due to some condition: " + portDetails.getErrorDetails());
+      }
+      
+      return (canHandleSelection);
+    }
+    else {
+      return (false);
+    }
+	}
+	
+	public List<ContextualView> getViews(ActivityInputPort selection) {
+		return Arrays.<ContextualView>asList(new ProcessorInputPortView(selection));
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginOutputPortContextViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginOutputPortContextViewFactory.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginOutputPortContextViewFactory.java
new file mode 100644
index 0000000..1014a54
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginOutputPortContextViewFactory.java
@@ -0,0 +1,45 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.contextual_views;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import net.sf.taverna.biocatalogue.model.SoapOperationPortIdentity;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.Integration;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityOutputPort;
+
+
+public class BioCataloguePluginOutputPortContextViewFactory implements
+		ContextualViewFactory<ActivityOutputPort> {
+  
+	public boolean canHandle(Object selection)
+	{
+		// TODO - HACK: this would stop showing the contextual view in case of any error,
+    //        not just in case of unsupported contextual selection; this needs to be
+    //        changed, so that useful error messages are still displayed in the
+    //        contextual view
+    if (selection instanceof ActivityOutputPort)
+    {
+      SoapOperationPortIdentity portDetails = Integration.
+          extractSoapOperationPortDetailsFromActivityInputOutputPort((ActivityOutputPort)selection);
+      boolean canHandleSelection = !portDetails.hasError();
+      if (!canHandleSelection) {
+        Logger.getLogger(BioCataloguePluginProcessorContextViewFactory.class).debug(
+            "Output port contextual view not shown due to some condition: " + portDetails.getErrorDetails());
+      }
+      
+      return (canHandleSelection);
+    }
+    else {
+      return (false);
+    }
+	}
+	
+	public List<ContextualView> getViews(ActivityOutputPort selection) {
+		return Arrays.<ContextualView>asList(new ProcessorOutputPortView(selection));
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginProcessorContextViewFactory.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginProcessorContextViewFactory.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginProcessorContextViewFactory.java
new file mode 100644
index 0000000..05380d3
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/BioCataloguePluginProcessorContextViewFactory.java
@@ -0,0 +1,43 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.contextual_views;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.Integration;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory;
+import net.sf.taverna.t2.workflowmodel.Processor;
+
+public class BioCataloguePluginProcessorContextViewFactory implements
+		ContextualViewFactory<Processor> {
+  
+	public boolean canHandle(Object selection)
+	{
+		// TODO - HACK: this would stop showing the contextual view in case of any error,
+	  //        not just in case of unsupported contextual selection; this needs to be
+	  //        changed, so that useful error messages are still displayed in the
+	  //        contextual view
+	  if (selection instanceof Processor)
+	  {
+	    SoapOperationIdentity opId = Integration.extractSoapOperationDetailsFromProcessor((Processor) selection);
+	    boolean canHandleSelection = !opId.hasError();
+		  if (!canHandleSelection) {
+	      Logger.getLogger(BioCataloguePluginProcessorContextViewFactory.class).debug(
+	          "Service's contextual view not shown due to some condition: " + opId.getErrorDetails());
+	    }
+		  
+		  return (canHandleSelection);
+	  }
+	  else {
+	    return (false);
+	  }
+	}
+	
+	public List<ContextualView> getViews(Processor selection) {
+		return Arrays.<ContextualView>asList(new ProcessorView(selection));
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorInputPortView.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorInputPortView.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorInputPortView.java
new file mode 100644
index 0000000..4e307f1
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorInputPortView.java
@@ -0,0 +1,52 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.contextual_views;
+
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+
+import net.sf.taverna.biocatalogue.model.BioCataloguePluginConstants;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityInputPort;
+
+
+public class ProcessorInputPortView extends ContextualView
+{
+	private final ActivityInputPort inputPort;
+	private JPanel jPanel;
+
+	public ProcessorInputPortView(ActivityInputPort inputPort) {
+		this.inputPort = inputPort;
+		
+		jPanel = new JPanel();
+		
+		// NB! This is required to have the body of this contextual
+		// view added to the main view; otherwise, body will be
+		// blank
+		initView();
+	}
+	
+	@Override
+	public JComponent getMainFrame()
+	{
+		return jPanel;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Service Catalogue Information";
+	} 
+
+	@Override
+	public void refreshView()
+	{
+	  // this actually causes the parent container to validate itself,
+    // which is what is needed here
+    this.revalidate();
+    this.repaint();
+	}
+	
+	@Override
+	public int getPreferredPosition() {
+		return BioCataloguePluginConstants.CONTEXTUAL_VIEW_PREFERRED_POSITION;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorOutputPortView.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorOutputPortView.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorOutputPortView.java
new file mode 100644
index 0000000..feff0f4
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorOutputPortView.java
@@ -0,0 +1,52 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.contextual_views;
+
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+
+import net.sf.taverna.biocatalogue.model.BioCataloguePluginConstants;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityOutputPort;
+
+
+public class ProcessorOutputPortView extends ContextualView
+{
+	private final ActivityOutputPort outputPort;
+	private JPanel jPanel;
+
+	public ProcessorOutputPortView(ActivityOutputPort outputPort) {
+		this.outputPort = outputPort;
+		
+		jPanel = new JPanel();
+		
+		// NB! This is required to have the body of this contextual
+		// view added to the main view; otherwise, body will be
+		// blank
+		initView();
+	}
+	
+	@Override
+	public JComponent getMainFrame()
+	{
+		return jPanel;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Service Catalogue Information";
+	} 
+
+	@Override
+	public void refreshView()
+	{
+	  // this actually causes the parent container to validate itself,
+    // which is what is needed here
+    this.revalidate();
+    this.repaint();
+	}
+	
+	@Override
+	public int getPreferredPosition() {
+		return BioCataloguePluginConstants.CONTEXTUAL_VIEW_PREFERRED_POSITION;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorView.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorView.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorView.java
new file mode 100644
index 0000000..ea41805
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/contextual_views/ProcessorView.java
@@ -0,0 +1,229 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.contextual_views;
+
+import java.awt.Component;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.net.URI;
+import java.rmi.activation.UnknownObjectException;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+import net.sf.taverna.biocatalogue.model.BioCataloguePluginConstants;
+import net.sf.taverna.biocatalogue.model.ResourceManager;
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.biocatalogue.model.connectivity.BioCatalogueClient;
+import net.sf.taverna.t2.lang.ui.DeselectingButton;
+import net.sf.taverna.t2.lang.ui.ReadOnlyTextArea;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.MainComponentFactory;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.Integration;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check.ServiceHealthChecker;
+import net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check.ServiceMonitoringStatusInterpreter;
+import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView;
+import net.sf.taverna.t2.workflowmodel.Processor;
+
+import org.apache.log4j.Logger;
+import org.biocatalogue.x2009.xml.rest.Service;
+import org.biocatalogue.x2009.xml.rest.SoapOperation;
+
+
+public class ProcessorView extends ContextualView {
+	private final Processor processor;
+	private JPanel jPanel;
+	
+	private static Logger logger = Logger.getLogger(ProcessorView.class);
+	
+
+
+	public ProcessorView(Processor processor) {
+		this.processor = processor;
+		
+		jPanel = new JPanel();
+		
+		// this is required to have the body of this contextual
+		// view added to the main view; otherwise, body will be
+		// blank
+		initView();
+	}
+	
+
+	
+	@Override
+	public JComponent getMainFrame()
+	{
+	  Thread t = new Thread("loading processor data") {
+      public void run() {
+        final SoapOperationIdentity operationDetails = Integration.extractSoapOperationDetailsFromProcessor(processor);
+        
+        if (operationDetails.hasError()) {
+        	SwingUtilities.invokeLater(new RefreshThread(new JLabel(operationDetails.getErrorDetails().toString(),
+                    UIManager.getIcon("OptionPane.warningIcon"), JLabel.CENTER)));
+           return;
+        }
+        else {
+          BioCatalogueClient client = BioCatalogueClient.getInstance();
+          
+          if (client != null) {
+            try {
+              final SoapOperation soapOperation = client.lookupSoapOperation(operationDetails);
+              if (soapOperation == null) {
+            	  SwingUtilities.invokeLater(new RefreshThread(new JLabel("This service is not registered in the Service Catalogue",
+                          UIManager.getIcon("OptionPane.warningIcon"), JLabel.CENTER)));
+                 return;
+              }
+              
+              Service parentService = client.getBioCatalogueService(soapOperation.getAncestors().getService().getHref());
+              if (parentService == null) {
+               	  SwingUtilities.invokeLater(new RefreshThread(new JLabel("Problem while fetching monitoring data from the Service Catalogue",
+                          UIManager.getIcon("OptionPane.warningIcon"), JLabel.CENTER)));
+                 return;
+              }
+              
+              
+              // *** managed to get all necessary data successfully - present it ***
+              
+              // create status update panel
+              JButton jclServiceStatus = new DeselectingButton(
+                  new AbstractAction("Check monitoring status") {
+                    public void actionPerformed(ActionEvent e) {
+                      ServiceHealthChecker.checkWSDLProcessor(operationDetails);
+                    }
+                  });
+              jclServiceStatus.setAlignmentX(Component.LEFT_ALIGNMENT);
+              JLabel jlStatusMessage = new JLabel(parentService.getLatestMonitoringStatus().getMessage());
+              jlStatusMessage.setAlignmentX(Component.LEFT_ALIGNMENT);             
+              
+              // operation description
+              String operationDescription = (soapOperation.getDescription().length() > 0 ?
+                      soapOperation.getDescription() :
+                      "No description is available for this service");
+
+              ReadOnlyTextArea jlOperationDescription = new ReadOnlyTextArea(operationDescription);
+ 
+              jlOperationDescription.setAlignmentX(Component.LEFT_ALIGNMENT);              
+              
+              // a button to open preview of the service
+              JButton jbLaunchProcessorPreview = new DeselectingButton("Show on the Service Catalogue",
+            		  new ActionListener() {
+                  public void actionPerformed(ActionEvent e) {
+                    if (!operationDetails.hasError()) {
+                  	  String hrefString = soapOperation.getHref();
+     				   try {
+  						Desktop.getDesktop().browse(new URI(hrefString));
+  					    }
+  					    catch (Exception ex) {
+  					      logger.error("Failed while trying to open the URL in a standard browser; URL was: " +
+  					           hrefString, ex);
+  					    };
+                    }
+                    else {
+                      // this error message comes from Integration class extracting SOAP operation details from the contextual selection
+                      JOptionPane.showMessageDialog(null, operationDetails.getErrorDetails(), "Service Catalogue Error", JOptionPane.WARNING_MESSAGE);
+                    }
+                  }
+                },
+                "View this service on the Service Catalogue");
+              
+              JPanel jpPreviewButtonPanel = new JPanel();
+              jpPreviewButtonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+              jbLaunchProcessorPreview.setAlignmentX(Component.LEFT_ALIGNMENT);
+ //             jpPreviewButtonPanel.add(jbLaunchProcessorPreview);
+             // put everything together
+              JPanel jpInnerPane = new JPanel();
+              jpInnerPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+              jpInnerPane.setLayout(new BoxLayout(jpInnerPane, BoxLayout.Y_AXIS));
+              jpInnerPane.add(jlOperationDescription);
+              jpInnerPane.add(Box.createVerticalStrut(10));
+              jpInnerPane.add(jlStatusMessage);
+              jpInnerPane.add(Box.createVerticalStrut(10));
+              jpInnerPane.add(jclServiceStatus);
+              jpInnerPane.add(Box.createVerticalStrut(10));
+              jpInnerPane.add(jbLaunchProcessorPreview);
+             
+              JScrollPane spInnerPane = new JScrollPane(jpInnerPane);
+              
+              SwingUtilities.invokeLater(new RefreshThread(spInnerPane));
+              return;
+           }
+            catch (UnknownObjectException e) {
+            	SwingUtilities.invokeLater(new RefreshThread(new JLabel(e.getMessage(),
+                        UIManager.getIcon("OptionPane.informationIcon"), JLabel.CENTER)));
+            	return;
+             }
+            catch (Exception e) {
+              // a real error occurred while fetching data about selected processor
+             logger.error("ERROR: unexpected problem while trying to ", e);
+             SwingUtilities.invokeLater(new RefreshThread(new JLabel("An unknown problem has prevented Taverna from loading this preview",
+                     UIManager.getIcon("OptionPane.errorIcon"), JLabel.CENTER)));
+             return;
+            }
+          }
+          else {
+        	  SwingUtilities.invokeLater(new RefreshThread(new JLabel("Service Catalogue integration has not initialised yet. Please wait and try again.",
+                                  UIManager.getIcon("OptionPane.warningIcon"), JLabel.CENTER)));
+        	  return;
+         }
+        }
+      }
+	  };
+		
+	  jPanel.removeAll();
+    jPanel.setPreferredSize(new Dimension(200,200));
+    jPanel.setLayout(new GridLayout());
+    jPanel.add(new JLabel(ResourceManager.getImageIcon(ResourceManager.BAR_LOADER_ORANGE), JLabel.CENTER));
+	  t.start();
+		return jPanel;
+	}
+
+	@Override
+	public String getViewTitle() {
+		return "Service Catalogue Information";
+	} 
+
+	@Override
+	public void refreshView()
+	{
+	  // this actually causes the parent container to validate itself,
+	  // which is what is needed here
+	  this.revalidate();
+	  this.repaint();
+	}
+	
+	@Override
+	public int getPreferredPosition() {
+		return BioCataloguePluginConstants.CONTEXTUAL_VIEW_PREFERRED_POSITION;
+	}
+	
+	class RefreshThread extends Thread {
+		private final Component component;
+
+		public RefreshThread (Component component) {
+			this.component = component;		
+		}
+		
+		public void run() {
+			jPanel.removeAll();
+			if (component != null) {
+				jPanel.add(component);
+			}
+			refreshView();
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthCheck.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthCheck.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthCheck.java
new file mode 100644
index 0000000..2461c2c
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthCheck.java
@@ -0,0 +1,40 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check;
+
+import net.sf.taverna.t2.visit.VisitKind;
+import net.sf.taverna.t2.visit.Visitor;
+
+/**
+ * A {@link BioCatalogueWSDLActivityHealthCheck} is a kind of visit that determines
+ * if the corresponding WSDL activity in a workflow will work during a workflow run -
+ * checks will be made based on the monitoring status held about that service in BioCatalogue.
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class BioCatalogueWSDLActivityHealthCheck extends VisitKind
+{
+  // The following values indicate the type of results that can be associated
+  // with a VisitReport generated by a health-checking visitor.
+  public static final int MESSAGE_IN_VISIT_REPORT = 0;
+  
+  
+  // property names to be placed into VisitReport generated by BioCatalogueWSDLActivityHealthChecker
+  public static final String WSDL_LOCATION_PROPERTY = "wsdlLocation";
+  public static final String OPERATION_NAME_PROPERTY = "soapOperationName";
+  public static final String EXPLANATION_MSG_PROPERTY = "fullExplanationMessage";
+  
+  
+  
+  
+  @Override
+  public Class<? extends Visitor> getVisitorClass() {
+    return BioCatalogueWSDLActivityHealthChecker.class;
+  }
+  
+  private static class Singleton {
+    private static BioCatalogueWSDLActivityHealthCheck instance = new BioCatalogueWSDLActivityHealthCheck();
+  }
+  
+  public static BioCatalogueWSDLActivityHealthCheck getInstance() {
+    return Singleton.instance;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthCheckVisitExplainer.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthCheckVisitExplainer.java b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthCheckVisitExplainer.java
new file mode 100644
index 0000000..8bee663
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/java/net/sf/taverna/t2/ui/perspectives/biocatalogue/integration/health_check/BioCatalogueWSDLActivityHealthCheckVisitExplainer.java
@@ -0,0 +1,111 @@
+package net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+
+import net.sf.taverna.biocatalogue.model.SoapOperationIdentity;
+import net.sf.taverna.t2.lang.ui.ReadOnlyTextArea;
+import net.sf.taverna.t2.visit.VisitKind;
+import net.sf.taverna.t2.visit.VisitReport;
+import net.sf.taverna.t2.workbench.report.explainer.VisitExplainer;
+
+// import status constants
+import static net.sf.taverna.t2.ui.perspectives.biocatalogue.integration.health_check.BioCatalogueWSDLActivityHealthCheck.*;
+
+/**
+ * 
+ * @author Sergejs Aleksejevs
+ */
+public class BioCatalogueWSDLActivityHealthCheckVisitExplainer implements VisitExplainer
+{
+  
+  public boolean canExplain(VisitKind vk, int resultId) {
+    return (vk instanceof BioCatalogueWSDLActivityHealthCheck);
+  }
+  
+  
+  /**
+   * This class only handles {@link VisitReport} instances that are of
+   * {@link BioCatalogueWSDLActivityHealthCheck} kind. Therefore, decisions on
+   * the explanations / solutions are made solely by visit result IDs.
+   */
+  public JComponent getExplanation(final VisitReport vr)
+  {
+    int resultId = vr.getResultId();
+    String explanation = null;
+    
+    switch (resultId) {
+      case MESSAGE_IN_VISIT_REPORT:
+        explanation = (String) vr.getProperty(EXPLANATION_MSG_PROPERTY); break;
+        
+      default:
+        explanation = "Unknown issue - no expalanation available"; break;
+    }
+    
+    
+    JButton bRunBioCatalogueHealthCheck = new JButton("View monitoring status details");
+    bRunBioCatalogueHealthCheck.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        SoapOperationIdentity soapOpIdentity = 
+              new SoapOperationIdentity((String)vr.getProperty(BioCatalogueWSDLActivityHealthCheck.WSDL_LOCATION_PROPERTY),
+                                        (String)vr.getProperty(BioCatalogueWSDLActivityHealthCheck.OPERATION_NAME_PROPERTY), null);
+        
+        ServiceHealthChecker.checkWSDLProcessor(soapOpIdentity);
+      }
+    });
+    JPanel jpButton = new JPanel();
+    jpButton.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
+    jpButton.add(bRunBioCatalogueHealthCheck);
+    jpButton.setOpaque(false);
+    
+    JPanel jpExplanation = new JPanel(new BorderLayout());
+    jpExplanation.add(new ReadOnlyTextArea(explanation), BorderLayout.CENTER);
+    jpExplanation.add(jpButton, BorderLayout.SOUTH);
+    
+    return (jpExplanation);
+  }
+  
+  
+  
+  /**
+   * This class only handles {@link VisitReport} instances that are of
+   * {@link BioCatalogueWSDLActivityHealthCheck} kind. Therefore, decisions on
+   * the explanations / solutions are made solely by visit result IDs.
+   */
+  public JComponent getSolution(VisitReport vr)
+  {
+    String explanation = null;
+    
+    // instead of switching between possible health check resultIDs,
+    // simply choose from possible statuses: for all failures there's
+    // nothing specific that can be done, so no need to differentiate
+    // displayed messages
+    switch (vr.getStatus()) {
+      case OK:
+        explanation = "This WSDL service works fine - no change necessary"; break;
+        
+      case WARNING:
+      case SEVERE:
+        explanation = "This remote WSDL service appears to have an internal problem. There is nothing " +
+        		          "specific that can be done to fix it locally.\n\n" +
+        		          "It is possible that the current state of the service will still allow to execute " +
+        		          "the workflow successfully. Also, the service may have already recovered since the " +
+        		          "last time it's monitoring status has been checked.\n\n" +
+        		          "If this problem does affect the current workflow, it may be resolved by the " +
+        		          "service provider. It may be worth contacting them to report the issue.";
+                      break;
+      
+      default:
+        explanation = "Unknown issue - no solution available"; break;
+    }
+    
+    return (new ReadOnlyTextArea(explanation));
+  }
+  
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-biocatalogue/src/main/xsd/xlink.xsd
----------------------------------------------------------------------
diff --git a/taverna-perspective-biocatalogue/src/main/xsd/xlink.xsd b/taverna-perspective-biocatalogue/src/main/xsd/xlink.xsd
new file mode 100644
index 0000000..6c2034b
--- /dev/null
+++ b/taverna-perspective-biocatalogue/src/main/xsd/xlink.xsd
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- METS XLink Schema, v. 2, Nov. 15, 2004 -->
+<!--  Downloaded from http://www.loc.gov/standards/mets/xlink.xsd at 2007-04-19 -->
+<!--  Downloaded from http://taverna.cvs.sf.net/viewvc/taverna/taverna-service/taverna-interface/src/main/resources/xlink.xsd?view=log at 2009-10-19 -->
+<schema targetNamespace="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xlink="http://www.w3.org/1999/xlink" elementFormDefault="qualified">
+  <!--  global attributes  --> 
+  <attribute name="href"  type="anyURI"/>
+  <attribute name="role" type="string"/>
+  <attribute name="arcrole" type="string"/>
+  <attribute name="title" type="string" /> 
+  <attribute name="show">
+    <simpleType>
+
+      <restriction base="string">
+	<enumeration value="new" /> 
+	<enumeration value="replace" /> 
+	<enumeration value="embed" /> 
+	<enumeration value="other" /> 
+	<enumeration value="none" /> 
+      </restriction>
+    </simpleType>
+  </attribute>
+  <attribute name="actuate">
+    <simpleType>
+
+      <restriction base="string">
+	<enumeration value="onLoad" /> 
+	<enumeration value="onRequest" /> 
+	<enumeration value="other" /> 
+	<enumeration value="none" /> 
+      </restriction>
+    </simpleType>
+  </attribute>
+  <attribute name="label" type="string" /> 
+  <attribute name="from" type="string" /> 
+  <attribute name="to" type="string" /> 
+  <attributeGroup name="simpleLink">
+
+    <attribute name="type" type="string" fixed="simple" form="qualified" /> 
+    <attribute ref="xlink:href" use="optional" /> 
+    <attribute ref="xlink:role" use="optional" /> 
+    <attribute ref="xlink:arcrole" use="optional" /> 
+    <attribute ref="xlink:title" use="optional" /> 
+    <attribute ref="xlink:show" use="optional" /> 
+    <attribute ref="xlink:actuate" use="optional" /> 
+  </attributeGroup>
+  <attributeGroup name="extendedLink">
+    <attribute name="type" type="string" fixed="extended" form="qualified" /> 
+    <attribute ref="xlink:role" use="optional" /> 
+    <attribute ref="xlink:title" use="optional" /> 
+  </attributeGroup>
+  <attributeGroup name="locatorLink">
+
+    <attribute name="type" type="string" fixed="locator" form="qualified" /> 
+    <attribute ref="xlink:href" use="required" /> 
+    <attribute ref="xlink:role" use="optional" /> 
+    <attribute ref="xlink:title" use="optional" /> 
+    <attribute ref="xlink:label" use="optional" /> 
+  </attributeGroup>
+  <attributeGroup name="arcLink">
+    <attribute name="type" type="string" fixed="arc" form="qualified" /> 
+    <attribute ref="xlink:arcrole" use="optional" /> 
+    <attribute ref="xlink:title" use="optional" /> 
+    <attribute ref="xlink:show" use="optional" /> 
+    <attribute ref="xlink:actuate" use="optional" /> 
+    <attribute ref="xlink:from" use="optional" /> 
+    <attribute ref="xlink:to" use="optional" /> 
+  </attributeGroup>
+
+  <attributeGroup name="resourceLink">
+    <attribute name="type" type="string" fixed="resource" form="qualified" /> 
+    <attribute ref="xlink:role" use="optional" /> 
+    <attribute ref="xlink:title" use="optional" /> 
+    <attribute ref="xlink:label" use="optional" /> 
+  </attributeGroup>
+  <attributeGroup name="titleLink">
+    <attribute name="type" type="string" fixed="title" form="qualified" /> 
+  </attributeGroup>
+  <attributeGroup name="emptyLink">
+    <attribute name="type" type="string" fixed="none" form="qualified" /> 
+  </attributeGroup>
+
+</schema>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-design/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-perspective-design/pom.xml b/taverna-perspective-design/pom.xml
new file mode 100644
index 0000000..627c52c
--- /dev/null
+++ b/taverna-perspective-design/pom.xml
@@ -0,0 +1,98 @@
+<?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>perspective-design</artifactId>
+	<packaging>bundle</packaging>
+	<name>Design perspective</name>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>edits-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>menu-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>report-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>selection-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>workbench-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>activity-palette-ui</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>graph-view</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>workflow-explorer</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<!-- <dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>report-view</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency> -->
+		<dependency>
+			<groupId>net.sf.taverna.t2.lang</groupId>
+			<artifactId>ui</artifactId>
+			<version>${t2.lang.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>contextual-views-impl</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>uk.org.taverna.configuration</groupId>
+			<artifactId>taverna-app-configuration-api</artifactId>
+			<version>${taverna.configuration.version}</version>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/DesignPerspective.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/DesignPerspective.java b/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/DesignPerspective.java
new file mode 100644
index 0000000..eca98d4
--- /dev/null
+++ b/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/DesignPerspective.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * 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.ui.perspectives.design;
+
+import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.editIcon;
+
+import javax.swing.ImageIcon;
+import javax.swing.JComponent;
+
+import net.sf.taverna.t2.ui.menu.MenuManager;
+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.zaria.PerspectiveSPI;
+import net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI;
+
+public class DesignPerspective implements PerspectiveSPI {
+	private DesignPerspectiveComponent designPerspectiveComponent;
+	private UIComponentFactorySPI graphViewComponentFactory;
+	private UIComponentFactorySPI servicePanelComponentFactory;
+	private UIComponentFactorySPI contextualViewComponentFactory;
+	private UIComponentFactorySPI workflowExplorerFactory;
+	private UIComponentFactorySPI reportViewComponentFactory;
+	private FileManager fileManager;
+	private SelectionManager selectionManager;
+	private MenuManager menuManager;
+	private EditManager editManager;
+
+	@Override
+	public String getID() {
+		return DesignPerspective.class.getName();
+	}
+
+	@Override
+	public JComponent getPanel() {
+		if (designPerspectiveComponent == null)
+			designPerspectiveComponent = new DesignPerspectiveComponent(
+					graphViewComponentFactory, servicePanelComponentFactory,
+					contextualViewComponentFactory, workflowExplorerFactory,
+					reportViewComponentFactory, fileManager, selectionManager,
+					menuManager, editManager);
+		return designPerspectiveComponent;
+	}
+
+	@Override
+	public ImageIcon getButtonIcon() {
+		return editIcon;
+	}
+
+	@Override
+	public String getText() {
+		return "Design";
+	}
+
+	@Override
+	public int positionHint() {
+		return 10;
+	}
+
+	public void setGraphViewComponentFactory(
+			UIComponentFactorySPI graphViewComponentFactory) {
+		this.graphViewComponentFactory = graphViewComponentFactory;
+	}
+
+	public void setServicePanelComponentFactory(
+			UIComponentFactorySPI servicePanelComponentFactory) {
+		this.servicePanelComponentFactory = servicePanelComponentFactory;
+	}
+
+	public void setContextualViewComponentFactory(
+			UIComponentFactorySPI contextualViewComponentFactory) {
+		this.contextualViewComponentFactory = contextualViewComponentFactory;
+	}
+
+	public void setWorkflowExplorerFactory(
+			UIComponentFactorySPI workflowExplorerFactory) {
+		this.workflowExplorerFactory = workflowExplorerFactory;
+	}
+
+	public void setReportViewComponentFactory(
+			UIComponentFactorySPI reportViewComponentFactory) {
+		this.reportViewComponentFactory = reportViewComponentFactory;
+	}
+
+	public void setFileManager(FileManager fileManager) {
+		this.fileManager = fileManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setMenuManager(MenuManager menuManager) {
+		this.menuManager = menuManager;
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/DesignPerspectiveComponent.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/DesignPerspectiveComponent.java b/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/DesignPerspectiveComponent.java
new file mode 100644
index 0000000..37df0ac
--- /dev/null
+++ b/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/DesignPerspectiveComponent.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (C) 2011 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.ui.perspectives.design;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+
+import javax.swing.JPanel;
+import javax.swing.JSplitPane;
+import javax.swing.JTabbedPane;
+
+import net.sf.taverna.t2.ui.menu.MenuManager;
+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.zaria.UIComponentFactorySPI;
+
+/**
+ * @author David Withers
+ */
+public class DesignPerspectiveComponent extends JSplitPane {
+	private static final long serialVersionUID = 6199239532713982318L;
+
+	private final UIComponentFactorySPI graphViewComponentFactory;
+	private final UIComponentFactorySPI servicePanelComponentFactory;
+	private final UIComponentFactorySPI contextualViewComponentFactory;
+	private final UIComponentFactorySPI workflowExplorerFactory;
+	@SuppressWarnings("unused")
+	private final UIComponentFactorySPI reportViewComponentFactory;
+	private final SelectionManager selectionManager;
+
+	private final FileManager fileManager;
+	private final MenuManager menuManager;
+	private final EditManager editManager;
+
+	public DesignPerspectiveComponent(
+			UIComponentFactorySPI graphViewComponentFactory,
+			UIComponentFactorySPI servicePanelComponentFactory,
+			UIComponentFactorySPI contextualViewComponentFactory,
+			UIComponentFactorySPI workflowExplorerFactory,
+			UIComponentFactorySPI reportViewComponentFactory,
+			FileManager fileManager, SelectionManager selectionManager,
+			MenuManager menuManager, EditManager editManager) {
+		this.graphViewComponentFactory = graphViewComponentFactory;
+		this.servicePanelComponentFactory = servicePanelComponentFactory;
+		this.contextualViewComponentFactory = contextualViewComponentFactory;
+		this.workflowExplorerFactory = workflowExplorerFactory;
+		this.reportViewComponentFactory = reportViewComponentFactory;
+		this.fileManager = fileManager;
+		this.selectionManager = selectionManager;
+		this.menuManager = menuManager;
+		this.editManager = editManager;
+
+		setBorder(null);
+		setOrientation(HORIZONTAL_SPLIT);
+		setDividerLocation(300);
+		setLeftComponent(createLeftComponent());
+		setRightComponent(createRightComponent());
+	}
+
+	private Component createLeftComponent() {
+		JSplitPane leftComponent = new JSplitPane(VERTICAL_SPLIT);
+		leftComponent.setBorder(null);
+		leftComponent.setDividerLocation(400);
+
+		leftComponent.setLeftComponent((Component) servicePanelComponentFactory
+				.getComponent());
+
+		JTabbedPane rightComponent = new JTabbedPane();
+		rightComponent.addTab("Workflow explorer",
+				(Component) workflowExplorerFactory.getComponent());
+		rightComponent.addTab("Details",
+				(Component) contextualViewComponentFactory.getComponent());
+		// rightComponent.addTab("Validation report", (Component)
+		// reportViewComponentFactory.getComponent());
+		leftComponent.setRightComponent(rightComponent);
+
+		return leftComponent;
+	}
+
+	private Component createRightComponent() {
+		JPanel diagramComponent = new JPanel(new BorderLayout());
+		diagramComponent.add(new WorkflowSelectorComponent(selectionManager),
+				NORTH);
+		diagramComponent.add(
+				(Component) graphViewComponentFactory.getComponent(), CENTER);
+
+		JPanel rightComonent = new JPanel(new BorderLayout());
+		rightComonent.add(new WorkflowBundleSelectorComponent(selectionManager,
+				fileManager, menuManager, editManager), NORTH);
+		rightComonent.add(diagramComponent, CENTER);
+		return rightComonent;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowBundleSelectorComponent.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowBundleSelectorComponent.java b/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowBundleSelectorComponent.java
new file mode 100644
index 0000000..9fccf06
--- /dev/null
+++ b/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowBundleSelectorComponent.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (C) 2013 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.ui.perspectives.design;
+
+import java.awt.Component;
+import java.net.URI;
+
+import javax.swing.Action;
+import javax.swing.JMenuItem;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.lang.ui.tabselector.Tab;
+import net.sf.taverna.t2.lang.ui.tabselector.TabSelectorComponent;
+import net.sf.taverna.t2.ui.menu.MenuManager;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * Component for managing selection of workflow bundles.
+ *
+ * @author David Withers
+ */
+public class WorkflowBundleSelectorComponent extends TabSelectorComponent<WorkflowBundle> {
+	private static final long serialVersionUID = 7291973052895544750L;
+	private static final URI FILE_CLOSE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#fileClose");
+
+	private final SelectionManager selectionManager;
+	private final FileManager fileManager;
+	private final EditManager editManager;
+	private final MenuManager menuManager;
+
+	private Action closeMenuAction;
+
+	public WorkflowBundleSelectorComponent(SelectionManager selectionManager,
+			FileManager fileManager, MenuManager menuManager,
+			EditManager editManager) {
+		this.selectionManager = selectionManager;
+		this.fileManager = fileManager;
+		this.menuManager = menuManager;
+		this.editManager = editManager;
+		fileManager.addObserver(new FileManagerObserver());
+		selectionManager.addObserver(new SelectionManagerObserver());
+	}
+
+	private class FileManagerObserver extends
+			SwingAwareObserver<FileManagerEvent> {
+		@Override
+		public void notifySwing(Observable<FileManagerEvent> sender,
+				FileManagerEvent message) {
+			if (message instanceof ClosedDataflowEvent) {
+				ClosedDataflowEvent event = (ClosedDataflowEvent) message;
+				removeObject(event.getDataflow());
+			}
+		}
+	}
+
+	private class SelectionManagerObserver extends
+			SwingAwareObserver<SelectionManagerEvent> {
+		@Override
+		public void notifySwing(Observable<SelectionManagerEvent> sender,
+				SelectionManagerEvent message) {
+			if (message instanceof WorkflowBundleSelectionEvent) {
+				WorkflowBundleSelectionEvent workflowBundleSelectionEvent = (WorkflowBundleSelectionEvent) message;
+				WorkflowBundle workflowBundle = workflowBundleSelectionEvent
+						.getSelectedWorkflowBundle();
+				selectObject(workflowBundle);
+			}
+		}
+	}
+
+	@Override
+	protected Tab<WorkflowBundle> createTab(WorkflowBundle workflowBundle) {
+		return new WorkflowTab(this, workflowBundle, selectionManager,
+				fileManager, editManager, getCloseMenuAction());
+	}
+
+	private Action getCloseMenuAction() {
+		if (closeMenuAction == null) {
+			Component component = menuManager.getComponentByURI(FILE_CLOSE_URI);
+			if (component instanceof JMenuItem) {
+				JMenuItem menuItem = (JMenuItem) component;
+				closeMenuAction = menuItem.getAction();
+			}
+		}
+		return closeMenuAction;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowSelectorComponent.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowSelectorComponent.java b/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowSelectorComponent.java
new file mode 100644
index 0000000..6472828
--- /dev/null
+++ b/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowSelectorComponent.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.ui.perspectives.design;
+
+import static java.awt.FlowLayout.LEFT;
+
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent;
+import net.sf.taverna.t2.workbench.selection.events.WorkflowSelectionEvent;
+import uk.org.taverna.scufl2.api.activity.Activity;
+import uk.org.taverna.scufl2.api.common.NamedSet;
+import uk.org.taverna.scufl2.api.common.Scufl2Tools;
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Processor;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Component for managing selection of workflows.
+ *
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class WorkflowSelectorComponent extends JPanel {
+	private static final URI NESTED_WORKFLOW_TYPE = URI
+			.create("http://ns.taverna.org.uk/2010/activity/nested-workflow");
+
+	private final Scufl2Tools scufl2Tools = new Scufl2Tools();
+	private final SelectionManager selectionManager;
+
+	public WorkflowSelectorComponent(SelectionManager selectionManager) {
+		super(new FlowLayout(LEFT));
+		this.selectionManager = selectionManager;
+		update(selectionManager.getSelectedWorkflow());
+		selectionManager.addObserver(new SelectionManagerObserver());
+	}
+
+	private void update(Workflow workflow) {
+		removeAll();
+		if (workflow != null
+				&& workflow.getParent().getMainWorkflow() != workflow)
+			update(workflow, selectionManager.getSelectedProfile());
+		revalidate();
+		repaint();
+	}
+
+	/** @see #update(Workflow) */
+	private void update(Workflow workflow, Profile profile) {
+		boolean first = true;
+		for (final Workflow workflowItem : getPath(
+				new NamedSet<>(profile.getActivities()), workflow, profile)) {
+			JButton button = new JButton(workflowItem.getName());
+//				button.setBorder(null);
+			button.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					selectionManager.setSelectedWorkflow(workflowItem);
+				}
+			});
+			if (!first)
+				add(new JLabel(">"));
+			first = false;
+			add(button);
+		}
+	}
+
+	private List<Workflow> getPath(NamedSet<Activity> activities,
+			Workflow workflow, Profile profile) {
+		LinkedList<Workflow> path = new LinkedList<>();
+		for (Activity activity : activities) {
+			if (!activity.getType().equals(NESTED_WORKFLOW_TYPE))
+				continue;
+			if (getNestedWorkflow(workflow, profile, activity) != workflow)
+				continue;
+
+			List<ProcessorBinding> processorBindings = scufl2Tools
+					.processorBindingsToActivity(activity);
+			for (ProcessorBinding processorBinding : processorBindings) {
+				Processor processor = processorBinding.getBoundProcessor();
+				Workflow parentWorkflow = processor.getParent();
+				if (workflow.getParent().getMainWorkflow() == parentWorkflow)
+					path.add(parentWorkflow);
+				else {
+					activities.remove(activity);
+					path.addAll(getPath(activities, parentWorkflow, profile));
+				}
+				break;
+			}
+			break;
+		}
+		path.add(workflow);
+		return path;
+	}
+
+	private Workflow getNestedWorkflow(Workflow workflow, Profile profile,
+			Activity activity) {
+		for (Configuration configuration : scufl2Tools.configurationsFor(
+				activity, profile)) {
+			JsonNode nested = configuration.getJson().get("nestedWorkflow");
+			Workflow wf = workflow.getParent().getWorkflows()
+					.getByName(nested.asText());
+			if (wf != null)
+				return wf;
+		}
+		return null;
+	}
+
+	private class SelectionManagerObserver extends
+			SwingAwareObserver<SelectionManagerEvent> {
+		@Override
+		public void notifySwing(Observable<SelectionManagerEvent> sender,
+				SelectionManagerEvent message) {
+			if (message instanceof WorkflowBundleSelectionEvent)
+				bundleSelected((WorkflowBundleSelectionEvent) message);
+			else if (message instanceof WorkflowSelectionEvent)
+				workflowSelected((WorkflowSelectionEvent) message);
+		}
+	}
+
+	private void workflowSelected(WorkflowSelectionEvent event) {
+		update(event.getSelectedWorkflow());
+	}
+
+	private void bundleSelected(WorkflowBundleSelectionEvent event) {
+		WorkflowBundle workflowBundle = event.getSelectedWorkflowBundle();
+		if (workflowBundle == null)
+			update((Workflow) null);
+		else
+			update(selectionManager.getSelectedWorkflow());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowTab.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowTab.java b/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowTab.java
new file mode 100644
index 0000000..e2f7bd6
--- /dev/null
+++ b/taverna-perspective-design/src/main/java/net/sf/taverna/t2/ui/perspectives/design/WorkflowTab.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.ui.perspectives.design;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.SwingAwareObserver;
+import net.sf.taverna.t2.lang.ui.tabselector.Tab;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent;
+import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
+import net.sf.taverna.t2.workbench.file.events.SavedDataflowEvent;
+import net.sf.taverna.t2.workbench.file.exceptions.UnsavedException;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * Tab for selecting current workflow.
+ *
+ * @author David Withers
+ */
+@SuppressWarnings("serial")
+public class WorkflowTab extends Tab<WorkflowBundle> {
+	private static final String SAVED_MARKER = "*";
+
+	private final Component component;
+	private final SelectionManager selectionManager;
+	private final FileManager fileManager;
+	private final EditManager editManager;
+	private final Action closeMenuAction;
+
+	private boolean saved = true;
+	private EditManagerObserver editManagerObserver;
+	private FileManagerObserver fileManagerObserver;
+
+	public WorkflowTab(Component component, final WorkflowBundle workflowBundle,
+			final SelectionManager selectionManager, final FileManager fileManager,
+			EditManager editManager, Action closeMenuAction) {
+		super(workflowBundle.getMainWorkflow().getName(), workflowBundle);
+		this.component = component;
+		this.selectionManager = selectionManager;
+		this.fileManager = fileManager;
+		this.editManager = editManager;
+		this.closeMenuAction = closeMenuAction;
+		editManagerObserver = new EditManagerObserver();
+		fileManagerObserver = new FileManagerObserver();
+		editManager.addObserver(editManagerObserver);
+		fileManager.addObserver(fileManagerObserver);
+	}
+
+	@Override
+	protected void clickTabAction() {
+		selectionManager.setSelectedWorkflowBundle(selection);
+	}
+
+	@Override
+	protected void closeTabAction() {
+		if (!saved && closeMenuAction != null) {
+			selectionManager.setSelectedWorkflowBundle(selection);
+			closeMenuAction.actionPerformed(new ActionEvent(component, 0, ""));
+		} else
+			try {
+				fileManager.closeDataflow(selection, false);
+			} catch (UnsavedException e) {
+			}
+	}
+
+	private class EditManagerObserver extends
+			SwingAwareObserver<EditManagerEvent> {
+		@Override
+		public void notifySwing(Observable<EditManagerEvent> sender,
+				EditManagerEvent message) {
+			if (message instanceof AbstractDataflowEditEvent) {
+				AbstractDataflowEditEvent event = (AbstractDataflowEditEvent) message;
+				if (event.getDataFlow() == selection)
+					setSaved(false);
+			}
+		}
+	}
+
+	private class FileManagerObserver extends
+			SwingAwareObserver<FileManagerEvent> {
+		@Override
+		public void notifySwing(Observable<FileManagerEvent> sender,
+				FileManagerEvent message) {
+			if (message instanceof ClosedDataflowEvent) {
+				ClosedDataflowEvent event = (ClosedDataflowEvent) message;
+				if (event.getDataflow() == selection) {
+					fileManager.removeObserver(fileManagerObserver);
+					editManager.removeObserver(editManagerObserver);
+				}
+			} else if (message instanceof SavedDataflowEvent) {
+				SavedDataflowEvent event = (SavedDataflowEvent) message;
+				if (event.getDataflow() == selection)
+					setSaved(true);
+			}
+		}
+	}
+
+	public void setSaved(boolean saved) {
+		this.saved = saved;
+		String name = getName();
+		if (saved && name.startsWith(SAVED_MARKER))
+			setName(name.substring(SAVED_MARKER.length()));
+		else if (!saved && !name.startsWith(SAVED_MARKER))
+			setName(SAVED_MARKER + name);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-design/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI
----------------------------------------------------------------------
diff --git a/taverna-perspective-design/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI b/taverna-perspective-design/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI
new file mode 100644
index 0000000..db4397c
--- /dev/null
+++ b/taverna-perspective-design/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.PerspectiveSPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.ui.perspectives.design.DesignPerspective

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-design/src/main/resources/META-INF/spring/perspective-design-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-perspective-design/src/main/resources/META-INF/spring/perspective-design-context-osgi.xml b/taverna-perspective-design/src/main/resources/META-INF/spring/perspective-design-context-osgi.xml
new file mode 100644
index 0000000..3473a47
--- /dev/null
+++ b/taverna-perspective-design/src/main/resources/META-INF/spring/perspective-design-context-osgi.xml
@@ -0,0 +1,21 @@
+<?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="DesignPerspective" auto-export="interfaces" />
+
+	<reference id="graphViewComponentFactory" interface="net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI" bean-name="GraphViewComponentFactory" />
+	<reference id="servicePanelComponentFactory" interface="net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI" bean-name="ServicePanelComponentFactory"/>
+	<reference id="contextualViewComponentFactory" interface="net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI" bean-name="ContextualViewComponentFactory"/>
+	<reference id="workflowExplorerFactory" interface="net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI" bean-name="WorkflowExplorerFactory"/>
+	<!-- <reference id="reportViewComponentFactory" interface="net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI" bean-name="ReportViewComponentFactory"/> -->
+	<reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" />
+	<reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" />
+	<reference id="menuManager" interface="net.sf.taverna.t2.ui.menu.MenuManager" />
+	<reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-design/src/main/resources/META-INF/spring/perspective-design-context.xml
----------------------------------------------------------------------
diff --git a/taverna-perspective-design/src/main/resources/META-INF/spring/perspective-design-context.xml b/taverna-perspective-design/src/main/resources/META-INF/spring/perspective-design-context.xml
new file mode 100644
index 0000000..9e4e383
--- /dev/null
+++ b/taverna-perspective-design/src/main/resources/META-INF/spring/perspective-design-context.xml
@@ -0,0 +1,18 @@
+<?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="DesignPerspective" class="net.sf.taverna.t2.ui.perspectives.design.DesignPerspective">
+			<property name="graphViewComponentFactory" ref="graphViewComponentFactory" />
+			<property name="servicePanelComponentFactory" ref="servicePanelComponentFactory" />
+			<property name="contextualViewComponentFactory" ref="contextualViewComponentFactory" />
+			<property name="workflowExplorerFactory" ref="workflowExplorerFactory" />
+			<!-- <property name="reportViewComponentFactory" ref="reportViewComponentFactory" /> -->
+			<property name="fileManager" ref="fileManager" />
+			<property name="selectionManager" ref="selectionManager" />
+			<property name="menuManager" ref="menuManager" />
+			<property name="editManager" ref="editManager" />
+	</bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/pom.xml b/taverna-perspective-myexperiment/pom.xml
new file mode 100644
index 0000000..82dded6
--- /dev/null
+++ b/taverna-perspective-myexperiment/pom.xml
@@ -0,0 +1,100 @@
+<?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>perspective-myexperiment</artifactId>
+	<name>myExperiment perspective</name>
+	<repositories>
+		<repository>
+			<releases />
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+			<id>mygrid-repository</id>
+			<name>myGrid Repository</name>
+			<url>http://www.mygrid.org.uk/maven/repository</url>
+		</repository>
+		<repository>
+			<releases>
+				<enabled>false</enabled>
+			</releases>
+			<snapshots />
+			<id>mygrid-snapshot-repository</id>
+			<name>myGrid Snapshot Repository</name>
+			<url>
+				http://www.mygrid.org.uk/maven/snapshot-repository
+			</url>
+		</repository>
+
+	</repositories>
+
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>menu-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>workbench-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>net.sf.taverna.t2.ui-activities</groupId>
+			<artifactId>dataflow-activity-ui</artifactId>
+			<version>${t2.ui.activities.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.taverna.t2.lang</groupId>
+			<artifactId>ui</artifactId>
+			<version>${t2.lang.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>uk.org.taverna.configuration</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${taverna.configuration.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>uk.org.taverna.configuration</groupId>
+			<artifactId>taverna-app-configuration-api</artifactId>
+			<version>${taverna.configuration.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.jdom</groupId>
+			<artifactId>com.springsource.org.jdom</artifactId>
+			<version>${jdom.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.log4j</groupId>
+			<artifactId>com.springsource.org.apache.log4j</artifactId>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/AddCommentDialog.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/AddCommentDialog.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/AddCommentDialog.java
new file mode 100644
index 0000000..fc1a8e6
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/AddCommentDialog.java
@@ -0,0 +1,330 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.net.HttpURLConnection;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JRootPane;
+import javax.swing.JScrollPane;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.WindowConstants;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+
+import net.sf.taverna.t2.lang.ui.DialogTextArea;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.ServerResponse;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Util;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs, Jiten Bhagat
+ */
+
+public class AddCommentDialog extends HelpEnabledDialog implements ActionListener, CaretListener, ComponentListener, KeyListener {
+  // components for accessing application's main elements
+  private MainComponent pluginMainComponent;
+  private MyExperimentClient myExperimentClient;
+  private Logger logger;
+
+  // COMPONENTS
+  private DialogTextArea taComment;
+  private JButton bPost;
+  private JButton bCancel;
+  private JLabel lStatusMessage;
+
+  // STORAGE
+  private Resource resource; // a resource for which the comment is being posted
+  private String strComment = null;
+  private boolean bPostingSuccessful = false;
+
+  public AddCommentDialog(JFrame owner, Resource resource, MainComponent component, MyExperimentClient client, Logger logger) {
+	super(owner, "Add comment for \"" + resource.getTitle() + "\" "
+			+ resource.getItemTypeName(), true);
+
+	// set main variables to ensure access to myExperiment, logger and the parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	// set the resource for which the comment is being added
+	this.resource = resource;
+
+	// set options of the 'add comment' dialog box
+	this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+	//this.setIconImage(new ImageIcon(MyExperimentPerspective.getLocalResourceURL("myexp_icon")).getImage());
+
+	this.initialiseUI();
+  }
+
+  private void initialiseUI() {
+	// get content pane
+	Container contentPane = this.getContentPane();
+
+	// set up layout
+	contentPane.setLayout(new GridBagLayout());
+	GridBagConstraints c = new GridBagConstraints();
+
+	// add all components
+	JLabel lInfo = new JLabel("Please type in you comment:");
+	c.gridx = 0;
+	c.gridy = 0;
+	c.anchor = GridBagConstraints.WEST;
+	c.gridwidth = 2;
+	c.fill = GridBagConstraints.NONE;
+	c.insets = new Insets(10, 10, 5, 10);
+	contentPane.add(lInfo, c);
+
+	this.taComment = new DialogTextArea(5, 35);
+	this.taComment.setLineWrap(true);
+	this.taComment.setWrapStyleWord(true);
+	this.taComment.addKeyListener(this);
+	this.taComment.addCaretListener(this);
+
+	JScrollPane spComment = new JScrollPane(this.taComment);
+	c.gridy = 1;
+	c.fill = GridBagConstraints.HORIZONTAL;
+	c.insets = new Insets(0, 10, 0, 10);
+	contentPane.add(spComment, c);
+
+	this.bPost = new JButton("Post Comment");
+	this.bPost.setEnabled(false);
+	this.bPost.setDefaultCapable(true);
+	this.getRootPane().setDefaultButton(this.bPost);
+	this.bPost.addActionListener(this);
+	this.bPost.addKeyListener(this);
+	c.gridy = 2;
+	c.anchor = GridBagConstraints.EAST;
+	c.gridwidth = 1;
+	c.fill = GridBagConstraints.NONE;
+	c.weightx = 0.5;
+	c.insets = new Insets(10, 5, 10, 5);
+	contentPane.add(bPost, c);
+
+	this.bCancel = new JButton("Cancel");
+	this.bCancel.setPreferredSize(this.bPost.getPreferredSize());
+	this.bCancel.addActionListener(this);
+	c.gridx = 1;
+	c.anchor = GridBagConstraints.WEST;
+	c.weightx = 0.5;
+	contentPane.add(bCancel, c);
+
+	this.pack();
+	this.setMinimumSize(this.getPreferredSize());
+	this.setMaximumSize(this.getPreferredSize());
+	this.addComponentListener(this);
+  }
+
+  /**
+   * Opens up a modal dialog where the user can enter the comment text.
+   * 
+   * Window is simply closed if 'Cancel' button is pressed; on pressing 'Post
+   * Comment' button the window will turn into 'waiting' state, post the comment
+   * and return the resulting XML document (which would contain the newly added
+   * comment) back to the caller.
+   * 
+   * @return String value of the non-empty comment text to be sent to
+   *         myExperiment or null if action was cancelled.
+   */
+  public String launchAddCommentDialogAndPostCommentIfRequired() {
+	// makes the 'add comment' dialog visible, then waits until it is closed;
+	// control returns to this method when the dialog window is disposed
+	this.setVisible(true);
+	return (strComment);
+  }
+
+  // *** Callback for ActionListener interface ***
+  public void actionPerformed(ActionEvent e) {
+	if (e.getSource().equals(this.bPost)) {
+	  // 'Post' button is not active when text in the comment field is blank,
+	  // so if it was pressed, there should be some text typed in
+	  this.strComment = this.taComment.getText();
+
+	  // the window will stay visible, but should turn into 'waiting' state
+	  final JRootPane rootPane = this.getRootPane();
+	  final Container contentPane = this.getContentPane();
+	  contentPane.remove(this.bPost);
+	  contentPane.remove(this.bCancel);
+	  if (this.lStatusMessage != null)
+		contentPane.remove(this.lStatusMessage);
+	  this.taComment.setEditable(false);
+
+	  final GridBagConstraints c = new GridBagConstraints();
+	  c.gridx = 0;
+	  c.gridy = 2;
+	  c.gridwidth = 2;
+	  c.anchor = GridBagConstraints.CENTER;
+	  c.fill = GridBagConstraints.NONE;
+	  c.insets = new Insets(10, 5, 10, 5);
+	  lStatusMessage = new JLabel("Posting your comment...", new ImageIcon(MyExperimentPerspective.getLocalResourceURL("spinner")), SwingConstants.CENTER);
+	  contentPane.add(lStatusMessage, c);
+
+	  // disable the (X) button (ideally, would need to remove it, but there's no way to do this)
+	  this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
+
+	  // revalidate the window
+	  this.pack();
+	  this.validate();
+	  this.repaint();
+
+	  new Thread("Posting comment") {
+		public void run() {
+		  // *** POST THE COMMENT ***
+		  final ServerResponse response = myExperimentClient.postComment(resource, Util.stripAllHTML(strComment));
+		  bPostingSuccessful = (response.getResponseCode() == HttpURLConnection.HTTP_OK);
+
+		  SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+			  // *** REACT TO POSTING RESULT ***
+			  if (bPostingSuccessful) {
+				// comment posted successfully
+				setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+				taComment.setEnabled(false);
+				contentPane.remove(lStatusMessage);
+
+				c.insets = new Insets(10, 5, 5, 5);
+				lStatusMessage = new JLabel("Your comment was posted successfully", new ImageIcon(MyExperimentPerspective.getLocalResourceURL("success_icon")), SwingConstants.LEFT);
+				contentPane.add(lStatusMessage, c);
+
+				bCancel.setText("OK");
+				bCancel.setDefaultCapable(true);
+				rootPane.setDefaultButton(bCancel);
+				c.insets = new Insets(5, 5, 10, 5);
+				c.gridy += 1;
+				contentPane.add(bCancel, c);
+
+				pack();
+				bCancel.requestFocusInWindow();
+			  } else {
+				// posting wasn't successful, notify the user
+				// and provide an option to close window or start again
+				setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+				taComment.setEditable(true);
+				contentPane.remove(lStatusMessage);
+
+				c.insets = new Insets(10, 5, 5, 5);
+				lStatusMessage = new JLabel("Error occurred while posting comment: "
+					+ Util.retrieveReasonFromErrorXMLDocument(response.getResponseBody()), new ImageIcon(MyExperimentPerspective.getLocalResourceURL("failure_icon")), SwingConstants.LEFT);
+				contentPane.add(lStatusMessage, c);
+
+				bPost.setText("Try again");
+				bPost.setToolTipText("Please review your comment before trying to post it again");
+				c.anchor = GridBagConstraints.EAST;
+				c.insets = new Insets(5, 5, 10, 5);
+				c.gridwidth = 1;
+				c.weightx = 0.5;
+				c.gridx = 0;
+				c.gridy += 1;
+				contentPane.add(bPost, c);
+				rootPane.setDefaultButton(bPost);
+
+				c.anchor = GridBagConstraints.WEST;
+				c.gridx = 1;
+				bCancel.setPreferredSize(bPost.getPreferredSize());
+				contentPane.add(bCancel, c);
+
+				pack();
+				validate();
+				repaint();
+			  }
+			}
+		  });
+
+		}
+	  }.start();
+	} else if (e.getSource().equals(this.bCancel)) {
+	  // cleanup the comment if it wasn't posted successfully + simply close and destroy the window
+	  if (!this.bPostingSuccessful)
+		this.strComment = null;
+	  this.dispose();
+	}
+
+  }
+
+  // *** Callbacks for KeyListener interface ***
+  public void keyPressed(KeyEvent e) {
+	// if TAB was pressed in the text area, need to move keyboard focus
+	if (e.getSource().equals(this.taComment)
+		&& e.getKeyCode() == KeyEvent.VK_TAB) {
+	  if ((e.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) == KeyEvent.SHIFT_DOWN_MASK) {
+		// SHIFT + TAB move focus backwards
+		((Component) e.getSource()).transferFocusBackward();
+	  } else {
+		// TAB moves focus forward
+		((Component) e.getSource()).transferFocus();
+	  }
+	  e.consume();
+	}
+  }
+
+  public void keyReleased(KeyEvent e) {
+	// not in use
+  }
+
+  public void keyTyped(KeyEvent e) {
+	// not in use
+  }
+
+  // *** Callback for CaretListener interface ***
+  public void caretUpdate(CaretEvent e) {
+	// check whether the 'Post' button should be available or not
+	this.bPost.setEnabled(this.taComment.getText().length() > 0);
+  }
+
+  // *** Callbacks for ComponentListener interface ***
+  public void componentShown(ComponentEvent e) {
+	// center this dialog box within the preview browser window
+	Util.centerComponentWithinAnother(this.pluginMainComponent.getPreviewBrowser(), this);
+  }
+
+  public void componentHidden(ComponentEvent e) {
+	// not in use
+  }
+
+  public void componentMoved(ComponentEvent e) {
+	// not in use
+  }
+
+  public void componentResized(ComponentEvent e) {
+	// not in use
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/AddRemoveFavouriteDialog.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/AddRemoveFavouriteDialog.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/AddRemoveFavouriteDialog.java
new file mode 100644
index 0000000..563d2b8
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/AddRemoveFavouriteDialog.java
@@ -0,0 +1,277 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.Container;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.net.HttpURLConnection;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.WindowConstants;
+
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.ServerResponse;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Util;
+import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sergejs Aleksejevs
+ */
+public class AddRemoveFavouriteDialog extends HelpEnabledDialog implements ActionListener, ComponentListener {
+  // CONSTANTS
+  protected static final int OPERATION_SUCCESSFUL = 1;
+  protected static final int OPERATION_CANCELLED = 0;
+  protected static final int OPERATION_FAILED = -1;
+
+  // components for accessing application's main elements
+  private final MainComponent pluginMainComponent;
+  private final MyExperimentClient myExperimentClient;
+  private final Logger logger;
+
+  // COMPONENTS
+  private JButton bAddRemoveFavourite;
+  private JButton bCancel;
+
+  // STORAGE
+  private final Resource resource; // a resource which is being favourited / removed from favourites
+  private final boolean bIsFavouriteBeingAdded;
+  private int iOperationStatus = OPERATION_CANCELLED;
+  private ServerResponse response = null;
+
+  public AddRemoveFavouriteDialog(JFrame owner, boolean isFavouriteAdded, Resource resource, MainComponent component, MyExperimentClient client, Logger logger) {
+	super(owner, isFavouriteAdded ? "Add to" : "Remove from"
+	+ " favourites - \"" + resource.getTitle() + "\" "
+	+ resource.getItemTypeName(), true);
+
+	// set main variables to ensure access to myExperiment, logger and the parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	// parameters
+	this.bIsFavouriteBeingAdded = isFavouriteAdded;
+	this.resource = resource;
+
+	// set options of the 'add/remove favourite' dialog box
+	this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+	//this.setIconImage(new ImageIcon(MyExperimentPerspective.getLocalResourceURL("myexp_icon")).getImage());
+
+	this.initialiseUI();
+  }
+
+  private void initialiseUI() {
+	// get content pane
+	Container contentPane = this.getContentPane();
+
+	// set up layout
+	contentPane.setLayout(new GridBagLayout());
+	GridBagConstraints c = new GridBagConstraints();
+
+	// add all components
+	JLabel lInfo = new JLabel("<html><center>You are about to "
+		+ (this.bIsFavouriteBeingAdded ? "add" : "remove") + " \""
+		+ resource.getTitle() + "\" " + resource.getItemTypeName()
+		+ (this.bIsFavouriteBeingAdded ? " to" : " from")
+		+ " your favourites.<br><br>"
+		+ "Do you want to proceed?</center></html>");
+	c.gridx = 0;
+	c.gridy = 0;
+	c.anchor = GridBagConstraints.WEST;
+	c.gridwidth = 2;
+	c.fill = GridBagConstraints.NONE;
+	c.insets = new Insets(10, 10, 10, 10);
+	contentPane.add(lInfo, c);
+
+	if (this.bIsFavouriteBeingAdded) {
+	  this.bAddRemoveFavourite = new JButton("Add to Favourites");
+	} else {
+	  this.bAddRemoveFavourite = new JButton("Remove from Favourites");
+	}
+	this.bAddRemoveFavourite.setDefaultCapable(true);
+	this.bAddRemoveFavourite.addActionListener(this);
+
+	c.gridy = 1;
+	c.anchor = GridBagConstraints.EAST;
+	c.gridwidth = 1;
+	c.fill = GridBagConstraints.NONE;
+	c.weightx = 0.5;
+	c.insets = new Insets(5, 5, 10, 5);
+	contentPane.add(this.bAddRemoveFavourite, c);
+
+	this.bCancel = new JButton("Cancel");
+	this.bCancel.setPreferredSize(this.bAddRemoveFavourite.getPreferredSize());
+	this.bCancel.addActionListener(this);
+	c.gridx = 1;
+	c.anchor = GridBagConstraints.WEST;
+	contentPane.add(bCancel, c);
+
+	this.pack();
+	this.getRootPane().setDefaultButton(this.bAddRemoveFavourite);
+	this.setMinimumSize(this.getPreferredSize());
+	this.setMaximumSize(this.getPreferredSize());
+	this.addComponentListener(this);
+  }
+
+  /**
+   * Makes the dialog for adding / removing an item from favourites visible.
+   * Based on the user actions, it might execute the adding / removing
+   * operation.
+   * 
+   * @return Returns an integer value which represents status of the operation:
+   *         {@value #OPERATION_SUCCESSFUL} - favourite item was added / removed
+   *         successfully; {@value #OPERATION_CANCELLED} - the user has
+   *         cancelled operation; {@value #OPERATION_FAILED} - failed while
+   *         adding / removing favourite item;
+   */
+  public int launchAddRemoveFavouriteDialogAndPerformNecessaryActionIfRequired() {
+	this.setVisible(true);
+	return (this.iOperationStatus);
+  }
+
+  // *** Callback for ActionListener interface ***
+  public void actionPerformed(ActionEvent e) {
+
+	if (e.getSource().equals(this.bAddRemoveFavourite)) {
+	  // the window will stay visible, but should turn into 'waiting' state
+	  final Container contentPane = this.getContentPane();
+	  contentPane.removeAll();
+
+	  final GridBagConstraints c = new GridBagConstraints();
+	  c.gridx = 0;
+	  c.gridy = 0;
+	  c.gridwidth = 2;
+	  c.anchor = GridBagConstraints.CENTER;
+	  c.fill = GridBagConstraints.NONE;
+	  c.insets = new Insets(10, 5, 10, 5);
+	  JLabel lInfo = new JLabel(this.bIsFavouriteBeingAdded ? "Adding to favourites..." : "Removing from favourites...", new ImageIcon(MyExperimentPerspective.getLocalResourceURL("spinner")), SwingConstants.CENTER);
+	  contentPane.add(lInfo, c);
+
+	  // disable the (X) button (ideally, would need to remove it, but there's no way to do this)
+	  this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
+
+	  // revalidate the window
+	  this.pack();
+	  this.validate();
+	  this.repaint();
+
+	  new Thread("Execute add / remove favourite operation") {
+		@Override
+		public void run() {
+		  // *** DO THE REQUIRED ACTION ***
+		  response = (bIsFavouriteBeingAdded ? myExperimentClient.addFavourite(resource) : myExperimentClient.deleteFavourite(resource));
+		  iOperationStatus = (response.getResponseCode() == HttpURLConnection.HTTP_OK) ? OPERATION_SUCCESSFUL : OPERATION_FAILED;
+
+		  if (iOperationStatus == OPERATION_SUCCESSFUL) {
+			// update local list of favourite items - no data sync with the API is required at this point
+			if (bIsFavouriteBeingAdded) {
+			  myExperimentClient.getCurrentUser().getFavourites().add(resource);
+			} else {
+			  myExperimentClient.getCurrentUser().getFavourites().remove(resource);
+			}
+		  } else {
+			// operation has failed - this might have been due to outdated data which is stored locally;
+			// sync favourite data with the API so that the UI can show more relevant data
+			// (e.g. replace 'add to favourites' with 'remove from favourites' if this item is already
+			//  added to favourites - probably via another interface: web or another instance of the plugin)
+			myExperimentClient.updateUserFavourites(myExperimentClient.getCurrentUser());
+		  }
+
+		  SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+			  // *** REACT TO RESULT ***
+			  setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+			  contentPane.removeAll();
+			  c.insets = new Insets(10, 5, 5, 5);
+
+			  if (iOperationStatus == OPERATION_SUCCESSFUL) {
+				// favourite was added / removed successfully
+				contentPane.add(new JLabel("Item has been successfully "
+					+ (bIsFavouriteBeingAdded ? "added to" : "removed from")
+					+ " your favourites", new ImageIcon(MyExperimentPerspective.getLocalResourceURL("success_icon")), SwingConstants.LEFT), c);
+			  } else {
+				// favourite wasn't added / removed - operation failed;
+				// display error message
+				contentPane.add(new JLabel("<html><center>Error occurred while "
+					+ (bIsFavouriteBeingAdded ? "adding" : "removing")
+					+ " the item "
+					+ (bIsFavouriteBeingAdded ? "to" : "from")
+					+ " your favourites:<br>"
+					+ Util.retrieveReasonFromErrorXMLDocument(response.getResponseBody())
+					+ "</center></html>", new ImageIcon(MyExperimentPerspective.getLocalResourceURL("failure_icon")), SwingConstants.LEFT), c);
+			  }
+
+			  bCancel.setText("OK");
+			  bCancel.setPreferredSize(null); // resets preferred size to the automatic one
+			  bCancel.setDefaultCapable(true);
+			  c.insets = new Insets(5, 5, 10, 5);
+			  c.gridy += 1;
+			  contentPane.add(bCancel, c);
+
+			  pack();
+			  repaint();
+
+			  bCancel.requestFocusInWindow();
+			  getRootPane().setDefaultButton(bCancel);
+			}
+		  });
+		}
+	  }.start();
+
+	} else if (e.getSource().equals(this.bCancel)) {
+	  // simply close and destroy the window
+	  this.dispose();
+	}
+  }
+
+  // *** Callbacks for ComponentListener interface ***
+  public void componentShown(ComponentEvent e) {
+	// center this dialog box within the preview browser window
+	Util.centerComponentWithinAnother(this.pluginMainComponent.getPreviewBrowser(), this);
+  }
+
+  public void componentHidden(ComponentEvent e) {
+	// not in use
+  }
+
+  public void componentMoved(ComponentEvent e) {
+	// not in use
+  }
+
+  public void componentResized(ComponentEvent e) {
+	// not in use
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ExampleWorkflowsPanel.java
----------------------------------------------------------------------
diff --git a/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ExampleWorkflowsPanel.java b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ExampleWorkflowsPanel.java
new file mode 100644
index 0000000..d3a58a0
--- /dev/null
+++ b/taverna-perspective-myexperiment/src/main/java/net/sf/taverna/t2/ui/perspectives/myexperiment/ExampleWorkflowsPanel.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (C) 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.ui.perspectives.myexperiment;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.ChangeEvent;
+
+import org.apache.log4j.Logger;
+
+import net.sf.taverna.t2.workbench.icons.WorkbenchIcons;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.ResourceListPanel;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.MyExperimentClient;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Resource;
+import net.sf.taverna.t2.ui.perspectives.myexperiment.model.Workflow;
+
+/**
+ * @author Sergejs Aleksejevs, Jiten Bhagat
+ */
+public class ExampleWorkflowsPanel extends JPanel implements ActionListener, ChangeListener {
+
+  private static final String ACTION_REFRESH = "refresh_example_workflows";
+
+  private MainComponent pluginMainComponent;
+  private MyExperimentClient myExperimentClient;
+  private Logger logger;
+
+  private JLabel statusLabel;
+  private JButton refreshButton;
+
+  private List<Workflow> workflows = new ArrayList<Workflow>();
+
+  private ResourceListPanel workflowsListPanel;
+
+  public ExampleWorkflowsPanel(MainComponent component, MyExperimentClient client, Logger logger) {
+	super();
+
+	// set main variables to ensure access to myExperiment, logger and the
+	// parent component
+	this.pluginMainComponent = component;
+	this.myExperimentClient = client;
+	this.logger = logger;
+
+	this.initialiseUI();
+  }
+
+  public void actionPerformed(ActionEvent event) {
+	if (ACTION_REFRESH.equals(event.getActionCommand())) {
+	  this.refresh();
+	}
+  }
+
+  public void stateChanged(ChangeEvent event) {
+
+  }
+
+  public void clear() {
+	this.statusLabel.setText("");
+	this.workflowsListPanel.clear();
+  }
+
+  public void refresh() {
+	this.pluginMainComponent.getStatusBar().setStatus(this.getClass().getName(), "Fetching example workflows from myExperiment");
+	this.statusLabel.setText("");
+
+	// Make call to myExperiment API in a different thread
+	// (then use SwingUtilities.invokeLater to update the UI when ready).
+	new Thread("Refresh for ExampleWorkflowsPanel") {
+	  public void run() {
+		logger.debug("Refreshing Example Workflows tab");
+
+		try {
+		  workflows = myExperimentClient.getExampleWorkflows();
+
+		  SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+			  repopulate();
+			}
+		  });
+		} catch (Exception ex) {
+		  logger.error("Failed to refresh Example Workflows panel", ex);
+		}
+	  }
+	}.start();
+
+  }
+
+  public void repopulate() {
+	logger.debug("Repopulating Example Workflows tab");
+
+	this.pluginMainComponent.getStatusBar().setStatus(this.getClass().getName(), null);
+	this.statusLabel.setText("This will contain example resources to"
+		+ " help get you started in Taverna.  Coming Soon.");
+	this.statusLabel.setText(this.workflows.size() + " example workflows found");
+
+	// cannot cast a list of subclass items into a list of superclass items -
+	// hence a new list is created
+	this.workflowsListPanel.setListItems(new ArrayList<Resource>(this.workflows));
+
+	this.revalidate();
+  }
+
+  private void initialiseUI() {
+	this.setLayout(new BorderLayout());
+
+	JPanel topPanel = new JPanel(new BorderLayout());
+	topPanel.setBorder(BorderFactory.createEtchedBorder());
+	this.statusLabel = new JLabel();
+	this.statusLabel.setBorder(BorderFactory.createEmptyBorder(0, 7, 0, 0));
+	topPanel.add(this.statusLabel, BorderLayout.CENTER);
+	this.refreshButton = new JButton("Refresh", WorkbenchIcons.refreshIcon);
+	this.refreshButton.setActionCommand(ACTION_REFRESH);
+	this.refreshButton.addActionListener(this);
+	this.refreshButton.setToolTipText("Click this button to refresh the Example Workflows list");
+	topPanel.add(this.refreshButton, BorderLayout.EAST);
+	this.add(topPanel, BorderLayout.NORTH);
+
+	this.workflowsListPanel = new ResourceListPanel(this.pluginMainComponent, this.myExperimentClient, this.logger);
+	JScrollPane spExampleWorkflowList = new JScrollPane(this.workflowsListPanel);
+	spExampleWorkflowList.getVerticalScrollBar().setUnitIncrement(ResourcePreviewBrowser.PREFERRED_SCROLL);
+
+	this.add(spExampleWorkflowList, BorderLayout.CENTER);
+  }
+}


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

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphEditToolbarSection.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphEditToolbarSection.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphEditToolbarSection.java
new file mode 100644
index 0000000..a61259a
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphEditToolbarSection.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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.views.graph.toolbar;
+
+import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author Alex Nenadic
+ */
+public class GraphEditToolbarSection extends AbstractMenuSection {
+	public static final URI GRAPH_EDIT_TOOLBAR_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphEditToolbarSection");
+
+	public GraphEditToolbarSection() {
+		super(DEFAULT_TOOL_BAR, 30, GRAPH_EDIT_TOOLBAR_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/RenameWFInputOutputProcessorToolbarAction.java
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/RenameWFInputOutputProcessorToolbarAction.java b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/RenameWFInputOutputProcessorToolbarAction.java
new file mode 100644
index 0000000..1794e8f
--- /dev/null
+++ b/taverna-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/RenameWFInputOutputProcessorToolbarAction.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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.views.graph.toolbar;
+
+import static net.sf.taverna.t2.workbench.views.graph.toolbar.GraphEditToolbarSection.GRAPH_EDIT_TOOLBAR_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.edits.EditManager;
+import net.sf.taverna.t2.workbench.selection.SelectionManager;
+import net.sf.taverna.t2.workbench.views.graph.actions.RenameWFInputOutputProcessorAction;
+
+/**
+ * @author Alex Nenadic
+ */
+public class RenameWFInputOutputProcessorToolbarAction extends
+		AbstractMenuAction {
+	private static final URI RENAME_WF_INPUT_OUTPUT_PROCESSOR_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#graphToolbarRenameWFInputOutputProcessor");
+
+	private EditManager editManager;
+	private SelectionManager selectionManager;
+
+	public RenameWFInputOutputProcessorToolbarAction() {
+		super(GRAPH_EDIT_TOOLBAR_SECTION, 30,
+				RENAME_WF_INPUT_OUTPUT_PROCESSOR_URI);
+	}
+
+	@Override
+	protected Action createAction() {
+		return new RenameWFInputOutputProcessorAction(editManager,
+				selectionManager);
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
new file mode 100644
index 0000000..226078d
--- /dev/null
+++ b/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,29 @@
+net.sf.taverna.t2.workbench.views.graph.toolbar.GraphEditToolbarSection
+net.sf.taverna.t2.workbench.views.graph.toolbar.GraphDeleteToolbarSection
+net.sf.taverna.t2.workbench.views.graph.toolbar.GraphSaveToolbarSection
+net.sf.taverna.t2.workbench.views.graph.toolbar.AddWFInputToolbarAction
+net.sf.taverna.t2.workbench.views.graph.toolbar.AddWFOutputToolbarAction
+net.sf.taverna.t2.workbench.views.graph.toolbar.RenameWFInputOutputProcessorToolbarAction
+net.sf.taverna.t2.workbench.views.graph.toolbar.DeleteGraphComponentToolbarAction
+net.sf.taverna.t2.workbench.views.graph.toolbar.SaveGraphImageToolbarAction
+
+net.sf.taverna.t2.workbench.views.graph.menu.DiagramMenu
+net.sf.taverna.t2.workbench.views.graph.menu.DiagramSaveMenuSection
+net.sf.taverna.t2.workbench.views.graph.menu.DiagramZoomMenuSection
+
+net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection
+net.sf.taverna.t2.workbench.views.graph.menu.GraphCopyMenuSection
+net.sf.taverna.t2.workbench.views.graph.menu.GraphEditMenuSection
+net.sf.taverna.t2.workbench.views.graph.menu.GraphDeleteMenuSection
+net.sf.taverna.t2.workbench.views.graph.menu.GraphDetailsMenuSection
+
+net.sf.taverna.t2.workbench.views.graph.menu.InsertMenu
+
+net.sf.taverna.t2.workbench.views.graph.menu.AddWFInputMenuAction
+net.sf.taverna.t2.workbench.views.graph.menu.AddWFOutputMenuAction
+net.sf.taverna.t2.workbench.views.graph.menu.RenameWFInputOutputProcessorMenuAction
+net.sf.taverna.t2.workbench.views.graph.menu.DeleteGraphComponentMenuAction
+net.sf.taverna.t2.workbench.views.graph.menu.SaveGraphImageSubMenu
+net.sf.taverna.t2.workbench.views.graph.menu.ZoomInMenuAction
+net.sf.taverna.t2.workbench.views.graph.menu.ZoomOutMenuAction
+net.sf.taverna.t2.workbench.views.graph.menu.ResetDiagramMenuAction
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
new file mode 100644
index 0000000..70830ec
--- /dev/null
+++ b/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfigurationUIFactory

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI b/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
new file mode 100644
index 0000000..8086a8d
--- /dev/null
+++ b/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.views.graph.GraphViewComponentFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI b/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
new file mode 100644
index 0000000..563c21d
--- /dev/null
+++ b/taverna-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
@@ -0,0 +1 @@
+net.sf.taverna.t2.workbench.views.graph.GraphViewComponent

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/resources/META-INF/spring/graph-view-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/resources/META-INF/spring/graph-view-context-osgi.xml b/taverna-graph-view/src/main/resources/META-INF/spring/graph-view-context-osgi.xml
new file mode 100644
index 0000000..c7adec0
--- /dev/null
+++ b/taverna-graph-view/src/main/resources/META-INF/spring/graph-view-context-osgi.xml
@@ -0,0 +1,46 @@
+<?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="GraphViewConfigurationUIFactory" interface="uk.org.taverna.configuration.ConfigurationUIFactory" />
+
+	<service ref="GraphViewComponentFactory" interface="net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI" />
+
+	<service ref="GraphEditToolbarSection" auto-export="interfaces" />
+	<service ref="GraphDeleteToolbarSection" auto-export="interfaces" />
+	<service ref="AddWFInputToolbarAction" auto-export="interfaces" />
+	<service ref="AddWFOutputToolbarAction" auto-export="interfaces" />
+	<service ref="RenameWFInputOutputProcessorToolbarAction" auto-export="interfaces" />
+	<service ref="DeleteGraphComponentToolbarAction" auto-export="interfaces" />
+	<service ref="DiagramMenu" auto-export="interfaces" />
+	<service ref="DiagramSaveMenuSection" auto-export="interfaces" />
+	<service ref="DiagramZoomMenuSection" auto-export="interfaces" />
+	<service ref="GraphMenuSection" auto-export="interfaces" />
+	<service ref="GraphCopyMenuSection" auto-export="interfaces" />
+	<service ref="GraphEditMenuSection" auto-export="interfaces" />
+	<service ref="GraphDeleteMenuSection" auto-export="interfaces" />
+	<service ref="GraphDetailsMenuSection" auto-export="interfaces" />
+	<service ref="InsertMenu" auto-export="interfaces" />
+	<service ref="AddWFInputMenuAction" auto-export="interfaces" />
+	<service ref="AddWFOutputMenuAction" auto-export="interfaces" />
+	<service ref="RenameWFInputOutputProcessorMenuAction" auto-export="interfaces" />
+	<service ref="DeleteGraphComponentMenuAction" auto-export="interfaces" />
+	<!-- <service ref="SaveGraphImageSubMenu" auto-export="interfaces" /> -->
+	<service ref="ZoomInMenuAction" auto-export="interfaces" />
+	<service ref="ZoomOutMenuAction" auto-export="interfaces" />
+	<service ref="ResetDiagramMenuAction" auto-export="interfaces" />
+
+	<reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" />
+	<reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" />
+	<reference id="menuManager" interface="net.sf.taverna.t2.ui.menu.MenuManager" />
+	<reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" />
+	<reference id="colourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" />
+	<reference id="workbenchConfiguration" interface="net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration" />
+	<reference id="configurationManager" interface="uk.org.taverna.configuration.ConfigurationManager" />
+	<reference id="serviceRegistry" interface="uk.org.taverna.commons.services.ServiceRegistry" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/main/resources/META-INF/spring/graph-view-context.xml
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/main/resources/META-INF/spring/graph-view-context.xml b/taverna-graph-view/src/main/resources/META-INF/spring/graph-view-context.xml
new file mode 100644
index 0000000..9968805
--- /dev/null
+++ b/taverna-graph-view/src/main/resources/META-INF/spring/graph-view-context.xml
@@ -0,0 +1,107 @@
+<?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="graphViewConfiguration"
+		class="net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration">
+		<constructor-arg name="configurationManager" ref="configurationManager" />
+	</bean>
+
+	<bean id="GraphViewConfigurationUIFactory"
+		class="net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfigurationUIFactory">
+		<property name="graphViewConfiguration">
+			<ref local="graphViewConfiguration" />
+		</property>
+	</bean>
+
+	<bean id="GraphViewComponentFactory" class="net.sf.taverna.t2.workbench.views.graph.GraphViewComponentFactory">
+		<property name="colourManager" ref="colourManager" />
+		<property name="editManager" ref="editManager" />
+		<property name="menuManager" ref="menuManager" />
+		<property name="graphViewConfiguration">
+			<ref local="graphViewConfiguration" />
+		</property>
+		<property name="workbenchConfiguration" ref="workbenchConfiguration" />
+		<property name="selectionManager" ref="selectionManager" />
+		<property name="fileManager" ref="fileManager" />
+		<property name="serviceRegistry" ref="serviceRegistry" />
+	</bean>
+
+	<bean id="GraphEditToolbarSection"
+		class="net.sf.taverna.t2.workbench.views.graph.toolbar.GraphEditToolbarSection" />
+	<bean id="GraphDeleteToolbarSection"
+		class="net.sf.taverna.t2.workbench.views.graph.toolbar.GraphDeleteToolbarSection" />
+	<bean id="AddWFInputToolbarAction"
+		class="net.sf.taverna.t2.workbench.views.graph.toolbar.AddWFInputToolbarAction">
+		<property name="editManager" ref="editManager" />
+		<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="AddWFOutputToolbarAction"
+		class="net.sf.taverna.t2.workbench.views.graph.toolbar.AddWFOutputToolbarAction">
+		<property name="editManager" ref="editManager" />
+		<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="RenameWFInputOutputProcessorToolbarAction"
+		class="net.sf.taverna.t2.workbench.views.graph.toolbar.RenameWFInputOutputProcessorToolbarAction">
+		<property name="editManager" ref="editManager" />
+		<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="DeleteGraphComponentToolbarAction"
+		class="net.sf.taverna.t2.workbench.views.graph.toolbar.DeleteGraphComponentToolbarAction">
+		<property name="editManager" ref="editManager" />
+		<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="DiagramMenu"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.DiagramMenu" />
+	<bean id="DiagramSaveMenuSection"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.DiagramSaveMenuSection" />
+	<bean id="DiagramZoomMenuSection"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.DiagramZoomMenuSection" />
+	<bean id="GraphMenuSection"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection" />
+	<bean id="GraphCopyMenuSection"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.GraphCopyMenuSection" />
+	<bean id="GraphEditMenuSection"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.GraphEditMenuSection" />
+	<bean id="GraphDeleteMenuSection"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.GraphDeleteMenuSection" />
+	<bean id="GraphDetailsMenuSection"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.GraphDetailsMenuSection" />
+	<bean id="InsertMenu" class="net.sf.taverna.t2.workbench.views.graph.menu.InsertMenu" />
+	<bean id="AddWFInputMenuAction"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.AddWFInputMenuAction">
+		<property name="editManager" ref="editManager" />
+		<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="AddWFOutputMenuAction"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.AddWFOutputMenuAction">
+		<property name="editManager" ref="editManager" />
+		<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="RenameWFInputOutputProcessorMenuAction"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.RenameWFInputOutputProcessorMenuAction">
+		<property name="editManager" ref="editManager" />
+		<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<bean id="DeleteGraphComponentMenuAction"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.DeleteGraphComponentMenuAction">
+		<property name="editManager" ref="editManager" />
+		<property name="selectionManager" ref="selectionManager" />
+	</bean>
+	<!-- <bean id="SaveGraphImageSubMenu"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.SaveGraphImageSubMenu">
+		<property name="fileManager" ref="fileManager" />
+		<property name="workbenchConfiguration" ref="workbenchConfiguration" />
+		<property name="selectionManager" ref="selectionManager" />
+		<property name="graphViewComponent" ref="GraphViewComponent" />
+	</bean> -->
+	<bean id="ZoomInMenuAction"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.ZoomInMenuAction" />
+	<bean id="ZoomOutMenuAction"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.ZoomOutMenuAction" />
+	<bean id="ResetDiagramMenuAction"
+		class="net.sf.taverna.t2.workbench.views.graph.menu.ResetDiagramMenuAction" />
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/test/resources/nested_iteration.t2flow
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/test/resources/nested_iteration.t2flow b/taverna-graph-view/src/test/resources/nested_iteration.t2flow
new file mode 100644
index 0000000..9b50c7f
--- /dev/null
+++ b/taverna-graph-view/src/test/resources/nested_iteration.t2flow
@@ -0,0 +1,111 @@
+<workflow xmlns="http://taverna.sf.net/2008/xml/t2flow"><dataflow id="23f84bb1-4a04-47fa-8150-7063310db697" role="top"><name>nested_iteration</name><inputPorts /><outputPorts><port><name>concat</name></port><port><name>list</name></port><port><name>constant</name></port></outputPorts><processors><processor><name>constant</name><inputPorts /><outputPorts><port><name>value</name><depth>0</depth><granularDepth>0</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>stringconstant-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.stringconstant.StringConstantActivity</class><inputMap /><outputMap><map from="value" to="value" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.stringconstant.StringConstantConfigurationBean xmlns="">
+  <value>constant</value>
+</net.sf.taverna.t2.activities.stringconstant.StringConstantConfigurationBean></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns="">
+  <maxJobs>1</maxJobs>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryCo
 nfig xmlns="">
+  <backoffFactor>1.0</backoffFactor>
+  <initialDelay>0</initialDelay>
+  <maxDelay>0</maxDelay>
+  <maxRetries>0</maxRetries>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross /></strategy></iteration></iterationStrategyStack></processor><processor><name>generate_list</name><inputPorts><port><name>prefix</name><depth>0</depth></port></inputPorts><outputPorts><port><name>list</name><depth>1</depth><granularDepth>1</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>beanshell-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.beanshell.BeanshellActivity</class><inputMap><map from="prefix" to="prefi
 x" /></inputMap><outputMap><map from="list" to="list" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean xmlns="">
+  <script>list = new ArrayList();
+for (int i = 0; i &lt; 20; i++) {
+ list.add(prefix + i);
+}</script>
+  <dependencies />
+  <inputs>
+    <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean>
+      <handledReferenceSchemes />
+      <translatedElementType>java.lang.String</translatedElementType>
+      <allowsLiteralValues>true</allowsLiteralValues>
+      <name>prefix</name>
+      <depth>0</depth>
+      <mimeTypes>
+        <string>'text/plain'</string>
+      </mimeTypes>
+    </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean>
+  </inputs>
+  <outputs>
+    <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean>
+      <granularDepth>1</granularDepth>
+      <name>list</name>
+      <depth>1</depth>
+      <mimeTypes>
+        <string>l('text/plain')</string>
+      </mimeTypes>
+    </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean>
+  </outputs>
+</net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns="">
+  <maxJobs>1</maxJobs>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryCo
 nfig xmlns="">
+  <backoffFactor>1.0</backoffFactor>
+  <initialDelay>0</initialDelay>
+  <maxDelay>0</maxDelay>
+  <maxRetries>0</maxRetries>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><port name="prefix" depth="0" /></strategy></iteration></iterationStrategyStack></processor><processor><name>merge</name><inputPorts><port><name>in1</name><depth>0</depth></port><port><name>in2</name><depth>0</depth></port></inputPorts><outputPorts><port><name>out</name><depth>0</depth><granularDepth>0</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>dataflow-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.dataflow.DataflowA
 ctivity</class><inputMap><map from="in2" to="in2" /><map from="in1" to="in1" /></inputMap><outputMap><map from="out" to="out" /></outputMap><configBean encoding="dataflow"><dataflow ref="79ad4092-abcb-42bf-ac98-d66dfac67dff" /></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns="">
+  <maxJobs>1</maxJobs>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryCo
 nfig xmlns="">
+  <backoffFactor>1.0</backoffFactor>
+  <initialDelay>0</initialDelay>
+  <maxDelay>0</maxDelay>
+  <maxRetries>0</maxRetries>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross><port name="in1" depth="0" /><port name="in2" depth="0" /></cross></strategy></iteration></iterationStrategyStack></processor><processor><name>generate_list_prefix_defaultValue</name><inputPorts /><outputPorts><port><name>value</name><depth>0</depth><granularDepth>0</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>stringconstant-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.stringconstant.StringConstantActivity</cla
 ss><inputMap /><outputMap><map from="value" to="value" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.stringconstant.StringConstantConfigurationBean xmlns="">
+  <value>prefix</value>
+</net.sf.taverna.t2.activities.stringconstant.StringConstantConfigurationBean></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns="">
+  <maxJobs>1</maxJobs>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryCo
 nfig xmlns="">
+  <backoffFactor>1.0</backoffFactor>
+  <initialDelay>0</initialDelay>
+  <maxDelay>0</maxDelay>
+  <maxRetries>0</maxRetries>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross /></strategy></iteration></iterationStrategyStack></processor></processors><conditions><condition control="generate_list" target="constant" /></conditions><datalinks><datalink><sink type="processor"><processor>generate_list</processor><port>prefix</port></sink><source type="processor"><processor>generate_list_prefix_defaultValue</processor><port>value</port></source></datalink><datalink><sink type="processor"><processor>merge</processor><port>in1</port></sink><source type="processor"><processor>constant</processor><port>value</port></
 source></datalink><datalink><sink type="merge"><processor>merge</processor><port>in2</port></sink><source type="processor"><processor>generate_list</processor><port>list</port></source></datalink><datalink><sink type="merge"><processor>merge</processor><port>in2</port></sink><source type="processor"><processor>generate_list</processor><port>list</port></source></datalink><datalink><sink type="dataflow"><port>concat</port></sink><source type="processor"><processor>merge</processor><port>out</port></source></datalink><datalink><sink type="dataflow"><port>list</port></sink><source type="processor"><processor>generate_list</processor><port>list</port></source></datalink><datalink><sink type="dataflow"><port>constant</port></sink><source type="processor"><processor>constant</processor><port>value</port></source></datalink></datalinks></dataflow><dataflow id="79ad4092-abcb-42bf-ac98-d66dfac67dff" role="nested"><name>Untitled workflow #24</name><inputPorts><port><name>in1</name><depth>0</d
 epth><granularDepth>0</granularDepth></port><port><name>in2</name><depth>0</depth><granularDepth>0</granularDepth></port></inputPorts><outputPorts><port><name>out</name></port></outputPorts><processors><processor><name>Nested_Workflow</name><inputPorts><port><name>in2</name><depth>0</depth></port><port><name>in1</name><depth>0</depth></port></inputPorts><outputPorts><port><name>out</name><depth>0</depth><granularDepth>0</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>dataflow-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.dataflow.DataflowActivity</class><inputMap><map from="in2" to="in2" /><map from="in1" to="in1" /></inputMap><outputMap><map from="out" to="out" /></outputMap><configBean encoding="dataflow"><dataflow ref="ebd93027-c046-4a04-befa-c5715e8ba3da" /></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</gro
 up><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns="">
+  <maxJobs>1</maxJobs>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryCo
 nfig xmlns="">
+  <backoffFactor>1.0</backoffFactor>
+  <initialDelay>0</initialDelay>
+  <maxDelay>0</maxDelay>
+  <maxRetries>0</maxRetries>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross><port name="in1" depth="0" /><port name="in2" depth="0" /></cross></strategy></iteration></iterationStrategyStack></processor></processors><conditions /><datalinks><datalink><sink type="processor"><processor>Nested_Workflow</processor><port>in2</port></sink><source type="dataflow"><port>in2</port></source></datalink><datalink><sink type="processor"><processor>Nested_Workflow</processor><port>in1</port></sink><source type="dataflow"><port>in1</port></source></datalink><datalink><sink type="dataflow"><port>out</port></sink><source type=
 "processor"><processor>Nested_Workflow</processor><port>out</port></source></datalink></datalinks></dataflow><dataflow id="ebd93027-c046-4a04-befa-c5715e8ba3da" role="nested"><name>Untitled workflow #36</name><inputPorts><port><name>in1</name><depth>0</depth><granularDepth>0</granularDepth></port><port><name>in2</name><depth>0</depth><granularDepth>0</granularDepth></port></inputPorts><outputPorts><port><name>out</name></port></outputPorts><processors><processor><name>concat</name><inputPorts><port><name>in1</name><depth>0</depth></port><port><name>in2</name><depth>0</depth></port></inputPorts><outputPorts><port><name>out</name><depth>0</depth><granularDepth>0</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>beanshell-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.beanshell.BeanshellActivity</class><inputMap><map from="in2" to="in2" /><map from="in1" to="in1" /></i
 nputMap><outputMap><map from="out" to="out" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean xmlns="">
+  <script>Thread.sleep(200);
+out = in1 + in2;</script>
+  <dependencies />
+  <inputs>
+    <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean>
+      <handledReferenceSchemes />
+      <translatedElementType>java.lang.String</translatedElementType>
+      <allowsLiteralValues>true</allowsLiteralValues>
+      <name>in1</name>
+      <depth>0</depth>
+      <mimeTypes>
+        <string>'text/plain'</string>
+      </mimeTypes>
+    </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean>
+    <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean>
+      <handledReferenceSchemes />
+      <translatedElementType>java.lang.String</translatedElementType>
+      <allowsLiteralValues>true</allowsLiteralValues>
+      <name>in2</name>
+      <depth>0</depth>
+      <mimeTypes>
+        <string>'text/plain'</string>
+      </mimeTypes>
+    </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean>
+  </inputs>
+  <outputs>
+    <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean>
+      <granularDepth>0</granularDepth>
+      <name>out</name>
+      <depth>0</depth>
+      <mimeTypes>
+        <string>'text/plain'</string>
+      </mimeTypes>
+    </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean>
+  </outputs>
+</net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns="">
+  <maxJobs>1</maxJobs>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryCo
 nfig xmlns="">
+  <backoffFactor>1.0</backoffFactor>
+  <initialDelay>0</initialDelay>
+  <maxDelay>0</maxDelay>
+  <maxRetries>0</maxRetries>
+</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross><port name="in1" depth="0" /><port name="in2" depth="0" /></cross></strategy></iteration></iterationStrategyStack></processor></processors><conditions /><datalinks><datalink><sink type="processor"><processor>concat</processor><port>in1</port></sink><source type="dataflow"><port>in1</port></source></datalink><datalink><sink type="processor"><processor>concat</processor><port>in2</port></sink><source type="dataflow"><port>in2</port></source></datalink><datalink><sink type="dataflow"><port>out</port></sink><source type="processor"><proce
 ssor>concat</processor><port>out</port></source></datalink></datalinks></dataflow></workflow>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-graph-view/src/test/resources/nested_iteration.xml
----------------------------------------------------------------------
diff --git a/taverna-graph-view/src/test/resources/nested_iteration.xml b/taverna-graph-view/src/test/resources/nested_iteration.xml
new file mode 100644
index 0000000..3a547bb
--- /dev/null
+++ b/taverna-graph-view/src/test/resources/nested_iteration.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<s:scufl xmlns:s="http://org.embl.ebi.escience/xscufl/0.1alpha" version="0.2" log="0">
+  <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:c7016fc0-c2f4-4171-b6f1-430f408f4822" author="" title="nested_iteration" />
+  <s:processor name="generate_list">
+    <s:defaults>
+      <s:default name="prefix">prefix</s:default>
+    </s:defaults>
+    <s:beanshell>
+      <s:scriptvalue>list = new ArrayList();
+for (int i = 0; i &lt; 20; i++) {
+ list.add(prefix + i);
+}</s:scriptvalue>
+      <s:beanshellinputlist>
+        <s:beanshellinput s:syntactictype="'text/plain'">prefix</s:beanshellinput>
+      </s:beanshellinputlist>
+      <s:beanshelloutputlist>
+        <s:beanshelloutput s:syntactictype="l('text/plain')">list</s:beanshelloutput>
+      </s:beanshelloutputlist>
+      <s:dependencies s:classloader="iteration" />
+    </s:beanshell>
+  </s:processor>
+  <s:processor name="constant" boring="true">
+    <s:stringconstant>constant</s:stringconstant>
+  </s:processor>
+  <s:processor name="merge">
+    <s:workflow>
+      <s:scufl version="0.2" log="0">
+        <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:3368fb8d-ecc7-4fcd-b511-6ace84b13c81" author="" title="Untitled workflow #24" />
+        <s:processor name="Nested_Workflow">
+          <s:workflow>
+            <s:scufl version="0.2" log="0">
+              <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:75b99c76-7a76-4d3c-8d39-8c48df3355ad" author="" title="Untitled workflow #36" />
+              <s:processor name="concat">
+                <s:beanshell>
+                  <s:scriptvalue>Thread.sleep(200);
+out = in1 + in2;</s:scriptvalue>
+                  <s:beanshellinputlist>
+                    <s:beanshellinput s:syntactictype="'text/plain'">in1</s:beanshellinput>
+                    <s:beanshellinput s:syntactictype="'text/plain'">in2</s:beanshellinput>
+                  </s:beanshellinputlist>
+                  <s:beanshelloutputlist>
+                    <s:beanshelloutput s:syntactictype="'text/plain'">out</s:beanshelloutput>
+                  </s:beanshelloutputlist>
+                  <s:dependencies s:classloader="iteration" />
+                </s:beanshell>
+              </s:processor>
+              <s:link source="in1" sink="concat:in1" />
+              <s:link source="in2" sink="concat:in2" />
+              <s:link source="concat:out" sink="out" />
+              <s:source name="in1" />
+              <s:source name="in2" />
+              <s:sink name="out" />
+            </s:scufl>
+          </s:workflow>
+        </s:processor>
+        <s:link source="in1" sink="Nested_Workflow:in1" />
+        <s:link source="in2" sink="Nested_Workflow:in2" />
+        <s:link source="Nested_Workflow:out" sink="out" />
+        <s:source name="in1" />
+        <s:source name="in2" />
+        <s:sink name="out" />
+      </s:scufl>
+    </s:workflow>
+    <s:mergemode input="in2" mode="merge" />
+  </s:processor>
+  <s:link source="constant:value" sink="merge:in1" />
+  <s:link source="generate_list:list" sink="merge:in2" />
+  <s:link source="generate_list:list" sink="merge:in2" />
+  <s:link source="constant:value" sink="constant" />
+  <s:link source="generate_list:list" sink="list" />
+  <s:link source="merge:out" sink="concat" />
+  <s:sink name="concat">
+    <s:metadata>
+      <s:mimeTypes>
+        <s:mimeType>'text/plain'</s:mimeType>
+      </s:mimeTypes>
+    </s:metadata>
+  </s:sink>
+  <s:sink name="list">
+    <s:metadata>
+      <s:mimeTypes>
+        <s:mimeType>l('text/plain')</s:mimeType>
+      </s:mimeTypes>
+    </s:metadata>
+  </s:sink>
+  <s:sink name="constant">
+    <s:metadata>
+      <s:mimeTypes>
+        <s:mimeType>'text/plain'</s:mimeType>
+      </s:mimeTypes>
+    </s:metadata>
+  </s:sink>
+  <s:coordination name="constant_BLOCKON_generate_list">
+    <s:condition>
+      <s:state>Completed</s:state>
+      <s:target>generate_list</s:target>
+    </s:condition>
+    <s:action>
+      <s:target>constant</s:target>
+      <s:statechange>
+        <s:from>Scheduled</s:from>
+        <s:to>Running</s:to>
+      </s:statechange>
+    </s:action>
+  </s:coordination>
+  <s:coordination name="merge_BLOCKON_generate_list">
+    <s:condition>
+      <s:state>Completed</s:state>
+      <s:target>generate_list</s:target>
+    </s:condition>
+    <s:action>
+      <s:target>merge</s:target>
+      <s:statechange>
+        <s:from>Scheduled</s:from>
+        <s:to>Running</s:to>
+      </s:statechange>
+    </s:action>
+  </s:coordination>
+</s:scufl>
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-helper-api/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-helper-api/pom.xml b/taverna-helper-api/pom.xml
new file mode 100644
index 0000000..4b40e30
--- /dev/null
+++ b/taverna-helper-api/pom.xml
@@ -0,0 +1,73 @@
+<?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>helper-api</artifactId>
+	<packaging>bundle</packaging>
+	<name>Help System</name>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<configuration>
+					<instructions>
+						<Embed-Dependency>javahelp</Embed-Dependency>
+						<Import-Package>org.jdesktop.jdic.browser;resolution:=optional,*</Import-Package>
+					</instructions>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>workbench-api</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>javax.help</groupId>
+			<artifactId>javahelp</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<!-- Required by javahelp -->
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>com.springsource.javax.servlet</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>com.springsource.javax.servlet.jsp</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>javax.el</groupId>
+			<artifactId>com.springsource.javax.el</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.log4j</groupId>
+			<artifactId>com.springsource.org.apache.log4j</artifactId>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpCollator.java
----------------------------------------------------------------------
diff --git a/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpCollator.java b/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpCollator.java
new file mode 100644
index 0000000..8b19b69
--- /dev/null
+++ b/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpCollator.java
@@ -0,0 +1,307 @@
+package net.sf.taverna.t2.workbench.helper;
+
+import java.awt.Component;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.help.BadIDException;
+import javax.help.HelpSet;
+import javax.help.HelpSetException;
+import javax.help.Map.ID;
+import javax.help.TryMap;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreePath;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This class loads the {@link HelpSet} and also deals with the registration of
+ * ids and the decoding from a {@link Component} to the corresponding id. These
+ * two sets of functionality should possibly be separated.
+ * 
+ * @author alanrw
+ */
+// TODO Convert to a bean
+public final class HelpCollator {
+	private static Logger logger = Logger.getLogger(HelpCollator.class);
+	/**
+	 * The HelpSet that is being used.
+	 */
+	private static HelpSet hs = null;
+	/**
+	 * The mapping from components to ids. This is used because of problems with
+	 * CSH throwing exceptions because it tried to use ids that were not in the
+	 * map.
+	 */
+	private static Map<Component, String> idMap;
+	/**
+	 * Indicates whether the HelpCollator has been initialized.
+	 */
+	private static boolean initialized = false;
+	/**
+	 * A Pattern for normalizing the ids.
+	 */
+	private static Pattern nonAlphanumeric;
+	/**
+	 * The emptyHelp is set if the HelpCollator was unable to read the
+	 */
+	private static boolean emptyHelp = true;
+	private static int TIMEOUT = 5000;
+
+	private static String externalHelpSetURL = "http://www.mygrid.org.uk/taverna/helpset/"
+			+ version() + "/helpset.hs";
+
+	// private static Profile profile = ProfileFactory.getInstance().getProfile();
+	private static String version() {
+		return "NO-VERSION";//profile.getVersion();
+		// TODO find a better way to find the version
+	}
+
+	/**
+	 * Attempt to read the up-to-date HelpSet from the web
+	 */
+	private static void readExternalHelpSet() {
+		try {
+			URL url = new URL(externalHelpSetURL);
+			checkConnection(url);
+			hs = new HelpSet(null, url);
+			if (hs.getLocalMap() == null) {
+			    hs = null;
+				logger.error("Helpset from " + externalHelpSetURL
+						+ " local map was null");
+			} else
+				logger.info("Read external help set from " + externalHelpSetURL);
+		} catch (MissingResourceException e) {
+		    logger.error("No external HelpSet URL specified", e);
+		} catch (MalformedURLException e) {
+		    logger.error("External HelpSet URL is malformed", e);
+		} catch (HelpSetException e) {
+		    logger.error("External HelpSet could not be read", e);
+		} catch (IOException e) {
+			logger.error("IOException reading External HelpSet", e);
+		}
+	}
+
+	private static void checkConnection(URL url) throws IOException {
+		if (!url.getProtocol().startsWith("http"))
+			return;
+		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+		connection.setReadTimeout(TIMEOUT);
+		connection.setConnectTimeout(TIMEOUT);
+		connection.setRequestMethod("HEAD");
+		connection.getInputStream().close();
+		connection.disconnect();
+	}
+
+	/**
+	 * This methods creates a HelpSet based upon, in priority, the external
+	 * HelpSet, then a newly created empty HelpSet.
+	 */
+	private static void initialize() {
+		if (initialized)
+			return;
+		readExternalHelpSet();
+		if (hs == null) {
+			hs = new HelpSet();
+			hs.setLocalMap(new TryMap());
+		} else {
+			logger.trace("EmptyHelp set to false");
+			emptyHelp = false;
+		}
+		idMap = new HashMap<>();
+		nonAlphanumeric = Pattern.compile("[^a-z0-9\\.]");
+		initialized = true;
+	}
+
+	/**
+	 * Indicates if an empty HelpSet is being used
+	 *
+	 * @return
+	 */
+	public static boolean isEmptyHelp() {
+		return emptyHelp;
+	}
+
+	public static URL getURLFromID(String id) throws BadIDException,
+			MalformedURLException {
+		initialize();
+		logger.trace("Looking for id: " + id);
+		ID theId = ID.create(id, hs);
+		if (theId == null)
+			return null;
+		return hs.getCombinedMap().getURLFromID(theId);
+	}
+
+	/**
+	 * Register a component under the specified id. The method checks that the
+	 * id is known to the HelpSet's map.
+	 * 
+	 * @param component
+	 * @param id
+	 */
+	public static void registerComponent(Component component, String id) {
+		logger.trace("Attempting to register " + id);
+		initialize();
+		String normalizedId = normalizeString(id.toLowerCase());
+		if (idMap.containsKey(component)) {
+			logger.info("Registered " + normalizedId);
+			return;
+		}
+
+		/*
+		 * If Workbench is started up while there is no network connection -
+		 * hs.getLocalMap() is null for some reason
+		 */
+		if (hs != null && hs.getLocalMap() != null
+				&& hs.getLocalMap().isValidID(normalizedId, hs)) {
+			idMap.put(component, normalizedId);
+			logger.info("Registered " + normalizedId);
+		} else
+			logger.warn("Refused to register component as " + normalizedId
+					+ " not in map");
+	}
+
+	/**
+	 * Register a component. Since no id is specified, the HelpCollator takes
+	 * the canonical name of the component's class. This is useful when an
+	 * explicit hierarchy-based approach has been taken.
+	 *
+	 * @param component
+	 */
+	public static void registerComponent(Component component) {
+		String canonicalName = component.getClass().getCanonicalName();
+		if (canonicalName != null)
+			registerComponent(component, canonicalName);
+	}
+
+	/**
+	 * Register a component based upon its parent's class and a suffix
+	 * indicating the component's purpose in the parent.
+	 *
+	 * @param component
+	 * @param parent
+	 * @param suffix
+	 */
+	public static void registerComponent(Component component, Object parent,
+			String suffix) {
+		String canonicalName = parent.getClass().getCanonicalName();
+		if (canonicalName != null)
+			registerComponent(component, canonicalName + "-" + suffix);
+	}
+
+	/**
+	 * Try to find an id for the Component. This code should be re-written when
+	 * we have more experience in how to couple the UI and HelpSets.
+	 *
+	 * @param c
+	 * @return
+	 */
+	static String getHelpID(Component c) {
+		initialize();
+		boolean found = false;
+		String result = null;
+		if (c instanceof JTree) {
+			String idInTree = getHelpIDInTree((JTree) c);
+			if (idInTree != null) {
+				found = true;
+				result = idInTree;
+			}
+		}
+		Component working = c;
+		if (c != null)
+			logger.trace("Starting at a " + working.getClass());
+		while (!found && (working != null)) {
+			if (idMap.containsKey(working)) {
+				result = idMap.get(working);
+				found = true;
+				logger.trace("Found component id " + result);
+			} else {
+				String className = working.getClass().getCanonicalName();
+				if (hs.getLocalMap().isValidID(className, hs)) {
+					result = className;
+					found = true;
+					logger.trace("Found class name " + result);
+				}
+			}
+			if (!found) {
+				working = working.getParent();
+				if (working != null)
+					logger.trace("Moved up to a " + working.getClass());
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * Change the input String into an id that contains only alphanumeric
+	 * characters or hyphens.
+	 *
+	 * @param input
+	 * @return
+	 */
+	private static String normalizeString(String input) {
+		Matcher m = nonAlphanumeric.matcher(input);
+		return m.replaceAll("-");
+	}
+
+	/**
+	 * If help is sought on part of a JTree, then this method attempts to find a
+	 * node of the tree that can be mapped to an id. The possibilities are ad
+	 * hoc and should be re-examined when more experience is gained.
+	 * 
+	 * @param c
+	 * @return
+	 */
+	private static String getHelpIDInTree(JTree c) {
+		initialize();
+
+		TreePath tp = c.getSelectionPath();
+		if (tp == null)
+			return null;
+
+		Object o = tp.getLastPathComponent();
+		if (o == null)
+			return null;
+
+		if (o instanceof DefaultMutableTreeNode) {
+			DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) o;
+			if (dmtn.getUserObject() != null)
+				o = dmtn.getUserObject();
+		}
+
+		String className = o.getClass().getCanonicalName();
+
+		logger.trace("Tree node as a string is " + o);
+
+		String possibility = normalizeString(o.toString().toLowerCase());
+
+		logger.trace("Normalized is " + possibility);
+		logger.trace("Tree node class name is " + className);
+
+		possibility = className + "-" + possibility;
+
+		logger.trace("Possibility is " + possibility);
+
+		String result;
+		if (hs.getLocalMap().isValidID(possibility, hs)) {
+			result = possibility;
+			logger.trace("Accepted tree node " + result);
+		} else if (hs.getLocalMap().isValidID(className, hs)) {
+			result = className;
+			logger.trace("Found tree node class name " + result);
+		} else {
+			result = null;
+		}
+
+		logger.debug("Tree node is a " + o.getClass());
+		return result;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpEnabledDialog.java
----------------------------------------------------------------------
diff --git a/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpEnabledDialog.java b/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpEnabledDialog.java
new file mode 100644
index 0000000..ec17171
--- /dev/null
+++ b/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpEnabledDialog.java
@@ -0,0 +1,101 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.helper;
+
+import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow;
+import static net.sf.taverna.t2.workbench.helper.HelpCollator.registerComponent;
+import static net.sf.taverna.t2.workbench.helper.Helper.setKeyCatcher;
+
+import java.awt.Dialog;
+import java.awt.Frame;
+import java.awt.HeadlessException;
+
+import javax.swing.JDialog;
+
+/**
+ * This class extends JDialog to register the dialog and also attach a key
+ * catcher so that F1 is interpreted as help
+ *
+ * @author alanrw
+ */
+public class HelpEnabledDialog extends JDialog {
+	private static final long serialVersionUID = -5068807887477419800L;
+
+	/**
+	 * Create a HelpEnabledDialog, register it (if possible) with the
+	 * HelpCollator and attach a keycatcher.
+	 *
+	 * @param owner
+	 * @param title
+	 * @param modal
+	 * @param id
+	 * @throws HeadlessException
+	 */
+	public HelpEnabledDialog(Frame owner, String title, boolean modal, String id)
+			throws HeadlessException {
+		super(owner == null ? getMainWindow() : owner, title, modal);
+
+		if (id != null)
+			registerComponent(this, id);
+		else if (owner != null)
+			registerComponent(this, owner.getClass().getCanonicalName()
+					+ "-dialog");
+		else if (title != null && !title.isEmpty())
+			registerComponent(this, title);
+		setKeyCatcher(this);
+	}
+
+	/**
+	 * Create a HelpEnabledDialog, register it (if possible) with the
+	 * HelpCollator and attach a keycatcher.
+	 *
+	 * @param owner
+	 * @param title
+	 * @param modal
+	 * @param id
+	 * @throws HeadlessException
+	 */
+	public HelpEnabledDialog(Dialog owner, String title, boolean modal,
+			String id) throws HeadlessException {
+		super(owner, title, modal);
+		if (id != null)
+			registerComponent(this, id);
+		else if (owner != null)
+			registerComponent(this, owner.getClass().getCanonicalName()
+					+ "-dialog");
+		setKeyCatcher(this);
+	}
+
+	/**
+	 * Create a HelpEnabledDialog, register it (if possible) with the
+	 * HelpCollator and attach a keycatcher.
+	 *
+	 * @param owner
+	 * @param title
+	 * @param modal
+	 * @throws HeadlessException
+	 */
+	public HelpEnabledDialog(Frame parent, String title, boolean modal) {
+		this(parent, title, modal, null);
+	}
+
+	/**
+	 * Create a HelpEnabledDialog, register it (if possible) with the
+	 * HelpCollator and attach a keycatcher.
+	 *
+	 * @param owner
+	 * @param title
+	 * @param modal
+	 * @throws HeadlessException
+	 */
+	public HelpEnabledDialog(Dialog parent, String title, boolean modal) {
+		this(parent, title, modal, null);
+	}
+
+	@Override
+	public void setVisible(boolean b) {
+		setLocationRelativeTo(getParent());
+		super.setVisible(b);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/Helper.java
----------------------------------------------------------------------
diff --git a/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/Helper.java b/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/Helper.java
new file mode 100644
index 0000000..21b0f75
--- /dev/null
+++ b/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/Helper.java
@@ -0,0 +1,187 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.workbench.helper;
+
+import static java.awt.Desktop.getDesktop;
+import static java.awt.MouseInfo.getPointerInfo;
+import static javax.swing.JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT;
+import static javax.swing.KeyStroke.getKeyStroke;
+import static net.sf.taverna.t2.workbench.helper.HelpCollator.getHelpID;
+import static net.sf.taverna.t2.workbench.helper.HelpCollator.getURLFromID;
+
+import java.awt.AWTEvent;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import javax.help.BadIDException;
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JRootPane;
+import javax.swing.RootPaneContainer;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This class creates the dialogs for the presentation of the HelpSet held by
+ * the HelpCollator.
+ *
+ * @author alanrw
+ */
+public final class Helper {
+	private static Helper instance;
+	private static Logger logger = Logger.getLogger(Helper.class);
+
+	/**
+	 * Create a Helper and initialize the static variables.
+	 */
+	private Helper() {
+	}
+
+	/**
+	 * Get the singleton instance of Helper. In theory there could be more than
+	 * one.
+	 *
+	 * @return
+	 */
+	private static Helper getInstance() {
+		if (instance == null)
+			instance = new Helper();
+		return instance;
+	}
+
+	/**
+	 * Show in the current dialog the entry (if any) corresponding to the
+	 * specified id.
+	 *
+	 * @param id
+	 */
+	private static void showID(String id) {
+		getInstance();
+		try {
+			URL result = getURLFromID(id);
+			if (result == null)
+				result = getURLFromID("home");
+			getDesktop().browse(result.toURI());
+		} catch (BadIDException | IOException | URISyntaxException e) {
+			logger.error(e);
+		}
+	}
+
+	/**
+	 * Show the most suitable help for the specified component.
+	 *
+	 * @param c
+	 */
+	public static void showHelp(Component c) {
+		showID(getHelpID(c));
+	}
+
+	/**
+	 * Display the default home page help.
+	 *
+	 * @param e
+	 */
+	public static void displayDefaultHelp(AWTEvent e) {
+		showID("home");
+	}
+
+	public static void displayFieldLevelHelp(ActionEvent e) {
+		//
+	}
+
+	private static final String HELP_KEY = "F1";
+
+	/**
+	 * Associated the specified action with key presses in the specified
+	 * component.
+	 * 
+	 * @param component
+	 * @param theAction
+	 */
+	public static void setKeyCatcher(final JComponent component,
+			final AbstractAction theAction) {
+		InputMap oldInputMap = component
+				.getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+		InputMap newInputMap = new InputMap();
+		newInputMap.setParent(oldInputMap);
+		newInputMap.put(getKeyStroke(HELP_KEY), "doSomething");
+		component.setInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, newInputMap);
+		ActionMap oldActionMap = component.getActionMap();
+		ActionMap newActionMap = new ActionMap();
+		newActionMap.setParent(oldActionMap);
+		newActionMap.put("doSomething", theAction);
+		component.setActionMap(newActionMap);
+	}
+
+	/**
+	 * Set up a key-press catcher for the specified component such that when F1
+	 * is pressed it should help for the component where the cursor is.
+	 *
+	 * @param rootpanecontainer
+	 */
+	public static void setKeyCatcher(final RootPaneContainer rootpanecontainer) {
+		@SuppressWarnings("serial")
+		AbstractAction theAction = new AbstractAction() {
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				Component component = (Component) rootpanecontainer;
+				Container container = (Container) rootpanecontainer;
+				logger.info("frame action F1 pressed with source "
+						+ evt.getSource().getClass().getName());
+				Point mousePosition = getPointerInfo().getLocation();
+				Point framePosition = component.getLocation();
+				Point relativePosition = (Point) mousePosition.clone();
+				relativePosition.translate(-framePosition.x, -framePosition.y);
+				Component c = container.findComponentAt(relativePosition);
+				if (c != null)
+					logger.info("F1 pressed in a " + c.getClass().getName());
+				showHelpWithinContainer(rootpanecontainer, c);
+			}
+		};
+
+		JRootPane pane = rootpanecontainer.getRootPane();
+		setKeyCatcher(pane, theAction);
+	}
+
+	/**
+	 * Show the help most associated with the specific component within the container.
+	 *
+	 * @param root
+	 * @param c
+	 */
+	static void showHelpWithinContainer(RootPaneContainer root, Component c) {
+		getInstance();
+		showHelp(c);
+	}
+
+	/**
+	 * Register a component with the {@link HelpCollator} under the specified
+	 * id.
+	 * 
+	 * @param component
+	 * @param id
+	 */
+	public static void registerComponent(Component component, final String id) {
+		HelpCollator.registerComponent(component, id);
+	}
+
+	/**
+	 * Register a component with the {@link HelpCollator}.
+	 *
+	 * @param component
+	 * @param parent
+	 * @param suffix
+	 */
+	public static void registerComponent(Component component, Object parent,
+			String suffix) {
+		HelpCollator.registerComponent(component, parent, suffix);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/NonBlockedHelpEnabledDialog.java
----------------------------------------------------------------------
diff --git a/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/NonBlockedHelpEnabledDialog.java b/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/NonBlockedHelpEnabledDialog.java
new file mode 100644
index 0000000..67e6bc5
--- /dev/null
+++ b/taverna-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/NonBlockedHelpEnabledDialog.java
@@ -0,0 +1,40 @@
+/**
+ * 
+ */
+package net.sf.taverna.t2.workbench.helper;
+
+import static java.awt.Dialog.ModalExclusionType.APPLICATION_EXCLUDE;
+
+import java.awt.Dialog;
+import java.awt.Frame;
+import java.awt.HeadlessException;
+
+/**
+ * @author alanrw
+ */
+public class NonBlockedHelpEnabledDialog extends HelpEnabledDialog {
+	private static final long serialVersionUID = -2455471377333940417L;
+
+	public NonBlockedHelpEnabledDialog(Dialog owner, String title,
+			boolean modal, String id) throws HeadlessException {
+		super(owner, title, modal, id);
+		this.setModalExclusionType(APPLICATION_EXCLUDE);
+	}
+
+	public NonBlockedHelpEnabledDialog(Frame owner, String title,
+			boolean modal, String id) throws HeadlessException {
+		super(owner, title, modal, id);
+		this.setModalExclusionType(APPLICATION_EXCLUDE);
+	}
+
+	public NonBlockedHelpEnabledDialog(Frame parent, String title, boolean modal) {
+		super(parent, title, modal, null);
+		this.setModalExclusionType(APPLICATION_EXCLUDE);
+	}
+
+	public NonBlockedHelpEnabledDialog(Dialog parent, String title,
+			boolean modal) {
+		super(parent, title, modal, null);
+		this.setModalExclusionType(APPLICATION_EXCLUDE);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/52fd79dd/taverna-httpproxy-config/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-httpproxy-config/pom.xml b/taverna-httpproxy-config/pom.xml
new file mode 100644
index 0000000..9513f1c
--- /dev/null
+++ b/taverna-httpproxy-config/pom.xml
@@ -0,0 +1,46 @@
+<?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>httpproxy-config</artifactId>
+	<packaging>bundle</packaging>
+	<name>HTTP Proxy configuration</name>
+	<dependencies>
+		<dependency>
+			<groupId>net.sf.taverna.t2.lang</groupId>
+			<artifactId>ui</artifactId>
+			<version>${t2.lang.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>uk.org.taverna.configuration</groupId>
+			<artifactId>taverna-configuration-api</artifactId>
+			<version>${taverna.configuration.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>helper-api</artifactId>
+			<version>${project.parent.version}</version>
+		</dependency>
+ 	</dependencies>
+</project>