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();
+       
+}

Reply via email to