package com.openMap1.mapper.views;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Iterator;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.TableColumn;
import com.openMap1.mapper.query.CellContent;
import com.openMap1.mapper.util.XMLUtil;
import com.openMap1.mapper.util.GenUtil;
import com.openMap1.mapper.core.SortableRow;
import org.w3c.dom.Element;
/**
* helper class used by any tabular view whose rows may need to be sorted.
* @author robert
*
*/
public class RowSorter extends ViewerSorter{
// comparators, visible to viewers
public static Comparator<String> comparatorForType(int sortType)
{
Comparator<String> comp = null;
if (sortType == SortableRow.STRING) comp = stringComparator;
if (sortType == SortableRow.NUMBER) comp = numberComparator;
return comp;
}
/**
* alphabetic comparator for two Strings
*/
public static Comparator<String> stringComparator = new Comparator<String>(){
public int compare(String s1, String s2)
{return (s1.compareTo(s2));}
};
/**
* Comparator for two strings that are to be compared as numbers.
* Any String that cannot be interpreted as a number is taken as zero
* for this comparison.
*/
public static Comparator<String> numberComparator = new Comparator<String>(){
public int compare(String s1, String s2)
{
int result = 0;
double n1 = 0;
double n2 = 0;
try {n1 = new Double(s1).doubleValue();} catch (Exception ex) {}
try {n2 = new Double(s2).doubleValue();} catch (Exception ex) {}
if ((n1 - n2) > 0.0) result = 1;
if ((n1 - n2) < 0.0) result = -1;
return result;
}
};
protected TableViewer viewer;
// sort and comparison information for all columns in the query result
protected Vector<SortInfo> infos;
// those columns that have been selected for comparison in the sort
protected Vector<SortInfo> selectedSorters;
public Vector<SortInfo> selectedSorters() {return selectedSorters;}
protected int previousColumnSelected = -1;
/* keep track of the SelectionAdapter given to each TableColumn,
* so you can remove it before giving the column another one */
protected Hashtable<Integer,SelectionAdapter> selectionAdapters;
protected SortableView sortView;
//-------------------------------------------------------------------------------
// constructor, and initialisation after any query
//-------------------------------------------------------------------------------
public RowSorter(TableViewer viewer, SortableView sortView)
{
this.viewer = viewer;
this.sortView = sortView;
/* Need to keep track of selection adapters on columns throughout
* all queries in the lifetime of the Query Result view -
* do not re-initialise this for each new query result. */
selectionAdapters = new Hashtable<Integer,SelectionAdapter>();
}
/**
* Create selection listeners for all sortable columns, so that mouse clicks on those columns
* wil define a sort order
* @param columns
* @param comparators
*/
public void initialiseForResult(Vector<TableColumn> columns, Vector<Comparator<String>> comparators)
{
previousColumnSelected = -1;
infos = new Vector<SortInfo>();
selectedSorters = new Vector<SortInfo>();
for (int i = 0; i < columns.size(); i++)
{
SortInfo si = new SortInfo(i,comparators.elementAt(i));
infos.add(si);
createSelectionListener(columns.elementAt(i),si);
}
}
/* keep track of the SelectionAdapter given to each TableColumn,
* so you can remove it before giving the column another one */
protected void createSelectionListener(final TableColumn column, final SortInfo info)
{
SelectionAdapter sa = new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {sortUsing(info);}
};
SelectionAdapter oldSa = selectionAdapters.get(new Integer(info.columnIndex()));
if (oldSa != null) column.removeSelectionListener(oldSa);
column.addSelectionListener(sa);
selectionAdapters.put(new Integer(info.columnIndex()),sa);
}
//-------------------------------------------------------------------------------
// comparing rows
//-------------------------------------------------------------------------------
/**
* Work through the columns defined in selectedSorters, comparing on
* each column until you get a decision
*/
public int compare(Viewer viewer, Object r1, Object r2)
{
int result = 0;
Vector<?> row1 = new Vector<Object>();
Vector<?> row2 = new Vector<Object>();
if ((r1 instanceof Vector<?>) && (r2 instanceof Vector<?>))
{
row1 = (Vector<?>)r1;
row2 = (Vector<?>)r2;
}
else if ((r1 instanceof SortableRow) && (r2 instanceof SortableRow))
{
row1 = ((SortableRow)r1).rowVector();
row2 = ((SortableRow)r2).rowVector();
}
else {System.out.println("Rows of sorted table do not implement SortableRow");}
if ((row1.size() == row2.size()) && (selectedSorters != null))
{
// break off looking at further columns as soon as any column gives a decision
for (int i = 0; i < selectedSorters.size(); i++) if (result == 0)
{
SortInfo si = selectedSorters.get(i);
int col = si.columnIndex();
if ((col > -1) && (col < row1.size()))
{
Object obj1 = row1.get(col);
Object obj2 = row2.get(col);
result = compareCell(obj1,obj2,si);
}
}
}
return result;
}
// compare the String contents of two cells, according to the comparator used for the column
private int compareCell(Object obj1, Object obj2, SortInfo si)
{
int compare = 0;
if ((obj1 instanceof CellContent) && (obj2 instanceof CellContent))
{
CellContent cell1 = (CellContent)obj1;
CellContent cell2 = (CellContent)obj2;
compare = si.comparator().compare(cell1.getText(), cell2.getText());
}
else if ((obj1 instanceof String) && (obj2 instanceof String))
{
compare = si.comparator().compare((String)obj1, (String)obj2);
}
if (si.descending()) compare = -compare;
return compare;
}
//-------------------------------------------------------------------------------
// telling the sorter how to compare rows
//-------------------------------------------------------------------------------
// maintain the Vector selectedSorters to do the sort implied by the sequence of column selections
protected void sortUsing(SortInfo info)
{
/* if this column was the previous column selected,
* the new selection only reverses the ascending/descending choice of the main sort */
if (previousColumnSelected == info.columnIndex())
selectedSorters.get(0).reverse();
/* otherwise make the selected column head of the selected sorters, and
* remove it from any lower position to ensure it is not duplicated. */
else
{
selectedSorters.insertElementAt(info, 0);
for (int s = 1; s < selectedSorters.size();s++)
{
SortInfo sr = selectedSorters.elementAt(s);
if (sr.columnIndex() == info.columnIndex())
selectedSorters.remove(s);
}
}
// remember that this column has just been selected, so its sort can be reversed on the next selection
previousColumnSelected = info.columnIndex();
// do the sort
viewer.refresh();
sortView.showResultAgain();
}
/**
* for testing purposes, restore the selection of sort columns from
* a saved query result view
* @param sortEl the <Sort> element of a saved query result view
*/
public void restoreSortColumns(Element sortEl)
{
selectedSorters = new Vector<SortInfo>();
for (Iterator<Element> sc = XMLUtil.namedChildElements(sortEl,"Column").iterator();sc.hasNext();)
try
{
Element column = sc.next();
int index = new Integer(column.getAttribute("index")).intValue();
SortInfo colSort = infos.get(index);
if (column.getAttribute("descending").equals("true")) colSort.reverse();
selectedSorters.add(colSort);
}
catch (Exception ex) {GenUtil.surprise(ex,"RowSorter.restoreSortColumns");}
}
}