TableSorter

package com.zpec.utils;


import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDAbstractDropDownByIndex;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDAbstractDropDownByKey;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDAbstractInputField;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDAbstractTableColumn;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDCaption;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDCheckBox;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDLink;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDProgressIndicator;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDRadioButton;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTable;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTableCellEditor;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTableColumn;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTableColumnGroup;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTextEdit;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTextView;
import com.sap.tc.webdynpro.clientserver.uielib.standard.api.WDTableColumnSortDirection;
import com.sap.tc.webdynpro.progmodel.api.IWDAction;
import com.sap.tc.webdynpro.progmodel.api.IWDCustomEvent;
import com.sap.tc.webdynpro.progmodel.api.IWDNode;
import com.sap.tc.webdynpro.progmodel.api.IWDNodeElement;
import com.sap.tc.webdynpro.progmodel.api.IWDViewElement;
import com.sap.tc.webdynpro.services.sal.localization.api.WDResourceHandler;

/**
* Helper class that makes a Web Dynpro table UI element sortable (column-wise).
*/
public final class TableSorter {
/**
* @param table
* @param sortAction
* @param comparators
*/

/**
* Creates a table sorter for the given table using the given sort action.
* This constructor must be called from <code>wdDoModifyView()</code>, but
* usually only when that hook is called for the first time. Store the newly
* created instance in a context attribute with Java native type
* <code>com.sap.tc.webdynpro.tests.utils.TableSorter</code>.
* The given sort action's event handler will be bound to the <code>onSort</code>
* event of the table and must at least call this table sorter's
* <code>sort(wdEvent)</code> method.
*
* Every column of the table is made sortable if possible according to the
* following rules.
* If a comparator is given for a column's ID and it is a
* <code>NodeElementByAttributeComparator</code>, then that comparator defines
* both the attribute and the ordering used to sort that column.
* If any other comparator is given and an attribute can be determined from
* that column's table cell editor, then that attribute is used to sort that
* column according to the ordering imposed by the given comparator.
* If no comparator is given but an attribute can be determined from
* that column's table cell editor, then that attribute is used to sort that
* column according to the natural ordering of that attribute's type.
* Else that column is left untouched.
*
* Additionally it is possible to define the sortable columns by their
* TableColumn UI element ids.
*
* @see sort()
* @see NodeElementByAttributeComparator
* @see com.sap.tc.webdynpro.clientserver.uielib.standard.api.IWDTable
*/
public TableSorter(IWDTable table, IWDAction sortAction, Map comparators) {
init(table, sortAction, comparators, null);
}

public TableSorter(IWDTable table, IWDAction sortAction, Map comparators, String[] sortableColumns) {
init(table, sortAction, comparators, sortableColumns);
}

/**
* Initialisation stuff
*/
private void init(IWDTable table, IWDAction sortAction, Map comparators, String[] sortableColumns){
this.table = table;
if(sortableColumns == null){
sortableCols = null;
}else{
sortableCols = new HashMap();
for (int i = 0; i < sortableColumns.length; i++) {
sortableCols.put(sortableColumns[i], sortableColumns[i]);
}
}

// sanity checks
if (sortAction == null)
throw new IllegalArgumentException("Sort action must be given");
if (table == null)
throw new IllegalArgumentException("Table must be given");
if (table.bindingOfDataSource() == null)
throw new IllegalArgumentException(
"Data source of table with id '" + table.getId() + "' must be bound");

// make the columns sortable
String dataSourcePrefix = table.bindingOfDataSource() + ".";
//TODO: remove the following line since this method is not longer available in later releases
setComparatorsForColumns(dataSourcePrefix, table.iterateColumns(), comparators);
setComparatorsForColumns(dataSourcePrefix, table.iterateGroupedColumns(), comparators);

//set up the table properties
table.setOnSort(sortAction);
table.mappingOfOnSort().addSourceMapping(IWDTable.IWDOnSort.COL, "selectedColumn");
table.mappingOfOnSort().addSourceMapping(IWDTable.IWDOnSort.DIRECTION, "sortDirection");
}

/**
* Try to make the given columns sortable (recusivly, if necessary)
*/
private void setComparatorsForColumns(String dataSourcePrefix, Iterator columnIterator, Map comparators){
int index = 0;
for (Iterator it = columnIterator; it.hasNext(); ++index) { // for every column: try to make it bindable
IWDAbstractTableColumn abstractColumn = (IWDAbstractTableColumn) it.next();
if(abstractColumn instanceof IWDTableColumn){

IWDTableColumn column = (IWDTableColumn)abstractColumn;
if(sortableCols == null || sortableCols.containsKey(column.getId())){
//try to make this column sortable
Comparator comparator = null;
if (comparators != null){
comparator = (Comparator)comparators.get(column.getId());
}

NodeElementByAttributeComparator elementComparator = null;
if (comparator instanceof NodeElementByAttributeComparator) {
// the easy one, attribute and ordering are given
elementComparator = (NodeElementByAttributeComparator)comparator;
} else { // attribute must be determined
String bindingOfPrimaryProperty = bindingOfPrimaryProperty(column.getTableCellEditor());
if (bindingOfPrimaryProperty == null || !bindingOfPrimaryProperty.startsWith(dataSourcePrefix)){
//no attribute found or outside of data source
column.setSortState(WDTableColumnSortDirection.NOT_SORTABLE);
continue;
}
String attributeName = bindingOfPrimaryProperty.substring(dataSourcePrefix.length());
Collection subnodes = new ArrayList();
if (attributeName.indexOf('.') >= 0){
//attribute not immediately below data source
String[] tokens = tokenize (attributeName, ".");
for(int i=0; i<tokens.length-1; i++){
subnodes.add(tokens[i]);
}
attributeName = tokens[tokens.length-1];
}
if(subnodes.size() == 0){
elementComparator = new NodeElementByAttributeComparator(attributeName, comparator);
}else{
elementComparator = new NodeElementByAttributeComparator(attributeName, comparator, subnodes);
}
}

// set up internal data structures
comparatorForColumn.put(column, elementComparator);

//set sort state
column.setSortState(WDTableColumnSortDirection.NONE);
}else{
//column should not be sortable
column.setSortState(WDTableColumnSortDirection.NOT_SORTABLE);
}

}else if (abstractColumn instanceof IWDTableColumnGroup){
//it's just a column group -> try to bind the columns of the column group
IWDTableColumnGroup columnGroup = (IWDTableColumnGroup)abstractColumn;
setComparatorsForColumns(dataSourcePrefix, columnGroup.iterateColumns(), comparators);
}

}
}

/**
* Tokenizes the input string according to the given delimiters. The delimiters will be left out.
* Example: tokenize("Hello_World", "_") results ["Hello", "World"]
*/
private String[] tokenize (String input, String delim){
StringTokenizer tokenizer = new StringTokenizer(input, delim);
String[] tokens = new String[tokenizer.countTokens()];
int index = 0;
while(tokenizer.hasMoreTokens()){
tokens[index] = tokenizer.nextToken();
index++;
}
return tokens;
}

/**
* This method must be called from the event handler of this table sorter's
* sort action. It performs the actual sort operation.
*/
public void sort(IWDCustomEvent wdEvent, IWDNode dataSource) {
// find the things we need
String columnId = wdEvent.getString("selectedColumn");
String direction = wdEvent.getString("sortDirection");
IWDTableColumn column = (IWDTableColumn) table.getView().getElement(columnId);
NodeElementByAttributeComparator elementComparator = (NodeElementByAttributeComparator) comparatorForColumn.get(column);

if (elementComparator == null){
//not a sortable column
column.setSortState(WDTableColumnSortDirection.NOT_SORTABLE);
return;
}

// sorting
elementComparator.setSortDirection(WDTableColumnSortDirection.valueOf(direction));
dataSource.sortElements(elementComparator);
}

/**
* Returns the binding of the given table cell editor's property that is
* considered "primary" or <code>null</code> if no such binding exists or no
* such property can be determined.
*/
private static final String bindingOfPrimaryProperty(IWDTableCellEditor editor) {
return editor instanceof IWDViewElement ? bindingOfPrimaryProperty((IWDViewElement) editor) : null;
}

/**
* Returns the binding of the given view element's property that is
* considered "primary" or <code>null</code> if no such binding exists or no
* such property can be determined.
*/
private static final String bindingOfPrimaryProperty(IWDViewElement element) {
if (element instanceof IWDAbstractDropDownByIndex)
return ((IWDAbstractDropDownByIndex) element).bindingOfTexts();
if (element instanceof IWDAbstractDropDownByKey)
return ((IWDAbstractDropDownByKey) element).bindingOfSelectedKey();
if (element instanceof IWDAbstractInputField)
return ((IWDAbstractInputField) element).bindingOfValue();
if (element instanceof IWDCaption)
return ((IWDCaption) element).bindingOfText();
if (element instanceof IWDCheckBox)
return ((IWDCheckBox) element).bindingOfChecked();
if (element instanceof IWDLink)
return ((IWDLink) element).bindingOfText();
if (element instanceof IWDProgressIndicator)
return ((IWDProgressIndicator) element).bindingOfPercentValue();
if (element instanceof IWDRadioButton)
return ((IWDRadioButton) element).bindingOfSelectedKey();
if (element instanceof IWDTextEdit)
return ((IWDTextEdit) element).bindingOfValue();
if (element instanceof IWDTextView)
return ((IWDTextView) element).bindingOfText();

return null;
}

/**
* Instance of a comparator according to the ordering imposed by the
* implementation of <code>Comparable</code>.
*/
private static final Comparator DEFAULT = new Comparator() {
/**
* Compares the given objects according to the ordering imposed by the first
* ones <code>compareTo(Object)</code> function. Furthermore, <code>null</code>
* is treated to be less than any object.
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(Object o1, Object o2) {
if (o1 == null && o2 == null)
return 0;
if (o1 == null)
return -1;
if (o2 == null)
return +1;
if (o1 instanceof Boolean && o2 instanceof Boolean)
return o1.toString().compareTo(o2.toString()); // false < true
if (o1 instanceof String && o2 instanceof String){
//Use a Collator for sorting according to the given Locale
Collator collate = Collator.getInstance(WDResourceHandler.getCurrentSessionLocale());
return collate.compare(o1, o2);
}
return ((Comparable) o1).compareTo((Comparable) o2);
}
};

/**
* Map of table column to comparator (<code>ReversableComparator</code>)
* used for sorting that column (sortable columns only).
*/
private Map comparatorForColumn = new HashMap();

/**
* The table to be sorted.
*/
private IWDTable table = null;

/**
* Column-IDs of the columns, which should be sortable
*/
private Map sortableCols = null;

/**
* Generic comparator that compares node elements by a given attribute with
* the help of a given comparator.
*/
public final class NodeElementByAttributeComparator implements Comparator {


/**
* Creates a new comparator for the given attribute name that compares values
* of that attribute according to the natural ordering of that attribute's
* type (which must implement <code>java.lang.Comparable</code>).
*/
public NodeElementByAttributeComparator(String attributeName) {
this(attributeName, null, false, new ArrayList());
}

/**
* Creates a new comparator for the given attribute name that compares values
* of that attribute with the help of the given comparator. If no comparator
* is given, the natural ordering of that attribute's type is used.
*/
public NodeElementByAttributeComparator(String attributeName, Comparator comparator) {
this(attributeName, comparator, false, new ArrayList());
}

/**
* Creates a new comparator for the given attribute name that compares values
* of that attribute either as objects (i.e. "in internal format") or as text
* (i.e. "in external format") as indicated. The ordering is the natural
* ordering of that attribute's type (which must implement
* <code>java.lang.Comparable</code>) in case objects are compared or the
* natural ordering of <code>java.lang.String</code> in case texts are compared.
*/
public NodeElementByAttributeComparator(String attributeName, boolean compareAsText) {
this(attributeName, null, compareAsText, new ArrayList());
}

/**
* Creates a new comparator for the given attribute name that compares values
* of that attribute according to the natural ordering of that attribute's
* type (which must implement <code>java.lang.Comparable</code>). In addition it is possible
* to define the path to a child node with the <code>java.util.Collection</code> subnodes.
* (List of child node names in the correct order)
*/
public NodeElementByAttributeComparator(String attributeName, Collection subnodes) {
this(attributeName, null, false, subnodes);
}

/**
* Creates a new comparator for the given attribute name that compares values
* of that attribute with the help of the given comparator. If no comparator
* is given, the natural ordering of that attribute's type is used. In addition it is possible
* to define the path to a child node with the <code>java.util.Collection</code> subnodes.
* (List of child node names in the correct order)
*/
public NodeElementByAttributeComparator(String attributeName, Comparator comparator, Collection subnodes) {
this(attributeName, comparator, false, subnodes);
}

/**
* Creates a new comparator for the given attribute name that compares values
* of that attribute either as objects (i.e. "in internal format") or as text
* (i.e. "in external format") as indicated. The ordering is the natural
* ordering of that attribute's type (which must implement
* <code>java.lang.Comparable</code>) in case objects are compared or the
* natural ordering of <code>java.lang.String</code> in case texts are compared. In addition it is possible
* to define the path to a child node with the <code>java.util.Collection</code> subnodes.
* (List of child node names in the correct order)
*/
public NodeElementByAttributeComparator(String attributeName, boolean compareAsText, Collection subnodes) {
this(attributeName, null, compareAsText, subnodes);
}

/**
* Internal constructor.
*/
private NodeElementByAttributeComparator(
String attributeName,
Comparator comparator,
boolean compareAsText,
Collection subNodes) {
if (attributeName == null)
throw new IllegalArgumentException("Attribute name must not be null");
if (comparator == null)
comparator = DEFAULT;

this.attributeName = attributeName;
this.comparator = comparator;
this.compareAsText = compareAsText;
this.sortDirection = true;
this.subNodes = subNodes;
}

/**
* Sets the sort direction of this comparator to the given direction. The comparator sort in ascending order by default.
* @see com.sap.tc.webdynpro.clientserver.uielib.standard.api.WDTableColumnSortDirection
*/
public void setSortDirection(WDTableColumnSortDirection direction){
if(direction.equals(WDTableColumnSortDirection.UP)){
sortDirection = true;
}else if(direction.equals(WDTableColumnSortDirection.DOWN)){
sortDirection = false;
}
}

/**
* Compares the given objects which must be instances of <code>IWDNodeElement</code>
* according to the values of the attribute given at construction time
* with the help of the comparator given at construction time.
*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
* @see com.sap.tc.webdynpro.progmodel.api.IWDNodeElement
*/
public int compare(Object o1, Object o2) {
IWDNodeElement element1 = (IWDNodeElement) o1;
IWDNodeElement element2 = (IWDNodeElement) o2;
if(subNodes.size() > 0){
element1 = getSubNodeElement(element1, 0);
element2 = getSubNodeElement(element2, 0);
}
Object attributeValue1 = null;
Object attributeValue2 = null;
if(element1 != null){
attributeValue1 =
compareAsText
? element1.getAttributeAsText(attributeName)
: element1.getAttributeValue(attributeName);
}
if(element2 != null){
attributeValue2 =
compareAsText
? element2.getAttributeAsText(attributeName)
: element2.getAttributeValue(attributeName);
}

if(sortDirection){
return comparator.compare(attributeValue1, attributeValue2);
}else{
return comparator.compare(attributeValue2, attributeValue1);
}
}

/**
* Determines recursivly the child node, which have an attribute with the given name.
* The path to this child node must be specified in the subnodes property of this comparator.
* Start this method with index = 0.
*/
private IWDNodeElement getSubNodeElement(IWDNodeElement currentElement, int index){
if(currentElement == null || index >= subNodes.size()){
//end of recursion
return currentElement;
}else{
return getSubNodeElement(currentElement.node().getChildNode((String)subNodes.toArray()[index], currentElement.index()).getCurrentElement(), index+1);
//return getSubNodeElement(currentElement.node().getChildNode((String)subNodes.toArray()[index], currentElement.index()).getElementAt(0), index+1);
}
}

/**
* Name of the attribute used for comparisons.
*/
private final String attributeName;

/**
* Comparator used for comparing the attribute's values.
*/
private final Comparator comparator;

/**
* Indicates whether attribute values are compared as text (as opposed to
* "as objects").
*/
private final boolean compareAsText;

/**
* Sort direction (true = ascending order, false = descending order)
*/
private boolean sortDirection;

/**
* List of child node names
* (Description of the path from the given context node to the specified attribute)
*/
private Collection subNodes;
}

}

你可能感兴趣的:(UI,UP)