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/06 23:34:39 UTC

[43/50] [abbrv] incubator-taverna-workbench git commit: taverna-*

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-io/src/main/java/net/sf/taverna/t2/lang/io/StreamCopier.java
----------------------------------------------------------------------
diff --git a/taverna-io/src/main/java/net/sf/taverna/t2/lang/io/StreamCopier.java b/taverna-io/src/main/java/net/sf/taverna/t2/lang/io/StreamCopier.java
new file mode 100644
index 0000000..b0d600d
--- /dev/null
+++ b/taverna-io/src/main/java/net/sf/taverna/t2/lang/io/StreamCopier.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.lang.io;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Copies an InputStream to an OutputStream.
+ * 
+ * @author Tom Oinn
+ */
+public class StreamCopier extends Thread {
+
+	private static Logger logger = Logger
+	.getLogger(StreamCopier.class);
+
+	InputStream is;
+
+	OutputStream os;
+
+	/**
+	 * Create a new StreamCopier which will, when started, copy the specified
+	 * InputStream to the specified OutputStream
+	 */
+	public StreamCopier(InputStream is, OutputStream os) {
+		super("StreamCopier");
+		this.is = is;
+		this.os = os;
+	}
+
+	/**
+	 * Start copying the stream, exits when the InputStream runs out of data
+	 */
+	public void run() {
+		try {
+			byte[] buffer = new byte[1024];
+			int bytesRead;
+			while ((bytesRead = is.read(buffer)) != -1) {
+				os.write(buffer, 0, bytesRead);
+			}
+			os.flush();
+			os.close();
+		} catch (Exception ex) {
+			logger.error("Could not copy stream", ex);
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-io/src/main/java/net/sf/taverna/t2/lang/io/StreamDevourer.java
----------------------------------------------------------------------
diff --git a/taverna-io/src/main/java/net/sf/taverna/t2/lang/io/StreamDevourer.java b/taverna-io/src/main/java/net/sf/taverna/t2/lang/io/StreamDevourer.java
new file mode 100644
index 0000000..8495e27
--- /dev/null
+++ b/taverna-io/src/main/java/net/sf/taverna/t2/lang/io/StreamDevourer.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.lang.io;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Devours an input stream and allows the contents to be read as a String once
+ * the stream has completed.
+ * 
+ * @author Tom Oinn
+ * @author Alan R Williams
+ */
+public class StreamDevourer extends Thread {
+	
+	private static Logger logger = Logger.getLogger(StreamDevourer.class);
+
+	private static byte[] newLine = System.getProperty("line.separator").getBytes();
+
+	BufferedReader br;
+
+	ByteArrayOutputStream output;
+
+	/**
+	 * Returns the current value of the internal ByteArrayOutputStream
+	 */
+	@Override
+	public String toString() {
+		return output.toString();
+	}
+
+	/**
+	 * Waits for the stream to close then returns the String representation of
+	 * its contents (this is equivalent to doing a join then calling toString)
+	 */
+	public String blockOnOutput() {
+		try {
+			this.join();
+			return output.toString();
+		} catch (InterruptedException ie) {
+			logger.error("Interrupted", ie);
+			interrupt();
+			return "";
+		}
+	}
+
+	/**
+	 * Create the StreamDevourer and point it at an InputStream to consume
+	 */
+	public StreamDevourer(InputStream is) {
+		super("StreamDevourer");
+		this.br = new BufferedReader(new InputStreamReader(is));
+		this.output = new ByteArrayOutputStream();
+	}
+
+	/**
+	 * When started this Thread will copy all data from the InputStream into a
+	 * ByteArrayOutputStream via a BufferedReader. Because of the use of the
+	 * BufferedReader this is only really appropriate for streams of textual
+	 * data
+	 */
+	@Override
+	public void run() {
+		try {
+			String line = null;
+			while ((line = br.readLine()) != null) {
+				// && line.endsWith("</svg>") == false) {
+				if (line.endsWith("\\") && !line.endsWith("\\\\")) {
+					line = line.substring(0, line.length() - 1);
+					output.write(line.getBytes());
+				} else {
+					output.write(line.getBytes());
+					output.write(newLine);
+				}
+			}
+			br.close();
+		} catch (IOException ioe) {
+			logger.error(ioe);
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-partition/pom.xml b/taverna-partition/pom.xml
new file mode 100644
index 0000000..ba5088a
--- /dev/null
+++ b/taverna-partition/pom.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>net.sf.taverna.t2</groupId>
+		<artifactId>lang</artifactId>
+		<version>2.0.1-SNAPSHOT</version>
+	</parent>
+	<groupId>net.sf.taverna.t2.lang</groupId>
+	<artifactId>partition</artifactId>
+	<packaging>bundle</packaging>
+	<name>Partition</name>
+	<description>API for recursive subset partitioning</description>
+	<dependencies>
+		<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/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/HashSetModel.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/HashSetModel.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/HashSetModel.java
new file mode 100644
index 0000000..3a66eb0
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/HashSetModel.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.partition;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Implementation of SetModel based on a HashSet
+ * 
+ * @author Tom Oinn
+ */
+public class HashSetModel<ItemType> extends HashSet<ItemType> implements
+		SetModel<ItemType> {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 5763277571663880941L;
+	// Listeners for set change events
+	private List<SetModelChangeListener<ItemType>> changeListeners;
+
+	/**
+	 * Default constructor, creates a set model based on a HashSet
+	 */
+	public HashSetModel() {
+		super();
+		changeListeners = new ArrayList<SetModelChangeListener<ItemType>>();
+	}
+
+	/**
+	 * Implements SetModel
+	 */
+	public synchronized void addSetModelChangeListener(
+			SetModelChangeListener<ItemType> listener) {
+		changeListeners.add(listener);
+	}
+
+	/**
+	 * Implements SetModel
+	 */
+	public synchronized void removeSetModelChangeListener(
+			SetModelChangeListener<ItemType> listener) {
+		changeListeners.remove(listener);
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public synchronized void clear() {
+		notifyRemoval((Set<Object>) this);
+		super.clear();
+	}
+
+	@Override
+	public synchronized boolean add(ItemType item) {
+		if (super.add(item)) {
+			notifyAddition(Collections.singleton(item));
+			return true;
+		}
+		return false;
+	}
+
+	@Override
+	public synchronized boolean remove(Object item) {
+		if (super.remove(item)) {
+			notifyRemoval(Collections.singleton(item));
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Push addition notification to listeners
+	 * 
+	 * @param itemsAdded
+	 */
+	private synchronized void notifyAddition(Set<ItemType> itemsAdded) {
+		for (SetModelChangeListener<ItemType> listener : new ArrayList<SetModelChangeListener<ItemType>>(
+				changeListeners)) {
+			listener.itemsWereAdded(itemsAdded);
+		}
+	}
+
+	/**
+	 * Push removal notification to listeners
+	 * 
+	 * @param itemsRemoved
+	 */
+	private synchronized void notifyRemoval(Set<Object> itemsRemoved) {
+		for (SetModelChangeListener<ItemType> listener : new ArrayList<SetModelChangeListener<ItemType>>(
+				changeListeners)) {
+			listener.itemsWereRemoved(itemsRemoved);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/Partition.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/Partition.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/Partition.java
new file mode 100644
index 0000000..e1e5819
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/Partition.java
@@ -0,0 +1,441 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.partition;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.swing.event.TreeModelEvent;
+import javax.swing.tree.TreePath;
+
+/**
+ * A partition represents a set of items which can be exclusively classified
+ * into one or more distinct subsets along with the algorithm to perform this
+ * subset operation.
+ * 
+ * @author Tom Oinn
+ * 
+ * @param <ItemType>
+ *            all items in the underlying set of which this is a subset are
+ *            instances of this type or can be cast to it according to
+ *            conventional java language rules.
+ * @param <PartitionValueType>
+ *            the partition value type used by the parent partition algorithm to
+ *            create this partition object. As an example, if this partition
+ *            object represented all those entries with a particular host
+ *            institution this would be a String or possibly URL.
+ * @param <ChildPartitionValueType>
+ *            the partition value type used by this partition's partition
+ *            algorithm to create sub-partitions, it's used in the signature
+ *            here because ordering of children is done based on these values.
+ *            Any child partition will have a getPartitionValue return type
+ *            cast-able to this type.
+ */
+public class Partition<ItemType extends Comparable, PartitionValueType, ChildPartitionValueType> {
+
+	// A comparator operating over the value type of the child partitions and
+	// used to order them as created or to re-order on a change of this property
+	private Comparator<ChildPartitionValueType> childPartitionOrder = null;
+
+	// Back reference to the root partition of which this is a direct or
+	// indirect sub-partition. If this *is* the root partition this points to
+	// self
+	protected RootPartition<ItemType> root;
+
+	// A subset of the parent's member set containing items which have been
+	// allocated to this partition by the parent's partition algorithm. The
+	// partition is specified by the partitionValue field.
+	private List<ItemType> members;
+
+	// The parent partition of which this is a subset
+	private Partition<ItemType, ?, PartitionValueType> parent;
+
+	// Specification of the partition in terms of the parent's partitioning
+	// algorithm
+	private PartitionValueType partitionValue;
+
+	// List of partitioning algorithms to be applied to this subset to create
+	// further partitions, the algorithm at index 0 is the one used for this
+	// partition, all others are passed in to the constructors for
+	// sub-partitions
+	protected List<PartitionAlgorithm<?>> partitionAlgorithms;
+
+	// An initially empty list of sub-partitions created by the head element of
+	// the partition algorithm list
+	protected List<Partition<ItemType, ChildPartitionValueType, ?>> children;
+
+	// Path from this node back to the root, initialised on first access and
+	// cached
+	private List<Partition<ItemType, ?, ?>> partitionPath = null;
+
+	// For leaf partitions this is equal to the number of items in the member
+	// set, for all other partitions it is the sum of the item count of all
+	// child partitions
+	protected int itemCount = 0;
+
+	/**
+	 * Construct a new Partition, this is used by the RootPartition and by this
+	 * class to construct the recursively sub-divided partition structure based
+	 * on the rules encapsulated by the partition algorithm list.
+	 * 
+	 * @param parent
+	 *            parent partition of which this is a subset
+	 * @param pa
+	 *            partition algorithm list, with the algorithm used to create
+	 *            child partitions of this one at position 0, if this list is
+	 *            empty this is a leaf partition.
+	 * @param root
+	 *            reference to the RootPartition acting as the externally
+	 *            visible front-end to this structure
+	 * @param pv
+	 *            the value which the parent's partition algorithm has assigned
+	 *            to this partition. This must be interpreted in the context of
+	 *            the parent's first partition algorithm for display and other
+	 *            purposes
+	 */
+	protected Partition(Partition<ItemType, ?, PartitionValueType> parent,
+			List<PartitionAlgorithm<?>> pa, RootPartition<ItemType> root,
+			PartitionValueType pv) {
+		this.root = root;
+		this.members = new ArrayList<ItemType>();
+		this.parent = parent;
+		this.partitionValue = pv;
+		this.partitionAlgorithms = pa;
+		this.children = new ArrayList<Partition<ItemType, ChildPartitionValueType, ?>>();
+	}
+
+	/**
+	 * Return the number of items below this node in the partition tree; in the
+	 * case of leaf partitions this is the number of items in the member set,
+	 * for non-leaf partitions it is the sum of the item count for all immediate
+	 * child partitions.
+	 */
+	public int getItemCount() {
+		return this.itemCount;
+	}
+
+	/**
+	 * Sub-partitions of this partition are ordered based on a comparator over
+	 * the child partition value type.
+	 * 
+	 * @return a comparator over child partition value types, or null if no
+	 *         comparator has been specified (by default this returns null)
+	 */
+	public Comparator<ChildPartitionValueType> getChildPartitionOrder() {
+		return this.childPartitionOrder;
+	}
+
+	/**
+	 * Set a comparator for child partition ordering - if the supplied
+	 * comparator is different to the current one this will also trigger a
+	 * re-order of all child partitions and corresponding events in the root
+	 * partition's tree view. In the current implementation this is the very
+	 * broad 'tree structure changed' event for this node in the tree view.
+	 * 
+	 * @param order
+	 *            a new comparator to order child partitions
+	 */
+	public void setChildPartitionOrder(Comparator<ChildPartitionValueType> order) {
+		if (!order.equals(childPartitionOrder)) {
+			childPartitionOrder = order;
+			sortChildPartitions();
+		}
+	}
+
+	/**
+	 * Return the parent partition of which this is a sub-partition, or null if
+	 * this is the root partition.
+	 */
+	public Partition<ItemType, ?, PartitionValueType> getParent() {
+		return this.parent;
+	}
+
+	/**
+	 * The parent partition created this partition based on a particular value
+	 * of a property of the members of the sub-partition. This returns that
+	 * value, and is the result returned from the parent partition's first
+	 * partition algorithm when run on all members of this partition or its
+	 * direct or indirect sub-partitions.
+	 */
+	public PartitionValueType getPartitionValue() {
+		return this.partitionValue;
+	}
+
+	@Override
+	public String toString() {
+		if (getParent() != null) {
+			// query type
+			String string = this.getParent().getPartitionAlgorithms().get(0)
+					.toString();
+			// result of query
+			String string2 = this.partitionValue.toString();
+			return string2 + " (" + getItemCount() + ")";
+		} else {
+			// This is for a root partition, loop through its children to return
+			// the correct number when running a new query
+			int items = 0;
+			for (Partition child : children) {
+				items = items + child.getItemCount();
+			}
+			String queryType = getPartitionAlgorithms().get(0).toString();
+			// return "Activities which match query = " + getItemCount();
+			return "Available activities (" + items + ")";
+			//+ ", query by "
+				//	+ queryType;
+		}
+	}
+
+	/**
+	 * Return a list of Partition objects from the root (at index 0) to this
+	 * node at the final position in the list. Computes the first time then
+	 * caches, as it should be impossible for this to be modified without
+	 * recreation of the entire structure from scratch.
+	 */
+	public synchronized List<Partition<ItemType, ?, ?>> getPartitionPath() {
+		if (partitionPath == null) {
+			List<Partition<ItemType, ?, ?>> al = new ArrayList<Partition<ItemType, ?, ?>>();
+			Partition<ItemType, ?, ?> activePartition = this;
+			al.add(activePartition);
+			while (activePartition.getParent() != null) {
+				al.add(0, activePartition.getParent());
+				activePartition = activePartition.getParent();
+			}
+			partitionPath = al;
+		}
+		return partitionPath;
+	}
+
+	/**
+	 * If this is a leaf partition, defined as one with an empty list of
+	 * partition algorithms, then this method returns the set of all items which
+	 * have been classified as belonging to this leaf partition. For non-leaf
+	 * partitions it will return an empty set.
+	 */
+	public final List<ItemType> getMembers() {
+		return Collections.unmodifiableList(this.members);
+	}
+
+	/**
+	 * The list of partition algorithms applicable to this node (at index 0) and
+	 * subsequent downstream sub-partitions of it. If this is empty then the
+	 * partition is a leaf partition.
+	 */
+	public final List<PartitionAlgorithm<?>> getPartitionAlgorithms() {
+		return Collections.unmodifiableList(partitionAlgorithms);
+	}
+
+	/**
+	 * Sub-partitions of this partition defined by the partition algorithm at
+	 * index 0 of the list. If this is a leaf partition this will always be
+	 * empty.
+	 */
+	public final List<Partition<ItemType, ChildPartitionValueType, ?>> getChildren() {
+		return Collections.unmodifiableList(children);
+	}
+
+	/**
+	 * Inject an item into this partition, if there are partition algorithms in
+	 * the partition algorithm list (i.e. this is not a leaf) this will
+	 * recursively call the same method on child partitions or create new
+	 * partitions where there are none that match the value from the partition
+	 * algorithm. If this is a leaf partition the item is added to the member
+	 * set. The list is sorted when adding an item and it is inserted in the
+	 * appropriate place
+	 * 
+	 * @param item
+	 *            the item to add to the partition structure.
+	 */
+	@SuppressWarnings("unchecked")
+	protected synchronized void addItem(ItemType item) {
+		if (partitionAlgorithms.isEmpty()) {
+			// itemCount = 0;
+			// Allocate directly to member set, no further partitioning
+			members.add(item);
+			Collections.sort(members);
+			int indexOf = members.indexOf(item);
+			// root.treeNodesInserted(new TreeModelEvent(this, getTreePath(),
+			// new int[] { members.size() - 1 }, new Object[] { item }));
+			root.treeNodesInserted(new TreeModelEvent(this, getTreePath(),
+					new int[] { indexOf }, new Object[] { item }));
+			// Increment item count for all partitions in the partition path
+			for (Partition<ItemType, ?, ?> p : getPartitionPath()) {
+				synchronized (p) {
+					p.itemCount++;
+					root.treeNodesChanged(new TreeModelEvent(this, p
+							.getTreePath()));
+				}
+			}
+
+			// Cache the storage of this item to this partition in the root
+			// partition for more efficient removal if required (saves having to
+			// search the entire partition tree, although that wouldn't be too
+			// painful if it was required this is faster at the cost of a few
+			// bytes of memory)
+			root.itemStoredAt(item, this);
+			// TODO - when the tree model covers items on the leaf nodes we'll
+			// want to message it here as well.
+		} else {
+			PartitionAlgorithm<ChildPartitionValueType> pa;
+			pa = (PartitionAlgorithm<ChildPartitionValueType>) partitionAlgorithms
+					.get(0);
+			ChildPartitionValueType pvalue = pa.allocate(item, root
+					.getPropertyExtractorRegistry());
+			// FIXME not sure how to do this since you seem to have to add the
+			// items to the partition if you want to then search them again,
+			// maybe you need a non-showing partition or something?
+			// //if it is a failed search then don't bother adding to the
+			// partition
+			// if (pvalue.toString().equalsIgnoreCase("No match")) {
+			// return;
+			// }
+			// See if there's a partition with this value already in the child
+			// partition list
+			for (Partition<ItemType, ChildPartitionValueType, ?> potentialChild : children) {
+				if (potentialChild.getPartitionValue().equals(pvalue)) {
+					potentialChild.addItem(item);
+					return;
+				}
+			}
+			// If not we have to create a new sub-partition
+			List<PartitionAlgorithm<?>> tail = new ArrayList<PartitionAlgorithm<?>>();
+			for (int i = 1; i < partitionAlgorithms.size(); i++) {
+				tail.add(partitionAlgorithms.get(i));
+			}
+			Partition<ItemType, ChildPartitionValueType, ?> newPartition = new Partition(
+					this, tail, this.root, pvalue);
+			// Insert the new partition in the correct place according to the
+			// comparator currently installed, or at the end if none exists or
+			// the list is empty
+			if (childPartitionOrder == null || children.isEmpty()) {
+				children.add(newPartition);
+				root.treeNodesInserted(new TreeModelEvent(this, getTreePath(),
+						new int[] { children.indexOf(newPartition) },
+						new Object[] { newPartition }));
+			} else {
+				boolean foundIndex = false;
+				for (int i = 0; i < children.size(); i++) {
+					ChildPartitionValueType existingPartitionValue = children
+							.get(i).getPartitionValue();
+					if (childPartitionOrder.compare(pvalue,
+							existingPartitionValue) < 0) {
+						children.add(i, newPartition);
+						root.treeNodesInserted(new TreeModelEvent(this,
+								getTreePath(), new int[] { i },
+								new Object[] { newPartition }));
+						if (i != 0) {
+							root.treeStructureChanged(new TreeModelEvent(this,
+									getTreePath()));
+						}
+						foundIndex = true;
+						break;
+					}
+				}
+				if (!foundIndex) {
+					// Fallen off the end of the array without finding something
+					// with greater index than the new partition so we add it at
+					// the
+					// end (by definition this is the largest value according to
+					// the
+					// comparator)
+					children.add(newPartition);
+					root.treeNodesInserted(new TreeModelEvent(this,
+							getTreePath(), new int[] { children
+									.indexOf(newPartition) },
+							new Object[] { newPartition }));
+				}
+			}
+			// Add the item to the new partition to trigger creation of any
+			// sub-partitions required
+			newPartition.addItem(item);
+		}
+	}
+
+	/**
+	 * Remove an item from the member set
+	 * 
+	 * @param item
+	 *            the item to remove
+	 */
+	protected void removeMember(ItemType item) {
+		this.members.remove(item);
+	}
+
+	/**
+	 * Re-order the child partitions based on the comparator, if no comparator
+	 * has been defined this method does nothing. Tree structure changed
+	 * messages are fired from this node in the tree view if the comparator is
+	 * defined even if no nodes have been changed (lazy but not too much of an
+	 * issue I suspect)
+	 */
+	protected synchronized final void sortChildPartitions() {
+		if (this.childPartitionOrder == null) {
+			// Can't do anything unless the comparator is set appropriately
+			return;
+		}
+		Comparator<Partition<ItemType, ChildPartitionValueType, ?>> comparator = new Comparator<Partition<ItemType, ChildPartitionValueType, ?>>() {
+			public int compare(
+					Partition<ItemType, ChildPartitionValueType, ?> o1,
+					Partition<ItemType, ChildPartitionValueType, ?> o2) {
+				// FIXME is this really safe to do? It's fairly specific to our
+				// case. Doesn't seem very generic
+				if (o1.getPartitionValue().toString().equalsIgnoreCase(
+						"no value")) {
+					// No value so put it to the end
+					return 1;
+				}
+				return childPartitionOrder.compare(o1.getPartitionValue(), o2
+						.getPartitionValue());
+			}
+		};
+		Collections.<Partition<ItemType, ChildPartitionValueType, ?>> sort(
+				children, comparator);
+		// Message the root that the node structure under this node has changed
+		// (this is a bit lazy and we could almost certainly be more clever here
+		// as the nodes have been removed and added to re-order them)
+		root.treeStructureChanged(new TreeModelEvent(this, getTreePath()));
+	}
+
+	/**
+	 * Return a TreePath object with this node as the final entry in the path
+	 */
+	protected final synchronized TreePath getTreePath() {
+		return new TreePath(getPartitionPath().toArray());
+	}
+
+	// public void sortItems() {
+	// System.out.println("sorting the items");
+	// synchronized (members) {
+	// List<ItemType> oldOrder = new ArrayList<ItemType>(members);
+	// Collections.sort(oldOrder);
+	//
+	// for (ItemType item : oldOrder) {
+	// removeMember(item);
+	// }
+	// for (ItemType item : oldOrder) {
+	// addItem(item);
+	// }
+	// }
+	//
+	// }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PartitionAlgorithm.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PartitionAlgorithm.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PartitionAlgorithm.java
new file mode 100644
index 0000000..b7b80f6
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PartitionAlgorithm.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.partition;
+
+/**
+ * Interface for classes which can partition a set of objects into subsets according
+ * to some embedded partitioning rule
+ * 
+ * @author Tom Oinn
+ * @author Stuart Owen
+ * 
+ * @param ValueType
+ *            the java type of values used to represent the distinct partitions
+ *            created by this algorithm, in many cases these will be primitive
+ *            java types such as String but they could represent ranges of
+ *            values in the case of binning of continuous quantities etc.
+ */
+public interface PartitionAlgorithm<ValueType> {
+
+	/**
+	 * Given an object to classify return the value of the partition into which
+	 * the object falls.
+	 * 
+	 * @param newItem
+	 * @return
+	 */
+	ValueType allocate(Object newItem, PropertyExtractorRegistry reg);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PartitionAlgorithmSetSPI.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PartitionAlgorithmSetSPI.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PartitionAlgorithmSetSPI.java
new file mode 100644
index 0000000..330a11a
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PartitionAlgorithmSetSPI.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.partition;
+
+import java.util.Set;
+
+/**
+ * An SPI interface that provides access to a Set of partition algorithms.
+ * 
+ * @author Stuart Owen
+ *
+ */
+public interface PartitionAlgorithmSetSPI {
+	/**
+	 * @return a Set of PartitionAlgorithms
+	 */
+	public Set<PartitionAlgorithm<?>> getPartitionAlgorithms();
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PropertyExtractorRegistry.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PropertyExtractorRegistry.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PropertyExtractorRegistry.java
new file mode 100644
index 0000000..229aa87
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PropertyExtractorRegistry.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.partition;
+
+import java.util.Map;
+
+/**
+ * Convenience to allow caching of property extractors. Implementations should
+ * scan for available PropertyExtractorSPI implementations and use these to get
+ * the properties for each target, caching as applicable.
+ * 
+ * @author Tom Oinn
+ * 
+ */
+public interface PropertyExtractorRegistry {
+
+	public Map<String, Object> getAllPropertiesFor(Object target);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PropertyExtractorSPI.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PropertyExtractorSPI.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PropertyExtractorSPI.java
new file mode 100644
index 0000000..130f2b7
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/PropertyExtractorSPI.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.partition;
+
+import java.util.Map;
+
+/**
+ * SPI for classes which can extract or infer a set of named properties from a
+ * target object.
+ * 
+ * @author Tom Oinn
+ * 
+ */
+public interface PropertyExtractorSPI {
+
+	/**
+	 * Given a target object extract or infer the property map from it. If the
+	 * target is one which this plugin cannot act on then simply return an empty
+	 * map.
+	 * 
+	 * @param target
+	 * @return
+	 */
+	Map<String, Object> extractProperties(Object target);
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/Query.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/Query.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/Query.java
new file mode 100644
index 0000000..c8f3cb6
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/Query.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.partition;
+
+import java.util.Date;
+
+/**
+ * Defines a query which can be re-run and which presents a set view on its
+ * results. The Query is intended to represent both the old Taverna scavenger
+ * class (which were queries in all but name) and new integration with external
+ * search-able repositories in which case the term 'query' is a more literal
+ * description.
+ * 
+ * @author Tom Oinn
+ * 
+ * @param <ItemType>
+ *            the parameterised type of the result set of the query
+ */
+public interface Query<ItemType> extends SetModel<ItemType> {
+
+	/**
+	 * Run the query. The query has internal state from any previous runs
+	 * (including the initial empty state) and will notify all listeners from
+	 * the SetModel interface of any items that are present in the new query
+	 * result and not in the old state or vice versa. It also updates the query
+	 * time to be the current time.
+	 */
+	public void doQuery();
+
+	/**
+	 * Returns the time at which the query was last invoked, or null if the
+	 * query has not been invoked yet.
+	 * 
+	 * @return time of last call to doQuery or null if this hasn't happened yet.
+	 */
+	public Date getLastQueryTime();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/RootPartition.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/RootPartition.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/RootPartition.java
new file mode 100644
index 0000000..64edfd8
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/RootPartition.java
@@ -0,0 +1,394 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.partition;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.swing.SwingUtilities;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+/**
+ * Subclass of Partition acting as the public access point for the partition
+ * structure. Exposes a TreeModel for use with a UI.
+ * 
+ * @author Tom Oinn
+ * 
+ * @param <ItemType>
+ *            all items added to this partition must cast to this type
+ */
+public class RootPartition<ItemType extends Comparable> extends
+		Partition<ItemType, Object, Object> implements TreeModel {
+
+	// Used to track where we ended up putting items with the addOrUpdateItem
+	// method, this makes checking for duplicate items, reclassification and
+	// removal of partitions much faster at the expense of a few bytes of memory
+	private Map<ItemType, Partition<ItemType, ?, ?>> itemToLeafPartition;
+
+	private PropertyExtractorRegistry propertyExtractorRegistry;
+
+	private final SetModelChangeListener<ItemType> setChangeListener = new SetModelChangeListener<ItemType>() {
+
+		private List<Query<?>> queryList = new ArrayList<Query<?>>();
+		
+		public void itemsWereAdded(Set<ItemType> newItems) {
+			for (ItemType item : newItems) {
+				addOrUpdateItem(item);
+			}
+		}
+
+		@SuppressWarnings("unchecked")
+		public void itemsWereRemoved(Set<Object> itemsRemoved) {
+			for (Object item : itemsRemoved) {
+				try {
+					removeItem((ItemType) item);
+				} catch (ClassCastException cce) {
+					// Obviously wasn't the right type of item but that means it
+					// couldn't have been added in the first place so we can
+					// safely ignore this.
+				}
+			}
+		}
+
+		public void addQuery(Query<?> query) {
+			queryList.add(query);
+		}
+
+		public List<Query<?>> getQueries() {
+			return queryList;
+		}
+
+	};
+
+	/**
+	 * Build a new empty root partition with the specified list of partition
+	 * algorithm implementations used to recursively allocate new data items to
+	 * the sub-partitions below this one on addition
+	 * 
+	 * @param pa
+	 */
+	public RootPartition(List<PartitionAlgorithm<?>> pa,
+			PropertyExtractorRegistry per) {
+		super(null, pa, null, null);
+		this.root = this;
+		this.propertyExtractorRegistry = per;
+		this.itemToLeafPartition = new HashMap<ItemType, Partition<ItemType, ?, ?>>();
+	}
+
+	/**
+	 * The root partition comes with a convenience implementation of
+	 * SetModelChangeListener which can be used to attach it to a compliant
+	 * instance of SetModel (assuming the item types match). This allows the
+	 * SetModel to act as the backing data store for the partition - as Query
+	 * and the various subset / set union operators also implement this it
+	 * provides a relatively simple mechanism to link multiple sets of data to
+	 * this partition.
+	 */
+	public SetModelChangeListener<ItemType> getSetModelChangeListener() {
+		return this.setChangeListener;
+	}
+
+	/**
+	 * Alter the list of partition algorithms which drive the construction of
+	 * the partitions. Calling this effectively forces a complete rebuild of the
+	 * tree structure which is an expensive operation so be careful when you use
+	 * it.
+	 * 
+	 * @param pa
+	 *            a new list of PartitionAlgorithmSPI instances to use as the
+	 *            basis for the partition structure.
+	 */
+	public synchronized void setPartitionAlgorithmList(
+			List<PartitionAlgorithm<?>> pa) {
+		if (pa.equals(this.partitionAlgorithms)) {
+			// At the least this checks for reference equality, although I'm not
+			// sure it does much more than that. TODO - replace this with a
+			// smarter check to see whether the list has really changed, doing a
+			// full re-build is expensive.
+			return;
+		}
+		// First create a copy of the keyset containing all the items we've
+		// added to this point.
+		Set<ItemType> itemsToAdd = new HashSet<ItemType>(itemToLeafPartition
+				.keySet());
+		this.partitionAlgorithms = pa;
+		this.children.clear();
+		this.itemToLeafPartition.clear();
+		treeStructureChanged(new TreeModelEvent(this, getTreePath()));
+		for (ItemType item : itemsToAdd) {
+			addOrUpdateItem(item);
+		}
+		sortChildPartitions();
+	}
+
+	/**
+	 * Add a new item to the partition structure. If the item already exists
+	 * this is interpreted as a request to reclassify according to properties
+	 * which may have changed. This is not the same as a reclassification due to
+	 * modification of the partition algorithm list, and just refers to the
+	 * specified item. If the item exists already and its classification is
+	 * changed the model will be notified with a removal event from the previous
+	 * location and the item will be added as usual immediately afterwards.
+	 */
+	public synchronized void addOrUpdateItem(ItemType item) {
+		// First check whether the item is already stored
+		if (itemToLeafPartition.containsKey(item)) {
+			// request to reclassify item.
+			List<Partition<ItemType, ?, ?>> partitions = itemToLeafPartition
+					.get(item).getPartitionPath();
+			// partitions[i].getPartitionValue is the result of running
+			// getPartitionAlgorithms[i-1] on the item, we run through the array
+			// until either we hit the end (no reclassification) or the item
+			// classifies differently in which case we remove and re-add it.
+			for (int i = 1; i < partitions.size(); i++) {
+				PartitionAlgorithm<?> pa = getPartitionAlgorithms().get(
+						i - 1);
+				Object existingValue = partitions.get(i).getPartitionValue();
+				Object reclassifiedValue = pa.allocate(item,
+						getPropertyExtractorRegistry());
+				if (existingValue.equals(reclassifiedValue) == false) {
+					// Items classify differently, remove it
+					removeItem(item);
+					// ...and add it back again, forcing reclassification
+					super.addItem(item);
+					return;
+				}
+			}
+			// return as the item wasn't changed.
+			return;
+		} else {
+			// Value wasn't already in the map so we just add it as usual
+			super.addItem(item);
+		}
+
+	}
+
+	/**
+	 * Remove an item from the partition structure, if this leaves any
+	 * partitions with zero item count they are removed as well to keep things
+	 * tidy.
+	 * 
+	 * @param item
+	 *            the item to remove from the partition structure. If this isn't
+	 *            present in the structure this method does nothing along the
+	 *            lines of the collections API.
+	 */
+	public synchronized void removeItem(ItemType item) {
+		Partition<ItemType, ?, ?> partition = itemToLeafPartition.get(item);
+		if (partition != null) {
+			// Remove the item from the leaf partition
+			int previousIndex = partition.getMembers().indexOf(item);
+			TreePath pathToPartition = partition.getTreePath();
+			//partition.removeMember(item);
+			for (Partition<ItemType, ?, ?> parentPathElement : partition
+					.getPartitionPath()) {
+				// Notify path to root that the number of members
+				// has changed and it should update the renderers of
+				// any attached trees
+				treeNodesChanged(new TreeModelEvent(this, parentPathElement
+						.getTreePath()));
+			}
+			partition.removeMember(item);
+			treeNodesRemoved(new TreeModelEvent(this, pathToPartition,
+					new int[] { previousIndex }, new Object[] { item }));
+			// Traverse up the partition path and decrement the item count. If
+			// any item count becomes zero we mark this as a partition to
+			// remove, then at the end we remove the highest level one (so we
+			// only have to send a single delete event to the tree view)
+			for (Partition<ItemType, ?, ?> p : partition.getPartitionPath()) {
+				synchronized (p) {
+					p.itemCount--;
+					if (p.getItemCount() == 0 && p != this) {
+						// Can remove this node, all nodes after this will by
+						// definition have item count of zero. The exception is
+						// if this is the root node, in which case we just
+						// ignore it and move on to the next child. This avoids
+						// deleting the root, which is generally not a smart
+						// thing to do to a tree.
+						Partition<ItemType, ?, ?> parent = p.getParent();
+						int indexInParent = getIndexOfChild(parent, p);
+						parent.children.remove(indexInParent);
+						treeNodesRemoved(new TreeModelEvent(this, parent
+								.getTreePath(), new int[] { indexInParent },
+								new Object[] { p }));
+
+						break;
+					}
+				}
+			}
+			itemToLeafPartition.remove(item);
+		}
+		treeStructureChanged(new TreeModelEvent(this,getTreePath()));
+	}
+
+	/**
+	 * Called by a leaf Partition when it has stored an item in its member set,
+	 * used to keep track of where items have been stored to make removal more
+	 * efficient.
+	 */
+	void itemStoredAt(ItemType item, Partition<ItemType, ?, ?> partition) {
+		itemToLeafPartition.put(item, partition);
+	}
+
+	// ---------------------//
+	// TreeModel interfaces //
+	// ---------------------//
+	private List<TreeModelListener> treeListeners = new ArrayList<TreeModelListener>();
+
+	@SuppressWarnings("unchecked")
+	public synchronized Object getChild(Object parent, int index) {
+		if (parent instanceof Partition) {
+			Partition<ItemType, ?, ?> p = (Partition<ItemType, ?, ?>) parent;
+			if (p.getMembers().isEmpty() == false) {
+				if (index < 0 || index >= p.getMembers().size()) {
+					return null;
+				} else {
+					return p.getMembers().get(index);
+				}
+			} else {
+				if (index < 0 || index >= p.getChildren().size()) {
+					return null;
+				} else {
+					return p.getChildren().get(index);
+				}
+			}
+		}
+		return null;
+	}
+
+	@SuppressWarnings("unchecked")
+	public synchronized int getChildCount(Object parent) {
+		if (parent instanceof Partition) {
+			Partition<ItemType, ?, ?> p = (Partition<ItemType, ?, ?>) parent;
+			if (p.getMembers().isEmpty() == false) {
+				return p.getMembers().size();
+			}
+			return p.getChildren().size();
+		}
+		return 0;
+	}
+
+	@SuppressWarnings("unchecked")
+	public synchronized int getIndexOfChild(Object parent, Object child) {
+		if (parent != null && child != null && parent instanceof Partition
+				&& child instanceof Partition) {
+			Partition<ItemType, ?, ?> p = (Partition<ItemType, ?, ?>) parent;
+			Partition<ItemType, ?, ?> c = (Partition<ItemType, ?, ?>) child;
+			if (p.root == c.root && p.root == this) {
+				// Parent and child must both be members of this tree structure
+				return p.getChildren().indexOf(child);
+			}
+		} else if (parent != null && child != null
+				&& parent instanceof Partition) {
+			Partition<ItemType, ?, ?> p = (Partition<ItemType, ?, ?>) parent;
+			return p.getMembers().indexOf(child);
+		}
+		return -1;
+
+	}
+
+	public Object getRoot() {
+		// The root partition is also the root of the tree model
+		return this;
+	}
+
+	public boolean isLeaf(Object node) {
+		// No leaves at the moment as we're only considering partitions which
+		// are by definition not leaves (the items within the last partition are
+		// but at the moment we're not including them in the tree model)
+		return (!(node instanceof Partition));
+	}
+
+	public void removeTreeModelListener(TreeModelListener l) {
+		treeListeners.remove(l);
+	}
+
+	public void addTreeModelListener(TreeModelListener l) {
+		if (treeListeners.contains(l) == false) {
+			treeListeners.add(l);
+		}
+	}
+
+	public void valueForPathChanged(TreePath path, Object newValue) {
+		// Ignore this, the tree values are never changed by the user in this
+		// implementation so we don't have to do anything
+	}
+
+	// -------------------------------------------------------//
+	// Tree event forwarding helper methods used by Partition //
+	// -------------------------------------------------------//
+
+	void treeNodesChanged(final TreeModelEvent e) {
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				for (TreeModelListener listener : new ArrayList<TreeModelListener>(
+						treeListeners)) {
+					listener.treeNodesChanged(e);
+				}
+			}
+		});
+	}
+
+	void treeNodesInserted(final TreeModelEvent e) {
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				for (TreeModelListener listener : new ArrayList<TreeModelListener>(
+						treeListeners)) {
+					listener.treeNodesInserted(e);
+				}
+			}
+		});
+	}
+
+	void treeNodesRemoved(final TreeModelEvent e) {
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				for (TreeModelListener listener : new ArrayList<TreeModelListener>(
+						treeListeners)) {
+					listener.treeNodesRemoved(e);
+				}
+			}
+		});
+	}
+
+	void treeStructureChanged(final TreeModelEvent e) {
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				for (TreeModelListener listener : new ArrayList<TreeModelListener>(
+						treeListeners)) {
+					listener.treeStructureChanged(e);
+				}
+			}
+		});
+	}
+
+	public PropertyExtractorRegistry getPropertyExtractorRegistry() {
+		return this.propertyExtractorRegistry;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/SetModel.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/SetModel.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/SetModel.java
new file mode 100644
index 0000000..393d697
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/SetModel.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.partition;
+
+import java.util.Set;
+
+/**
+ * Extension of the java Set interface with the addition of change listener
+ * support. Intended to be plugged into the RootPartition class so the partition
+ * is synchronized with the set membership.
+ * 
+ * @author Tom Oinn
+ * 
+ * @param <ItemType>
+ *            the parameterised type of the set
+ */
+public interface SetModel<ItemType> extends Set<ItemType> {
+
+	/**
+	 * Add a listener to be notified of change events on the set's membership
+	 * 
+	 * @param listener
+	 */
+	public void addSetModelChangeListener(
+			SetModelChangeListener<ItemType> listener);
+
+	/**
+	 * Remove a previously registered change listener
+	 * 
+	 * @param listener
+	 */
+	public void removeSetModelChangeListener(
+			SetModelChangeListener<ItemType> listener);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/SetModelChangeListener.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/SetModelChangeListener.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/SetModelChangeListener.java
new file mode 100644
index 0000000..176eb7c
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/SetModelChangeListener.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.partition;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Handler for change events on a SetModel instance
+ * 
+ * @author Tom Oinn
+ * 
+ */
+public interface SetModelChangeListener<ItemType> {
+
+	public void itemsWereAdded(Set<ItemType> newItems);
+	
+	public void itemsWereRemoved(Set<Object> itemsRemoved);
+	
+	public List<Query<?>> getQueries();
+	
+	public void addQuery(Query<?> query);
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/algorithms/CustomPartitionAlgorithm.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/algorithms/CustomPartitionAlgorithm.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/algorithms/CustomPartitionAlgorithm.java
new file mode 100644
index 0000000..b6a3eea
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/algorithms/CustomPartitionAlgorithm.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.partition.algorithms;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.taverna.t2.partition.PartitionAlgorithm;
+import net.sf.taverna.t2.partition.PropertyExtractorRegistry;
+
+/**
+ * Takes a custom search term and checks against the properties eg
+ * "operation" of each of the available items. Adds the item to the activity
+ * palette if it matches
+ * 
+ * @author Ian Dunlop
+ * 
+ */
+public class CustomPartitionAlgorithm implements PartitionAlgorithm<Object> {
+
+	private String searchValue;
+	private List<String> properties;
+	private static String NO_SEARCH = "No match";
+	private static String MATCHING_ITEMS = "Activities with a matching property";
+
+	public String getSearchValue() {
+		return searchValue;
+	}
+
+	public void setSearchValue(String searchValue) {
+		this.searchValue = searchValue;
+	}
+
+	public CustomPartitionAlgorithm() {
+		properties = new ArrayList<String>();
+	}
+
+	public CustomPartitionAlgorithm(String searchValue) {
+		super();
+		this.searchValue = searchValue;
+		// this.propertyName = propertyName;
+		properties = new ArrayList<String>();
+	}
+
+	public void addProperty(String propertyValue) {
+		properties.add(propertyValue);
+	}
+
+	/**
+	 * Checks against the items property to see if it contains the search term.
+	 * Search each of the properties in {@link #properties} in turn
+	 */
+	public Object allocate(Object newItem, PropertyExtractorRegistry reg) {
+		for (String property : properties) {
+			Object propertyValue = reg.getAllPropertiesFor(newItem).get(
+					property);
+			String itemString = newItem.toString();
+			//search all the properties first
+			if (propertyValue != null) {
+				if (((String) propertyValue).contains(getSearchValue()
+						.toLowerCase())) {
+					return MATCHING_ITEMS;
+				}
+			}
+			//then the name of the item
+			if (itemString.toLowerCase().contains(
+					getSearchValue().toLowerCase())) {
+				return MATCHING_ITEMS;
+			}
+		}
+		return NO_SEARCH;
+
+	}
+
+	@Override
+	public String toString() {
+		return "search term=" + this.searchValue;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/algorithms/LiteralValuePartitionAlgorithm.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/algorithms/LiteralValuePartitionAlgorithm.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/algorithms/LiteralValuePartitionAlgorithm.java
new file mode 100644
index 0000000..b703d40
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/algorithms/LiteralValuePartitionAlgorithm.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.partition.algorithms;
+
+import net.sf.taverna.t2.partition.PartitionAlgorithm;
+import net.sf.taverna.t2.partition.PropertyExtractorRegistry;
+
+/**
+ * A naive partition algorithm that simply returns the property value it's been
+ * configured to use from the property getter.
+ * 
+ * @author Tom
+ * 
+ */
+public class LiteralValuePartitionAlgorithm implements
+		PartitionAlgorithm<Object> {
+
+	private String propertyName = null;
+	
+	private static String NO_PROPERTY = "No value";
+	
+	/**
+	 * Default constructor. The property name defaults to null, and needs setting using getPropertyName
+	 */
+	public LiteralValuePartitionAlgorithm() {
+		
+	}
+	
+	/**
+	 * Constructor that initialised the LiteralValuePartitionAlgorithm with a property name
+	 * 
+	 * @param propertyName
+	 */
+	public LiteralValuePartitionAlgorithm(String propertyName) {
+		super();
+		this.propertyName = propertyName;
+	}
+
+	public Object allocate(Object newItem, PropertyExtractorRegistry reg) {
+		if (propertyName == null) {
+			return NO_PROPERTY;
+		}
+		else {
+			Object propertyValue = reg.getAllPropertiesFor(newItem).get(propertyName);
+			if (propertyValue == null) {
+				return NO_PROPERTY;
+			}
+			else {
+				return propertyValue;
+			}
+		}
+	}
+
+	public String getPropertyName() {
+		return propertyName;
+	}
+
+	public void setPropertyName(String propertyName) {
+		this.propertyName = propertyName;
+	}
+	
+	
+	
+	/**
+	 * @return true if obj is a LiteralValuePartionAlgorithm and the property names match
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if (obj instanceof LiteralValuePartitionAlgorithm) {
+			LiteralValuePartitionAlgorithm alg = (LiteralValuePartitionAlgorithm)obj;
+			return getPropertyName().equals(alg.getPropertyName());
+		}
+		else {
+			return false;
+		}
+	}
+
+	@Override
+	public int hashCode() {
+		return getPropertyName().hashCode();
+	}
+
+	@Override
+	public String toString() {
+		return this.propertyName;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/PartitionAlgorithmListEditor.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/PartitionAlgorithmListEditor.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/PartitionAlgorithmListEditor.java
new file mode 100644
index 0000000..9c05c44
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/PartitionAlgorithmListEditor.java
@@ -0,0 +1,224 @@
+/*******************************************************************************
+ * Copyright (C) 2007 The University of Manchester   
+ * 
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ * 
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *    
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *    
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package net.sf.taverna.t2.partition.ui;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import net.sf.taverna.t2.partition.PartitionAlgorithm;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.GeneralPath;
+import java.util.List;
+
+public class PartitionAlgorithmListEditor extends JPanel {
+
+	// Serial version ID
+	private static final long serialVersionUID = 8206805090698009524L;
+
+	// Index of currently selected 'tab' or -1 if none selected
+	private int selectedIndex = 1;
+
+	// List of partition algorithm instances acting as the model for this
+	// component
+	private List<PartitionAlgorithm<?>> paList;
+
+	private int cornerSep = 8;
+	private int labelHorizontalPad = 10;
+	private int labelBottomPad = 2;
+	private int labelTopPad = 4;
+	private float selectedStrokeWidth = 3f;
+	
+	public List<PartitionAlgorithm<?>> getPartitionAlgorithmList() {
+		return null;
+	}
+
+	@Override
+	public Dimension getPreferredSize() {
+		if (paList.isEmpty()) {
+			return new Dimension(0, 16 + labelBottomPad + labelTopPad);
+		} else {
+			return new Dimension(0, (int) new Tab(getLabelForPA(paList.get(0)))
+					.getPreferredSize().getHeight());
+		}
+	}
+
+	public PartitionAlgorithmListEditor(
+			List<PartitionAlgorithm<?>> currentState) {
+		super();
+		this.paList = currentState;
+	}
+
+	protected JLabel getLabelForPA(PartitionAlgorithm<?> pa) {
+		return new JLabel("Test...");
+	}
+
+	protected Color getBorderColorForPA(PartitionAlgorithm<?> pa) {
+		return Color.black;
+	}
+
+	@Override
+	protected void paintComponent(Graphics g) {
+		Graphics2D g2d = (Graphics2D) g.create();
+		// Enable anti-aliasing for the curved lines
+		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+				RenderingHints.VALUE_ANTIALIAS_ON);
+
+		int selectedStartsAt = 0;
+		int selectedEndsAt = 0;
+		Color frameColor = null;
+		int cumulativeTranslation = 0;
+
+		super.paintComponent(g2d);
+
+		for (int i = 0; i < paList.size(); i++) {
+
+			Tab t = new Tab(getLabelForPA(paList.get(i)));
+			t.setBackground(new Color(150, 150, 255));
+			t.setSelected(i == selectedIndex);
+			int width = (int) (t.getPreferredSize()).getWidth();
+			t.setSize(new Dimension(width, getHeight()));
+			t.paint(g2d);
+
+			if (i < selectedIndex) {
+				selectedStartsAt += width;
+			} else if (i == selectedIndex) {
+				selectedEndsAt = selectedStartsAt + width;
+				frameColor = t.getBackground();
+			}
+			cumulativeTranslation += width;
+			g2d.translate(width, 0);
+		}
+			
+		
+		// Reset the transform
+		g2d.translate(-cumulativeTranslation, 0);
+		if (selectedIndex > 0) {
+			g2d.setStroke(new BasicStroke(selectedStrokeWidth, BasicStroke.CAP_BUTT,
+					BasicStroke.JOIN_MITER));
+			int height = (int)(getHeight() - selectedStrokeWidth/2);
+			// Render the selected index line...
+			if (frameColor != null) {
+				g2d.setPaint(frameColor.darker());
+			}
+			GeneralPath path = new GeneralPath();
+			path.moveTo(0, height);
+			path.lineTo(selectedStartsAt, height);
+			path.lineTo(selectedStartsAt, cornerSep);
+			path.curveTo(selectedStartsAt, cornerSep / 2, selectedStartsAt
+					+ cornerSep / 2, 0, selectedStartsAt + cornerSep, 0);
+			path.lineTo(selectedEndsAt - cornerSep, 0);
+			path.curveTo(selectedEndsAt - cornerSep / 2, 0, selectedEndsAt,
+					cornerSep / 2, selectedEndsAt, cornerSep);
+			path.lineTo(selectedEndsAt, height);
+			path.lineTo(getWidth(), height);
+
+			g2d.draw(path);
+		}
+		g2d.dispose();
+	}
+
+	@SuppressWarnings("serial")
+	// Renderer for a single tab in the partition algorithm list, used as a
+	// rubber stamp for a single tab in the tab list.
+	class Tab extends JComponent {
+
+		// Label to use to render tab contents
+		private JLabel label;
+
+		// If this is selected then we don't draw the stroke as it'll be drawn
+		// on later.
+		private boolean selected = false;
+
+		@Override
+		// Always false as we don't render the corners
+		public boolean isOpaque() {
+			return false;
+		}
+
+		public void setSelected(boolean b) {
+			this.selected = b;
+
+		}
+
+		@Override
+		public Dimension getPreferredSize() {
+			Dimension d = label.getPreferredSize();
+			return new Dimension((int) (d.getWidth()) + labelHorizontalPad * 2,
+					((int) d.getHeight()) + labelBottomPad + labelTopPad);
+		}
+
+		protected Tab(JLabel label) {
+			super();
+			this.label = label;
+		}
+
+		@Override
+		public void setBackground(Color colour) {
+			label.setBackground(colour);
+		}
+
+		@Override
+		public Color getBackground() {
+			return label.getBackground();
+		}
+
+		@Override
+		protected void paintComponent(Graphics g) {
+			Graphics2D g2d = (Graphics2D) g.create();
+
+			int width = getWidth();
+			int height = getHeight();
+
+			// Create a general path to draw the tab shape
+			g2d.setPaint(label.getBackground());
+			GeneralPath path = new GeneralPath();
+			path.moveTo(0, height);
+			path.lineTo(0, cornerSep);
+			path.curveTo(0, cornerSep / 2, cornerSep / 2, 0, cornerSep, 0);
+			path.lineTo(width - cornerSep, 0);
+			path.curveTo(width - cornerSep / 2, 0, width, cornerSep / 2, width,
+					cornerSep);
+			path.lineTo(width, height);
+			path.closePath();
+			g2d.fill(path);
+			if (!selected) {
+				g2d.setPaint(label.getBackground().darker());
+				g2d.draw(path);
+			}
+
+			label.setSize(width - labelHorizontalPad * 2, height
+					- (labelBottomPad + labelTopPad));
+			g2d.translate(labelHorizontalPad, labelTopPad);
+			label.paint(g2d);
+			g2d.translate(-labelHorizontalPad, -labelTopPad);
+
+			g2d.dispose();
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/TableTreeNodeColumn.java
----------------------------------------------------------------------
diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/TableTreeNodeColumn.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/TableTreeNodeColumn.java
new file mode 100644
index 0000000..c6b4b36
--- /dev/null
+++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/TableTreeNodeColumn.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.partition.ui;
+
+import java.awt.Color;
+import java.awt.Component;
+
+/**
+ * A column used in the tree part of the table tree node renderer
+ * 
+ * @author Tom Oinn
+ * 
+ */
+public interface TableTreeNodeColumn {
+
+	/**
+	 * Get a string to use as the header text
+	 * 
+	 * @return
+	 */
+	public String getShortName();
+
+	/**
+	 * Get a descriptive string for tooltips etc.
+	 * 
+	 * @return
+	 */
+	public String getDescription();
+
+	/**
+	 * Given a node value render the appropriate property for this column
+	 * 
+	 * @param value
+	 * @return
+	 */
+	public Component getCellRenderer(Object value);
+
+	/**
+	 * Return the width in pixels for this column
+	 * 
+	 * @return
+	 */
+	public int getColumnWidth();
+
+	/**
+	 * Get a header colour - the actual column colour will be a stripe of the
+	 * result of applying the lighter operator twice and once to this colour.
+	 */
+	public Color getColour();
+	
+}