Table sorting arbitrary components not implementing Comparable

I wonder if there is a best practice for implementing sortable columns that have arbitrary components which currently don’t implement the Comparable interface.

In my setup some table data is fetched from DB* and some (batch progress) data is fetched from other servers and presented in the table. What I want is to be able to click on a column header and have the data sorted according to the preferences of that column header, given the data type it’s been set to present. The only way I’'ve been able to achieve this with arbitrary components is to have those components implement the
Comparable<
MyComponent
>
interface.

It feels akward to have to implement that interface for all kinds of comparisons I’d like a table to make for standard Vaadin components and self made components, depending on what the column shall base its sorting on, for example:

 
...
indexedContainer.addContainerProperty("Checkbox", MyCheckBox.class, ""); // no luck with sorting CheckBox
indexedContainer.addContainerProperty("Name", MyButton.class, ""); // no luck with sorting Button
indexedContainer.addContainerProperty("Description", String.class, ""); // works fine
indexedContainer.addContainerProperty("Progress", MyComplexProgressComponent.class, "");

...
table.setContainerDataSource(indexedContainer);

MyCheckBox extends CheckBox implements Comparable<MyCheckBox> {
@Override
public int compareTo(MyCheckBox o) {
return booleanValue().compareTo(o.booleanValue());
}
MyButton extends Button implements Comparable<MyButton> {
@Override
public int compareTo(MyButton o) {
return getCaption().toLowerCase().compareTo(o.getCaption().toLowerCase());
}
MyComplexProgressComponent extends HorDashLayout implements Comparable<MyComplexProgressComponent > {
private Label caption;
// a bunch of Vaadin components

@Override
public int compareTo(MyComplexProgressComponent o) {
return caption.toLowerCase().compareTo(o.caption.toLowerCase());
}
}

I also have a bad feeling about not implementing equals and hashcode, but how should one best implement equals and hashcode if the self made component uses a bunch of Vaadin components?

I tried to use the
ItemSorter
but I don’t really understand how it’s supposed to be used:

private class MyTableSpecificItemSorter implements ItemSorter {
      private String selectedColumnId;
      private boolean ascendingOrder;

      private final static Map<String, MyClientObject.Property> COLUMN_TO_PROPERTY =
         new HashMap<String, MyClientObject.Property>();
      static {
         COLUMN_TO_PROPERTY.put("Checkbox", //??? );
         COLUMN_TO_PROPERTY.put("Name", MyClientObject.Property.NAME);
         COLUMN_TO_PROPERTY.put("Description", MyClientObject.Property.DESCRIPTION);
         COLUMN_TO_PROPERTY.put("Progress", //???);
      }

      @Override
      public void setSortProperties(Container.Sortable sortable, Object[] objects, boolean[]
 booleans) {
         selectedColumnId = (String) objects[0]
;
         ascendingOrder = booleans[0]
;
      }

      @Override
      public int compare(Object o, Object o1) {
         // Here I have no handle to the Checkbox or MyComplexProgressComponent since parameters
         // o and o1 are of type MyClientObject (the registered item ID), so I'm unable to 
         // sort those columns correctly, otherwise I can do:
         MyClientObject clientObject1 = (MyClientObject) o;
         MyClientObject clientObject2 = (MyClientObject) o1;
         MyClientObject.Property propertyToCompare = COLUMN_TO_PROPERTY.get(selectedColumnId);           
         int result = clientObject1.getProperty(propertyToCompare).compareTo(clientObject2.getProperty(propertyToCompare));
         return ascendingOrder ? result : result * -1;         
      }
   }

What I’d like to be able to do is to add a specific Comparator for each column that executes independent of the columns data type’s implementation of, or lack of implementation of, the Comparable interface:


indexedContainer.addContainerProperty("Checkbox", CheckBox.class, "", new MyCheckBoxComparator()); 
indexedContainer.addContainerProperty("Name", Button.class, "", new MyButtonComparator());
indexedContainer.addContainerProperty("Description", String.class, ""); 
indexedContainer.addContainerProperty("Progress", MyComplexProgressComponent.class, "", new MyComplexProgressComponentComparator());

To summarize:
Do you extend and implement the Comparable interface; or use the ItemSorter; or some other approach?

Thanks

  • When the web layer (Vaadin) wants to fetch domain data from DB an ‘EJB controller’ is used by the Vaadin view controller. The EJB controller in turn uses a DAO (through Hibernate) which retrieves the domain object which the EJB controller then maps to a kind of client object; where the members of the domain object are put into a property map for validation and I18N handling, for example myDomainObject.getName() would be accessed through myClientObject.getProperty(MyClientObject.Property.NAME).

Option 1: Do as you have done and implement Comparable when needed

Option 2: Use a custom ItemSorter or a DefaultItemSorter with a custom Comparator and override Container.getSortablePropertyIds

A simple example for case insensitive sorting of CheckBoxes, Buttons and Strings:

IndexedContainer ic = new IndexedContainer() {
            @Override
            public Collection<?> getSortableContainerPropertyIds() {
                // Default implementation allows sorting only if the property
                // type can be cast to Comparable
                return getContainerPropertyIds();
            }
        };

        ic.setItemSorter(new DefaultItemSorter(new Comparator<Object>() {

            public int compare(Object o1, Object o2) {
                if (o1 instanceof CheckBox && o2 instanceof CheckBox) {
                    Boolean b1 = ((CheckBox) o1).booleanValue();
                    return b1.compareTo(((CheckBox) o2).booleanValue());
                } else if (o1 instanceof Button && o2 instanceof Button) {
                    String caption1 = ((Button) o1).getCaption().toLowerCase();
                    String caption2 = ((Button) o2).getCaption().toLowerCase();
                    return caption1.compareTo(caption2);

                } else if (o1 instanceof String && o2 instanceof String) {
                    return ((String) o1).toLowerCase().compareTo(
                            ((String) o2).toLowerCase());
                }

                return 0;

            }
        }));

Thanks for your answer Artur, it worked out really well :smiley:

My Container is like this attributeTablecontainer.addContainerProperty(“value”,
CustomComponent
.class, null);
In which we are putting different CustomComponents Like (DateField , ComboBox TextField).
I need to sort that coloumn when up arrow is clicked in the coloumn header.Like date value will come first then the other combo box and textfield will be sorted based on the value.

Please can you help me in that .
Also the upArrow and Down arrow is coming to those coloumn which added in the container with Label or String attributeTablecontainer.addContainerProperty(“code”,
Label
.class, null);

But it is not coming in customcomponent case. Please tell me how can we get that arrows to arrange the coloumns in ascending and descending order.