cancel
Showing results for 
Search instead for 
Did you mean: 

TableSorter problem with boolean value

Former Member
0 Kudos

Hi,

We have a table with boolean attributes that are associated to CheckBox UI elements.

When Sorting according to a columns which is boolean (the UI element is Checkbox) we get a ClassCastException.

Any ideas?

Thanks,

Aviad

Accepted Solutions (0)

Answers (5)

Answers (5)

Former Member
0 Kudos

look at the code of table sorter.

You can either give a String[] of sortable columns or a null, if it a null the table sorter sort all columns

Former Member
0 Kudos

Can you please post a code sample?

Thanks,

Aviad

Former Member
0 Kudos

below is the code:


import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Hashtable;
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.IWDImage;
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);
	}
	public TableSorter(IWDTable table, IWDAction sortAction, Map comparators, String[] sortableColumns, boolean includeicons) {
		this.sorticons =includeicons;
		init(table, sortAction, comparators, sortableColumns); 
	}
	
	public TableSorter(IWDTable table, IWDAction sortAction, Map comparators, String[] sortableColumns, boolean includeicons, Hashtable binder) {
		this.sorticons =includeicons;
		this.hashbinder = binder;
		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 (this.hashbinder!=null && hashbinder.containsKey(attributeName)) attributeName=hashbinder.get(attributeName).toString();

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

	public void forceSort(String columnId,String direction , IWDNode dataSource) {
		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();
		if (element instanceof IWDImage){
			if (sorticons)return ((IWDImage) element).bindingOfSource();	
		}
		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;
	
	/*
	 * sorting icons
	 */
	
	private static  boolean  sorticons;
	/* biding hashtable
	 */

	private static Hashtable hashbinder=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;
	}

}

Message was edited by:

Armin Reichert

Former Member
0 Kudos

My experience is that it is VERY important that the node which contains the data which should be sorted is a VALUENODE and not a MODELNODE!

former_member197348
Active Contributor
0 Kudos

Hi Avaid,

In EP 7.0 you wont get this problem. If you are using the earlier versions, pass the columns individually in the wdDoModifyView.

regards,

Siva

Former Member
0 Kudos

this is for the old table sorter

look at the new table sorter.

/people/bertram.ganz/blog/2006/03/07/enhanced-web-dynpro-java-tablesorter-for-sap-netweaver-04s

former_member197348
Active Contributor
0 Kudos

Hi Aviad,

Which EP version are you using? and do you want sort that boolean field also?

Former Member
0 Kudos

What exception do you have???

Vito