[email protected] wrote:
>Revision: 10090 >Author: [email protected] >Date: Wed May 5 01:34:09 2010 >Log: Classes to support sorted tables >http://code.google.com/p/taverna/source/detail?r=10090 > >Added: > >/taverna/utils/net.sf.taverna.t2.lang/trunk/ui/src/main/java/net/sf/taverna/t2/lang/ui/TableMap.java > >/taverna/utils/net.sf.taverna.t2.lang/trunk/ui/src/main/java/net/sf/taverna/t2/lang/ui/TableSorter.java >Modified: > /taverna/utils/net.sf.taverna.t2.lang/trunk/ui/pom.xml > >======================================= >--- /dev/null >+++ >/taverna/utils/net.sf.taverna.t2.lang/trunk/ui/src/main/java/net/sf/taverna/t2/lang/ui/TableMap.java > >Wed May 5 01:34:09 2010 >@@ -0,0 +1,69 @@ >+/** >+ * >+ */ >+package net.sf.taverna.t2.lang.ui; >+ >+import javax.swing.table.AbstractTableModel; >+import javax.swing.table.TableModel; >+import javax.swing.event.TableModelListener; >+import javax.swing.event.TableModelEvent; >+ >+/** >+ * Copied from code found at >http://www.informit.com/guides/content.aspx?g=java&seqNum=57 >+ * >+ */ >+public class TableMap extends AbstractTableModel implements >TableModelListener { >+ protected TableModel model; >+ >+ public TableModel getModel() { >+ return model; >+ } >+ >+ public void setModel(TableModel model) { >+ this.model = model; >+ model.addTableModelListener(this); >+ } >+ >+ // By default, implement TableModel by forwarding all messages >+ // to the model. >+ >+ public Object getValueAt(int aRow, int aColumn) { >+ return model.getValueAt(aRow, aColumn); >+ } >+ >+ public void setValueAt(Object aValue, int aRow, int aColumn) { >+ model.setValueAt(aValue, aRow, aColumn); >+ } >+ >+ public int getRowCount() { >+ return (model == null) ? 0 : model.getRowCount(); >+ } >+ >+ public int getColumnCount() { >+ return (model == null) ? 0 : model.getColumnCount(); >+ } >+ >+ public String getColumnName(int aColumn) { >+ return model.getColumnName(aColumn); >+ } >+ >+ public Class getColumnClass(int aColumn) { >+ return model.getColumnClass(aColumn); >+ } >+ >+ public boolean isCellEditable(int row, int column) { >+ return model.isCellEditable(row, column); >+ } >+ >+ public int transposeRow(int row) { >+ return row; >+ } >+ >+ // >+ // Implementation of the TableModelListener interface, >+ // >+ // By default forward all events to all the listeners. >+ public void tableChanged(TableModelEvent e) { >+ fireTableChanged(e); >+ } >+} >======================================= >--- /dev/null >+++ >/taverna/utils/net.sf.taverna.t2.lang/trunk/ui/src/main/java/net/sf/taverna/t2/lang/ui/TableSorter.java > >Wed May 5 01:34:09 2010 >@@ -0,0 +1,315 @@ >+/** >+ * >+ */ >+package net.sf.taverna.t2.lang.ui; >+ >+/** >+ * Copied from code found at >http://www.informit.com/guides/content.aspx?g=java&seqNum=57 >+ */ >+ >+import java.util.Date; >+import java.util.Vector; >+ >+import javax.swing.table.TableModel; >+import javax.swing.event.TableModelEvent; >+ >+//Imports for picking up mouse events from the JTable. >+import java.awt.event.MouseAdapter; >+import java.awt.event.MouseEvent; >+import java.awt.event.InputEvent; >+import javax.swing.JTable; >+import javax.swing.table.JTableHeader; >+import javax.swing.table.TableColumnModel; >+ >+public class TableSorter extends TableMap { >+ int indexes[]; >+ Vector sortingColumns = new Vector(); >+ boolean ascending = true; >+ int compares; >+ >+ public TableSorter() { >+ indexes = new int[0]; // for consistency >+ } >+ >+ public TableSorter(TableModel model) { >+ setModel(model); >+ } >+ >+ public void setModel(TableModel model) { >+ super.setModel(model); >+ reallocateIndexes(); >+ } >+ >+ public int compareRowsByColumn(int row1, int row2, int column) { >+ Class type = model.getColumnClass(column); >+ TableModel data = model; >+ >+ // Check for nulls. >+ >+ Object o1 = data.getValueAt(row1, column); >+ Object o2 = data.getValueAt(row2, column); >+ >+ // If both values are null, return 0. >+ if (o1 == null && o2 == null) { >+ return 0; >+ } else if (o1 == null) { // Define null less than everything. >+ return -1; >+ } else if (o2 == null) { >+ return 1; >+ } >+ >+ if (o1 instanceof Comparable) { >+ return ((Comparable) o1).compareTo(o2); >+ } >+ /* >+ * We copy all returned values from the getValue call in case an >+ * optimised model is reusing one object to return many values. >The >+ * Number subclasses in the JDK are immutable and so will not >be used in >+ * this way but other subclasses of Number might want to do >this to save >+ * space and avoid unnecessary heap allocation. >+ */ >+ >+ if (type.getSuperclass() == java.lang.Number.class) { >+ Number n1 = (Number) data.getValueAt(row1, column); >+ double d1 = n1.doubleValue(); >+ Number n2 = (Number) data.getValueAt(row2, column); >+ double d2 = n2.doubleValue(); >+ >+ if (d1 < d2) { >+ return -1; >+ } else if (d1 > d2) { >+ return 1; >+ } else { >+ return 0; >+ } >+ } else if (type == java.util.Date.class) { >+ Date d1 = (Date) data.getValueAt(row1, column); >+ long n1 = d1.getTime(); >+ Date d2 = (Date) data.getValueAt(row2, column); >+ long n2 = d2.getTime(); >+ >+ if (n1 < n2) { >+ return -1; >+ } else if (n1 > n2) { >+ return 1; >+ } else { >+ return 0; >+ } >+ } else if (type == String.class) { >+ String s1 = (String) data.getValueAt(row1, column); >+ String s2 = (String) data.getValueAt(row2, column); >+ int result = s1.compareTo(s2); >+ >+ if (result < 0) { >+ return -1; >+ } else if (result > 0) { >+ return 1; >+ } else { >+ return 0; >+ } >+ } else if (type == Boolean.class) { >+ Boolean bool1 = (Boolean) data.getValueAt(row1, column); >+ boolean b1 = bool1.booleanValue(); >+ Boolean bool2 = (Boolean) data.getValueAt(row2, column); >+ boolean b2 = bool2.booleanValue(); >+ >+ if (b1 == b2) { >+ return 0; >+ } else if (b1) { // Define false < true >+ return 1; >+ } else { >+ return -1; >+ } >+ } else { >+ Object v1 = data.getValueAt(row1, column); >+ String s1 = v1.toString(); >+ Object v2 = data.getValueAt(row2, column); >+ String s2 = v2.toString(); >+ int result = s1.compareTo(s2); >+ >+ if (result < 0) { >+ return -1; >+ } else if (result > 0) { >+ return 1; >+ } else { >+ return 0; >+ } >+ } >+ } >+ >+ public int compare(int row1, int row2) { >+ compares++; >+ for (int level = 0; level < sortingColumns.size(); level++) { >+ Integer column = (Integer) >sortingColumns.elementAt(level); >+ int result = compareRowsByColumn(row1, row2, >column.intValue()); >+ if (result != 0) { >+ return ascending ? result : -result; >+ } >+ } >+ return 0; >+ } >+ >+ public void reallocateIndexes() { >+ int rowCount = model.getRowCount(); >+ >+ // Set up a new array of indexes with the right number of >elements >+ // for the new data model. >+ indexes = new int[rowCount]; >+ >+ // Initialise with the identity mapping. >+ for (int row = 0; row < rowCount; row++) { >+ indexes[row] = row; >+ } >+ } >+ >+ public void tableChanged(TableModelEvent e) { >+ // System.out.println("Sorter: tableChanged"); >+ reallocateIndexes(); >+ >+ super.tableChanged(e); >+ } >+ >+ public void checkModel() { >+ if (indexes.length != model.getRowCount()) { >+ System.err.println("Sorter not informed of a change in >model."); >+ } >+ } >+ >+ public void sort(Object sender) { >+ checkModel(); >+ >+ compares = 0; >+ // n2sort(); >+ // qsort(0, indexes.length-1); >+ shuttlesort((int[]) indexes.clone(), indexes, 0, >indexes.length); >+ // System.out.println("Compares: "+compares); >+ } >+ >+ public void n2sort() { >+ for (int i = 0; i < getRowCount(); i++) { >+ for (int j = i + 1; j < getRowCount(); j++) { >+ if (compare(indexes[i], indexes[j]) == -1) { >+ swap(i, j); >+ } >+ } >+ } >+ } >+ >+ // This is a home-grown implementation which we have not had time >+ // to research - it may perform poorly in some circumstances. It >+ // requires twice the space of an in-place algorithm and makes >+ // NlogN assigments shuttling the values between the two >+ // arrays. The number of compares appears to vary between N-1 and >+ // NlogN depending on the initial order but the main reason for >+ // using it here is that, unlike qsort, it is stable. >+ public void shuttlesort(int from[], int to[], int low, int high) { >+ if (high - low < 2) { >+ return; >+ } >+ int middle = (low + high) / 2; >+ shuttlesort(to, from, low, middle); >+ shuttlesort(to, from, middle, high); >+ >+ int p = low; >+ int q = middle; >+ >+ /* >+ * This is an optional short-cut; at each recursive call, check >to see >+ * if the elements in this subset are already ordered. If so, >no further >+ * comparisons are needed; the sub-array can just be copied. >The array >+ * must be copied rather than assigned otherwise sister calls >in the >+ * recursion might get out of sinc. When the number of elements >is three >+ * they are partitioned so that the first set, [low, mid), has >one >+ * element and and the second, [mid, high), has two. We skip the >+ * optimisation when the number of elements is three or less as >the >+ * first compare in the normal merge will produce the same >sequence of >+ * steps. This optimisation seems to be worthwhile for >partially ordered >+ * lists but some analysis is needed to find out how the >performance >+ * drops to Nlog(N) as the initial order diminishes - it may >drop very >+ * quickly. >+ */ >+ >+ if (high - low >= 4 && compare(from[middle - 1], from[middle]) ><= 0) { >+ for (int i = low; i < high; i++) { >+ to[i] = from[i]; >+ } >+ return; >+ } >+ >+ // A normal merge. >+ >+ for (int i = low; i < high; i++) { >+ if (q >= high || (p < middle && compare(from[p], >from[q]) <= 0)) { >+ to[i] = from[p++]; >+ } else { >+ to[i] = from[q++]; >+ } >+ } >+ } >+ >+ public void swap(int i, int j) { >+ int tmp = indexes[i]; >+ indexes[i] = indexes[j]; >+ indexes[j] = tmp; >+ } >+ >+ // The mapping only affects the contents of the data rows. >+ // Pass all requests to these rows through the mapping array: "indexes". >+ >+ public Object getValueAt(int aRow, int aColumn) { >+ checkModel(); >+ return model.getValueAt(indexes[aRow], aColumn); >+ } >+ >+ public void setValueAt(Object aValue, int aRow, int aColumn) { >+ checkModel(); >+ model.setValueAt(aValue, indexes[aRow], aColumn); >+ } >+ >+ public void sortByColumn(int column) { >+ sortByColumn(column, true); >+ } >+ >+ public void sortByColumn(int column, boolean ascending) { >+ this.ascending = ascending; >+ sortingColumns.removeAllElements(); >+ sortingColumns.addElement(new Integer(column)); >+ sort(this); >+ super.tableChanged(new TableModelEvent(this)); >+ } >+ >+ // There is no-where else to put this. >+ // Add a mouse listener to the Table to trigger a table sort >+ // when a column heading is clicked in the JTable. >+ public void addMouseListenerToHeaderInTable(JTable table) { >+ final TableSorter sorter = this; >+ final JTable tableView = table; >+ tableView.setColumnSelectionAllowed(false); >+ MouseAdapter listMouseListener = new MouseAdapter() { >+ >+ private int lastClickedColumn = -1; >+ private boolean lastAscending = false; >+ public void mouseClicked(MouseEvent e) { >+ TableColumnModel columnModel = >tableView.getColumnModel(); >+ int viewColumn = >columnModel.getColumnIndexAtX(e.getX()); >+ int column = >tableView.convertColumnIndexToModel(viewColumn); >+ if (e.getClickCount() == 1 && column != -1) { >+ // System.out.println("Sorting ..."); >+ boolean ascendingColumn = true; >+ if (lastClickedColumn == column) { >+ ascendingColumn = >!lastAscending; >+ } >+ lastClickedColumn = column; >+ lastAscending = ascendingColumn; >+ sorter.sortByColumn(column, >ascendingColumn); >+ } >+ } >+ }; >+ JTableHeader th = tableView.getTableHeader(); >+ th.addMouseListener(listMouseListener); >+ } >+ >+ public int transposeRow(int row) { >+ return indexes[row]; >+ } >+} >======================================= >--- /taverna/utils/net.sf.taverna.t2.lang/trunk/ui/pom.xml Tue Jan 5 >04:39:50 2010 >+++ /taverna/utils/net.sf.taverna.t2.lang/trunk/ui/pom.xml Wed May 5 >01:34:09 2010 >@@ -19,5 +19,10 @@ > <groupId>log4j</groupId> > <artifactId>log4j</artifactId> > </dependency> >+ <dependency> >+ <groupId>net.sf.taverna.jedit</groupId> >+ <artifactId>jedit-syntax</artifactId> >+ <version>2.2.4-SNAPSHOT</version> >+ </dependency> > </dependencies> > </project> > >------------------------------------------------------------------------------ >_______________________________________________ >taverna-cvs mailing list >[email protected] >https://lists.sourceforge.net/lists/listinfo/taverna-cvs >Developers Guide: http://www.taverna.org.uk/developers/ ------------------------------------------------------------------------------ _______________________________________________ taverna-hackers mailing list [email protected] Web site: http://www.taverna.org.uk Mailing lists: http://www.taverna.org.uk/about/contact-us/ Developers Guide: http://www.taverna.org.uk/developers/
