From 929885236e1f91b271b2de54a9d22135e7bfac1a Mon Sep 17 00:00:00 2001 From: Marc Englund Date: Fri, 27 Nov 2009 08:38:54 +0000 Subject: [PATCH] Integrated patch that implements ItemSorter for IndexedContainer, #3434 svn changeset:10081/svn branch:6.2 --- .../vaadin/data/util/BeanItemContainer.java | 71 ++++--- .../vaadin/data/util/DefaultItemSorter.java | 197 ++++++++++++++++++ .../data/util/HierarchicalContainer.java | 12 +- .../vaadin/data/util/IndexedContainer.java | 121 +++-------- src/com/vaadin/data/util/ItemSorter.java | 52 +++++ 5 files changed, 326 insertions(+), 127 deletions(-) create mode 100644 src/com/vaadin/data/util/DefaultItemSorter.java create mode 100644 src/com/vaadin/data/util/ItemSorter.java diff --git a/src/com/vaadin/data/util/BeanItemContainer.java b/src/com/vaadin/data/util/BeanItemContainer.java index aaa7d9cd56..2575615232 100644 --- a/src/com/vaadin/data/util/BeanItemContainer.java +++ b/src/com/vaadin/data/util/BeanItemContainer.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -52,6 +51,11 @@ public class BeanItemContainer implements Indexed, Sortable, Filterable, private Set filters = new HashSet(); + /** + * The item sorter which is used for sorting the container. + */ + private ItemSorter itemSorter = new DefaultItemSorter(); + /* Special serialization to handle method references */ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { @@ -385,46 +389,33 @@ public class BeanItemContainer implements Indexed, Sortable, Filterable, return sortables; } + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[], + * boolean[]) + */ public void sort(Object[] propertyId, boolean[] ascending) { - for (int i = 0; i < ascending.length; i++) { - final boolean asc = ascending[i]; - final Object property = propertyId[i]; - // sort allItems, then filter and notify - Collections.sort(allItems, new Comparator() { - @SuppressWarnings("unchecked") - public int compare(BT a, BT b) { - Comparable va, vb; - if (asc) { - va = (Comparable) getItem(a).getItemProperty(property) - .getValue(); - vb = (Comparable) getItem(b).getItemProperty(property) - .getValue(); - } else { - va = (Comparable) getItem(b).getItemProperty(property) - .getValue(); - vb = (Comparable) getItem(a).getItemProperty(property) - .getValue(); - } - - /* - * Null values are considered less than all others. The - * compareTo method cannot handle null values for the - * standard types. - */ - if (va == null) { - return (vb == null) ? 0 : -1; - } else if (vb == null) { - return 1; - } - - return va.compareTo(vb); - } - }); - } + itemSorter.setSortProperties(this, propertyId, ascending); + + doSort(); + // notifies if anything changes in the filtered list, including order filterAll(); } + /** + * Perform the sorting of the data structures in the container. This is + * invoked when the itemSorter has been prepared for the sort + * operation. Typically this method calls + * Collections.sort(aCollection, getItemSorter()) on all arrays + * (containing item ids) that need to be sorted. + * + */ + protected void doSort() { + Collections.sort(allItems, getItemSorter()); + } + public void addListener(ItemSetChangeListener listener) { if (itemSetChangeListeners == null) { itemSetChangeListeners = new LinkedList(); @@ -531,4 +522,12 @@ public class BeanItemContainer implements Indexed, Sortable, Filterable, filterAll(); } + public ItemSorter getItemSorter() { + return itemSorter; + } + + public void setItemSorter(ItemSorter itemSorter) { + this.itemSorter = itemSorter; + } + } diff --git a/src/com/vaadin/data/util/DefaultItemSorter.java b/src/com/vaadin/data/util/DefaultItemSorter.java new file mode 100644 index 0000000000..4a99714212 --- /dev/null +++ b/src/com/vaadin/data/util/DefaultItemSorter.java @@ -0,0 +1,197 @@ +package com.vaadin.data.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; + +import com.vaadin.data.Container; +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.Container.Sortable; + +/** + * Provides a default implementation of an ItemSorter. The + * DefaultItemSorter adheres to the + * {@link Sortable#sort(Object[], boolean[])} rules and sorts the container + * according to the properties given using + * {@link #setSortProperties(Sortable, Object[], boolean[])}. + *

