Provide cleaner, consistent sorting for dataTable and selectItems
-----------------------------------------------------------------

                 Key: TOMAHAWK-952
                 URL: https://issues.apache.org/jira/browse/TOMAHAWK-952
             Project: MyFaces Tomahawk
          Issue Type: New Feature
          Components: Extended Datatable
    Affects Versions: 1.1.6-SNAPSHOT
            Reporter: Mike Kienenberger
         Assigned To: Mike Kienenberger
            Priority: Minor


My requirements in most cases are to specify a sort order in the page code, not 
to allow end-users to manipulate the sort
order.    From what I can tell, there's no easy way to do this. I documented 
the most effective method I could find on the wiki under a "static sorting" 
subheading, but even that method leaves unnecessary links in the column headers.

At the same time, I looked into what it would take to make sorting cleaner and 
more user-friendly.

I came up with a subclass of extended dataTable and a replacement SortableModel 
that did what I wanted for the most part:

<my:sortableDataTable
       preserveDataModel="true"
       value="#{bean.carList}"
       var="car"
       >
       <f:facet name="comparator">
               <my:propertyComparator
                       property="style.color"
                       descending="true" />
       </f:facet>
</my:sortableDataTable>

This is based in part on reusing my components for sorting selectItem lists.   
For some reason, couldn't make this work without using preserveDataModel.   
[Strangely enough, doing the same thing with the current t:dataTable sort 
attributes didn't require preserveDataModel.]

In any case, a comparator component can be any UIComponent that implements a 
ComparatorSource interface (ie, public Comparator getComparator()), which 
provides a great deal of flexibility.

The propertyComparator implementation basically does the same thing as the 
internal guts of the current SortableModel, but is pluggable.  I used beanutils 
in my comparator rather than EL to process the property expression, which also 
eliminates the "rowObjectGet" hack.   An "EL comparator" could be implemented 
if the EL processing features were needed.

I think it would be worthwhile to replace the current SortableModel with a more 
generic pluggable one.   A good start would be to pull all of the 
property-resolving/comparison out of it, and stick it into a comparator like I 
did.   setSortCriteria(List criteria) appears to be misnomer since only the 
first item in the list is used -- using a comparator would also solve that 
issue as you can create MultipleComparator that takes a list of other 
comparators and goes through them in order.

Following is what DataTable looks like to make this work.  Note that this 
doesn't handle the current sorting options.

   protected DataModel createDataModel()
   {
       DataModel dataModel = super.createDataModel();

       UIComponent comparatorUIComponent = getComparator();
       Comparator comparator = null;
       if (null != comparatorUIComponent)
       {
               if (comparatorUIComponent instanceof ComparatorSource)
               {
                       comparator =
((ComparatorSource)comparatorUIComponent).getComparator();
               }
               else
               {
                       // TODO: need log error instead
                       throw new RuntimeException("comparatorUIComponent should
implement ComparatorSource");
               }
       }

       boolean isSortable = null != comparator;

       if (isSortable)
       {
           if (!(dataModel instanceof BaseSortableModel))
           {
               dataModel = new BaseSortableModel(dataModel);
           }

           ((BaseSortableModel)dataModel).setComparator(comparator);
       }

       return dataModel;
   }

After stripping out the comparator stuff from SortableModel, these are the 
major changes:

       public void setComparator(Comparator _comparator) {
               this._comparator = _comparator;
               _sort();
       }


   private void _sort()
   {
       if (null == _comparator)
       {
           // restore unsorted order:
           _baseIndicesList = _sortedIndicesList = null;
               return;
       }

       //TODO: support -1 for rowCount:
       int sz = getRowCount();
       if ((_baseIndicesList == null) || (_baseIndicesList.size() != sz))
       {
           // we do not want to mutate the original data.
           // however, instead of copying the data and sorting the copy,
           // we will create a list of indices into the original data, and
           // sort the indices. This way, when certain rows are made current
           // in this Collection, we can make them current in the underlying
           // DataModel as well.
           _baseIndicesList = new IntList(sz);
       }

       final int rowIndex = _model.getRowIndex();

       _model.setRowIndex(0);
       // Make sure the model has that row 0! (It could be empty.)
       if (_model.isRowAvailable())
       {
           Collections.sort(_baseIndicesList, new
RowDataComparator(_comparator, _model));
           _sortedIndicesList = null;
       }

       _model.setRowIndex(rowIndex);
   }

   protected class RowDataComparator implements Comparator
   {
               private Comparator dataComparator = null;
               private DataModel dataModel = null;

               public RowDataComparator(Comparator comparator, DataModel model)
               {
                       this.dataComparator = comparator;
                       this.dataModel = model;
               }

               public int compare(Object arg1, Object arg2) {
                       Integer r1 = (Integer)arg1;
                       Integer r2 = (Integer)arg2;
                       dataModel.setRowIndex(r1.intValue());
               Object rowData1 = _model.getRowData();
               dataModel.setRowIndex(r2.intValue());
               Object rowData2 = _model.getRowData();

               return dataComparator.compare(rowData1, rowData2);
               }
   }


Also, here's how I'd like to improve t:selectItems.  I've had a custom subclass 
of f:selectItems of this working for awhile.  Notice how we can reuse the same 
propertyComparator component.  This particular implementation can take a list 
of comparator children and implicitly wraps them in a MultipleComparator.   
That's not really possible with a dataTable facet, so we'd want to provide a 
MultipleComparator component.

<my:orderedSelectItems value="#{bean.carList}">
   <my:propertyComparator
        property="style.color"
        descending="false" />
</my:orderedSelectItems>

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to