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