+ * A Comparator is used for comparing the individual Property + * values. The comparator can be set using the constructor. If no comparator is + * provided a default comparator is used. + * + */ +public class DefaultItemSorter implements ItemSorter { + + private java.lang.Object[] sortPropertyIds; + private boolean[] sortDirections; + private Container container; + private Comparator propertyValueComparator; + + /** + * Constructs a DefaultItemSorter using the default Comparator + * for comparing Propertyvalues. + * + */ + public DefaultItemSorter() { + this(new DefaultPropertyValueComparator()); + } + + /** + * Constructs a DefaultItemSorter which uses the Comparator + * indicated by the propertyValueComparator parameter for + * comparing Propertyvalues. + * + * @param propertyValueComparator + * The comparator to use when comparing individual + * Property values + */ + public DefaultItemSorter(Comparator propertyValueComparator) { + this.propertyValueComparator = propertyValueComparator; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.ItemSorter#compare(java.lang.Object, + * java.lang.Object) + */ + public int compare(Object o1, Object o2) { + Item item1 = container.getItem(o1); + Item item2 = container.getItem(o2); + + for (int i = 0; i < sortPropertyIds.length; i++) { + + int result = compareProperty(sortPropertyIds[i], sortDirections[i], + item1, item2); + + // If order can be decided + if (result != 0) { + return result; + } + + } + + return 0; + } + + /** + * Compares the property indicated by propertyId in the items + * indicated by item1 and item2 for order. Returns + * a negative integer, zero, or a positive integer as the property value in + * the first item is less than, equal to, or greater than the property value + * in the second item. If the sortDirection is false the + * returned value is negated. + *

+ * The comparator set for this DefaultItemSorter is used for + * comparing the two property values. + * + * @param propertyId + * The property id for the property that is used for comparison. + * @param sortDirection + * The direction of the sort. A false value negates the result. + * @param item1 + * The first item to compare. + * @param item2 + * The second item to compare. + * @return + */ + protected int compareProperty(Object propertyId, boolean sortDirection, + Item item1, Item item2) { + + // Get the properties to compare + final Property property1 = item1.getItemProperty(propertyId); + final Property property2 = item2.getItemProperty(propertyId); + + // Get the values to compare + final Object value1 = (property1 == null) ? null : property1.getValue(); + final Object value2 = (property2 == null) ? null : property2.getValue(); + + // Result of the comparison + int r = 0; + if (sortDirection) { + r = propertyValueComparator.compare(value1, value2); + } else { + r = propertyValueComparator.compare(value2, value1); + } + + return r; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.util.ItemSorter#setSortProperties(com.vaadin.data.Container + * .Sortable, java.lang.Object[], boolean[]) + */ + public void setSortProperties(Container.Sortable container, + Object[] propertyId, boolean[] ascending) { + this.container = container; + + // Removes any non-sortable property ids + final List ids = new ArrayList(); + final List orders = new ArrayList(); + final Collection sortable = container.getSortableContainerPropertyIds(); + for (int i = 0; i < propertyId.length; i++) { + if (sortable.contains(propertyId[i])) { + ids.add(propertyId[i]); + orders.add(Boolean.valueOf(i < ascending.length ? ascending[i] + : true)); + } + } + + sortPropertyIds = ids.toArray(); + sortDirections = new boolean[orders.size()]; + for (int i = 0; i < sortDirections.length; i++) { + sortDirections[i] = (orders.get(i)).booleanValue(); + } + + } + + /** + * Provides a default comparator used for comparing {@link Property} values. + * The DefaultPropertyValueComparator assumes all objects it + * compares can be cast to Comparable. + * + */ + public static class DefaultPropertyValueComparator implements + Comparator { + + public int compare(Object o1, Object o2) { + int r = 0; + // Normal non-null comparison + if (o1 != null && o2 != null) { + // FIXME: Use standard Boolean comparison as Boolean implements + // Comaprable since JDK1.5 + if ((o1 instanceof Boolean) && (o2 instanceof Boolean)) { + if (o1.equals(o2)) { + r = 0; // both have the same boolean value + } else if (((Boolean) o1).booleanValue()) { + return 1; // true is greater than false + } else { + return -1; + } + } else { + // Assume the objects can be cast to Comparable, throw + // ClassCastException otherwise. + r = ((Comparable) o1).compareTo(o2); + } + } else if (o1 == o2) { + // Objects are equal if both are null + r = 0; + } else { + if (o1 == null) { + r = -1; // null is less than non-null + } else { + r = 1; // non-null is greater than null + } + } + + return r; + } + + } + +} diff --git a/src/com/vaadin/data/util/HierarchicalContainer.java b/src/com/vaadin/data/util/HierarchicalContainer.java index a6af0427ad..f5b65f1dad 100644 --- a/src/com/vaadin/data/util/HierarchicalContainer.java +++ b/src/com/vaadin/data/util/HierarchicalContainer.java @@ -308,12 +308,18 @@ public class HierarchicalContainer extends IndexedContainer implements return success; } + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.IndexedContainer#doSort() + */ @Override - void doSort() { + protected void doSort() { super.doSort(); - Collections.sort(roots, this); + + Collections.sort(roots, getItemSorter()); for (LinkedList childList : children.values()) { - Collections.sort(childList, this); + Collections.sort(childList, getItemSorter()); } } diff --git a/src/com/vaadin/data/util/IndexedContainer.java b/src/com/vaadin/data/util/IndexedContainer.java index 15c36953a1..609f488938 100644 --- a/src/com/vaadin/data/util/IndexedContainer.java +++ b/src/com/vaadin/data/util/IndexedContainer.java @@ -9,7 +9,6 @@ import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.EventObject; import java.util.HashMap; import java.util.HashSet; @@ -17,7 +16,6 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; -import java.util.List; import java.util.NoSuchElementException; import com.vaadin.data.Container; @@ -49,8 +47,8 @@ import com.vaadin.data.Property; @SuppressWarnings("serial") public class IndexedContainer implements Container.Indexed, Container.ItemSetChangeNotifier, Container.PropertySetChangeNotifier, - Property.ValueChangeNotifier, Container.Sortable, Comparator, - Cloneable, Container.Filterable { + Property.ValueChangeNotifier, Container.Sortable, Cloneable, + Container.Filterable { /* Internal structure */ @@ -108,14 +106,9 @@ public class IndexedContainer implements Container.Indexed, private LinkedList itemSetChangeListeners = null; /** - * Temporary store for sorting property ids. + * The item sorter which is used for sorting the container. */ - private Object[] sortPropertyId; - - /** - * Temporary store for sorting direction. - */ - private boolean[] sortDirection; + private ItemSorter itemSorter = new DefaultItemSorter(); /** * Filters that are applied to the container to limit the items visible in @@ -1379,49 +1372,31 @@ public class IndexedContainer implements Container.Indexed, * boolean[]) */ public void sort(Object[] propertyId, boolean[] ascending) { + // Set up the item sorter for the sort operation + itemSorter.setSortProperties(this, propertyId, ascending); - // Removes any non-sortable property ids - final List ids = new ArrayList(); - final List orders = new ArrayList(); - final Collection sortable = getSortableContainerPropertyIds(); - for (int i = 0; i < propertyId.length; i++) { - if (sortable.contains(propertyId[i])) { - ids.add(propertyId[i]); - orders.add(Boolean.valueOf(i < ascending.length ? ascending[i] - : true)); - } - } - - if (ids.size() == 0) { - return; - } - sortPropertyId = ids.toArray(); - sortDirection = new boolean[orders.size()]; - for (int i = 0; i < sortDirection.length; i++) { - sortDirection[i] = (orders.get(i)).booleanValue(); - } - + // Perform the actual sort doSort(); + // Post sort updates if (filteredItemIds != null) { updateContainerFiltering(); } else { fireContentsChange(-1); } - // Remove temporary references - sortPropertyId = null; - sortDirection = null; - } /** - * Perform the sorting of the container. Called when everything needed for - * the compare function has been set up. + * Perform the sorting of the data structures in the container. This is + * invoked when the itemSorter has been prepared for the sort + * operation. Typically this method calls + * Collections.sort(aCollection, getItemSorter()) on all arrays + * (containing item ids) that need to be sorted. * */ - void doSort() { - Collections.sort(itemIds, this); + protected void doSort() { + Collections.sort(itemIds, getItemSorter()); } /* @@ -1430,7 +1405,7 @@ public class IndexedContainer implements Container.Indexed, * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds * () */ - public Collection getSortableContainerPropertyIds() { + public Collection getSortableContainerPropertyIds() { final LinkedList list = new LinkedList(); for (final Iterator i = propertyIds.iterator(); i.hasNext();) { @@ -1445,52 +1420,25 @@ public class IndexedContainer implements Container.Indexed, } /** - * Compares two items for sorting. + * Returns the ItemSorter used for comparing items in a sort. See + * {@link #setItemSorter(ItemSorter)} for more information. * - * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) - * @see #sort((java.lang.Object[], boolean[]) + * @return The ItemSorter used for comparing two items in a sort. */ - public int compare(Object o1, Object o2) { - - for (int i = 0; i < sortPropertyId.length; i++) { - - // Get the compared properties - final Property pp1 = getContainerProperty(o1, sortPropertyId[i]); - final Property pp2 = getContainerProperty(o2, sortPropertyId[i]); - - // Get the compared values - final Object p1 = pp1 == null ? null : pp1.getValue(); - final Object p2 = pp2 == null ? null : pp2.getValue(); - - // Result of the comparison - int r = 0; - - // Normal non-null comparison - if (p1 != null && p2 != null) { - if ((p1 instanceof Boolean) && (p2 instanceof Boolean)) { - r = p1.equals(p2) ? 0 - : ((sortDirection[i] ? 1 : -1) * (((Boolean) p1) - .booleanValue() ? 1 : -1)); - } else { - r = sortDirection[i] ? ((Comparable) p1).compareTo(p2) - : -((Comparable) p1).compareTo(p2); - } - } - - // If both are nulls - else if (p1 == p2) { - r = 0; - } else { - r = (sortDirection[i] ? 1 : -1) * (p1 == null ? -1 : 1); - } - - // If order can be decided - if (r != 0) { - return r; - } - } + public ItemSorter getItemSorter() { + return itemSorter; + } - return 0; + /** + * Sets the ItemSorter used for comparing items in a sort. The ItemSorter is + * called for each collection that needs sorting. A default ItemSorter is + * used if this is not explicitly set. + * + * @param itemSorter + * The ItemSorter used for comparing two items in a sort. + */ + public void setItemSorter(ItemSorter itemSorter) { + this.itemSorter = itemSorter; } /** @@ -1524,10 +1472,7 @@ public class IndexedContainer implements Container.Indexed, nc.singlePropertyValueChangeListeners = singlePropertyValueChangeListeners != null ? (Hashtable) singlePropertyValueChangeListeners .clone() : null; - nc.sortDirection = sortDirection != null ? (boolean[]) sortDirection - .clone() : null; - nc.sortPropertyId = sortPropertyId != null ? (Object[]) sortPropertyId - .clone() : null; + nc.types = types != null ? (Hashtable) types.clone() : null; nc.filters = filters == null ? null : (HashSet) filters.clone(); diff --git a/src/com/vaadin/data/util/ItemSorter.java b/src/com/vaadin/data/util/ItemSorter.java new file mode 100644 index 0000000000..eb665b48cd --- /dev/null +++ b/src/com/vaadin/data/util/ItemSorter.java @@ -0,0 +1,52 @@ +package com.vaadin.data.util; + +import java.util.Comparator; + +import com.vaadin.data.Container; +import com.vaadin.data.Container.Sortable; + +/** + * An item comparator which is compatible with the {@link Sortable} interface. + * The ItemSorter interface can be used in Sortable + * implementations to provide a custom sorting method. The interface + */ +public interface ItemSorter extends Comparator, Cloneable { + + /** + * Sets the parameters for an upcoming sort operation. The parameters + * determine what container to sort and how the ItemSorter + * sorts the container. + * + * @param container + * The container that will be sorted. The container must contain + * the propertyIds given in the propertyId + * parameter. + * @param propertyId + * The property ids used for sorting. The property ids must exist + * in the container and should only be used if they are also + * sortable, i.e include in the collection returned by + * container.getSortableContainerPropertyIds(). See + * {@link Sortable#sort(Object[], boolean[])} for more + * information. + * @param ascending + * Sorting order flags for each property id. See + * {@link Sortable#sort(Object[], boolean[])} for more + * information. + */ + void setSortProperties(Container.Sortable container, Object[] propertyId, + boolean[] ascending); + + /** + * Compares its two arguments for order. Returns a negative integer, zero, + * or a positive integer as the first argument is less than, equal to, or + * greater than the second. + *

+ * The parameters for the ItemSorter compare() + * method must always be item ids which exist in the container set using + * {@link #setSortProperties(Sortable, Object[], boolean[])}. + * + * @see Comparator#compare(Object, Object) + */ + int compare(Object itemId1, Object itemId2); + +} -- 2.39.5