Plugable RowSorter Patch:

I can't get to the cvs repository from work, so I made
diffs in another tool.  I've included two diffs and an
interface.

If the mail system eats the files I'll find another
way to send them.

Aaron

--- Matt Raible <[EMAIL PROTECTED]> wrote:
> These enhancements sound great.  Fabrizio - what do
> you think about
> adding them?  Send patches!!
> 
> Matt
> 
> > -----Original Message-----
> > From: [EMAIL PROTECTED]
> 
> >
>
[mailto:[EMAIL PROTECTED]
> On 
> > Behalf Of Aaron Smuts
> > Sent: Monday, May 24, 2004 9:54 PM
> > To: [EMAIL PROTECTED]
> > Subject: [displaytag-devel] avoiding unecessary
> iterations--runSimple
> > 
> > 
> > Problem: 
> > Large lists of objects are iterated pointlessly
> for
> > most uses of display tag.  Except for on the first
> > iteration, the CulumnTag methods do almost nothing
> if
> > you specify the property for a column.  Most of
> the
> > time and empty cell is added toa  Row and the Row
> > added to the Table Model.  However, adding a row
> to
> > the table is unnecesary for most purposes, as we
> shall
> > see.  The typically unncessary iterations result
> in
> > numerous method calls that dramatically hurt
> > performance.
> > 
> > The sample paging jsp's only use a few
> columns--about
> > 4.  If you add a colum the number of additional
> method
> > calls is multiplied by the number of rows for each
> > column.  Add 8 columns and a simple paging example
> > that displays only 10 rows of a list of 2000 can
> take
> > over 2 seconds of iteration time and then only
> 15-30
> > millis of real work in the TableTag doEndTag
> method. 
> > This makes the run time of display tag
> exponential.  
> > 
> > 
> > Solution:
> > I wanted to skip the iterations and do only the
> work
> > required.  I was able to do this and get the time
> to
> > nearly O(1), on non sorts.  (There is still some
> > iteration on building the partial list, so it is
> > probably really O(N), but that iteration is
> > negligible.  Sorting is merge sort time. )
> > 
> > To do this I did a couple of things.  
> > 
> > 1.  If the pageSize is greater than zero, I return
> > SKIP_BODY from TableTag doAfterBody.  I did it
> here to
> > let the first colum iteration take place for
> building
> > header.  (The condition to skip should take into
> > account more options, however.)
> > 
> > 2.  I pass the original list of data to
> TableModel. 
> > Then, if there is paging, in getViewable data of
> > TableTag, I build up the rows of the TableModel
> that I
> > want to use based on what is left of the orignal
> list
> > after pulling out the chunk for the page.  
> > 
> > 3. If there is a sort, I made the RowSorter
> > Implementation able to handle any object and not
> just
> > a Row, so I can pass it the original list and not
> the
> > Row wrapped list.  This was easy.  I just set
> object1
> > = obj1 (the method param).  The ability to do this
> is
> > key, since it makes the initial iteration largely
> > unnecessary.  
> > 
> > Also, with the RowSorter enhancement, I was able
> to
> > skip decoration for sorts.  Another great boost.
> > 
> > 
> > Result: 
> > With the plugable RowSorter enhancement and the
> > runSimple changes I was able to sort without
> > reflection.  The total work takes about 16-30
> millis
> > for a list of 2000 items with 8 columns, whereas
> > before it took 2 1/2 seconds.  
> > 
> > This makes display tag usable for very large
> result
> > sets.
> > 
> > I'm not sure what features will be unavailable
> with
> > this enhancement.  My proposed option should be
> > configurable by setting a property like
> runSimple=true
> > or something on the table.  Also the condition to
> > skip_body should take into account more that just
> > whether or not the list can be paged.
> > 
> > I may have left out a few details, but it works
> great
> > for probably 99% of what anyone will do with the
> > library.  It should be an option to runSimple or
> > something similar, since you can get a 200X
> > performance boost at modest conditions.
> > 
> > I'll submit patches if there are no obvious
> drawbacks.
> > 
> > Cheers,
> > 
> > Aaron Smuts
> > http://jakarta.apache.org/turbine/jcs/
> > 
> > 
> > 
> >     
> >             
> > __________________________________
> > Do you Yahoo!?
> > Friends.  Fun.  Try the all-new Yahoo! Messenger. 
> http://messenger.yahoo.com/ 
> 
> 
>
-------------------------------------------------------
> This SF.Net email is sponsored by: Oracle 10g
> Get certified on the hottest thing ever to hit the
> market... Oracle 10g.
> 
> Take an Oracle 10g class now, and we'll give you the
> exam FREE.
>
http://ads.osdn.com/?ad_id=3149&alloc_id=8166&op=click
> _______________________________________________
> displaytag-devel mailing list
> [EMAIL PROTECTED]
>
https://lists.sourceforge.net/lists/listinfo/displaytag-devel
> 
> 
> 
> 
>
-------------------------------------------------------
> This SF.Net email is sponsored by: Oracle 10g
> Get certified on the hottest thing ever to hit the
> market... Oracle 10g. 
> Take an Oracle 10g class now, and we'll give you the
> exam FREE.
>
http://ads.osdn.com/?ad_id=3149&alloc_id=8166&op=click
> _______________________________________________
> displaytag-devel mailing list
> [EMAIL PROTECTED]
>
https://lists.sourceforge.net/lists/listinfo/displaytag-devel



        
                
__________________________________
Do you Yahoo!?
Friends.  Fun.  Try the all-new Yahoo! Messenger.
http://messenger.yahoo.com/ 
Title: Guiffy Together Differences
   1   1 package org.displaytag.model;
   2   2 
   3   3 import java.util.Comparator;
   4   4 
   5   5 import org.apache.commons.lang.builder.EqualsBuilder;
   6   6 import org.apache.commons.lang.builder.HashCodeBuilder;
   7   7 import org.displaytag.decorator.TableDecorator;
   8   8 import org.displaytag.exception.ObjectLookupException;
   9   9 import org.displaytag.exception.RuntimeLookupException;
  10  10 import org.displaytag.util.LookupUtil;
  11  11 
  12  12 
  13  13 /**
  14  14  * Comparator for rows.
  15  15  * @author Fabrizio Giustina
  16  16  * @version $Revision $ ($Author $)
  17  17  */
  18    <public class RowSorterImpl implements Comparator
      18>public class RowSorter implements Comparator
  19  19 {
  20  20 
  21  21     /**
  22  22      * name of the property in bean.
  23  23      */
  24  24     private String property;
  25  25 
  26  26     /**
  27  27      * table decorator.
  28  28      */
  29  29     private TableDecorator decorator;
  30  30 
  31  31     /**
  32  32      * sort order ascending?
  33  33      */
  34  34     private boolean ascending;
  35  35 
  36  36     /**
  37  37      * Index of the sorted column.
  38  38      */
  39  39     private int columnIndex;
  40  40 
  41  41     /**
  42  42      * Initialize a new RowSorter.
  43  43      * @param sortedColumnIndex index of the sorted column
  44  44      * @param beanProperty name of the property. If pProperty is null column index is used to get a static cell value
  45  45      * from the row object
  46  46      * @param tableDecorator TableDecorator instance
  47  47      * @param ascendingOrder boolean ascending order?
  48  48      */
  49    <    public RowSorterImpl(int sortedColumnIndex, String beanProperty, TableDecorator tableDecorator, boolean ascendingOrder)
      49>    public RowSorter(int sortedColumnIndex, String beanProperty, TableDecorator tableDecorator, boolean ascendingOrder)
  50  50     {
  51  51         this.columnIndex = sortedColumnIndex;
  52  52         this.property = beanProperty;
  53  53         this.decorator = tableDecorator;
  54  54         this.ascending = ascendingOrder;
  55  55     }
  56  56 
  57  57     /**
  58  58      * Compares two objects by first fetching a property from each object and then comparing that value. If there are
  59  59      * any errors produced while trying to compare these objects then a RunTimeException will be thrown as any error
  60  60      * found here will most likely be a programming error that needs to be quickly addressed (like trying to compare
  61  61      * objects that are not comparable, or trying to read a property from a bean that is invalid, etc...)
  62  62      * @param object1 Object
  63  63      * @param object2 Object
  64  64      * @return int
  65  65      * @see java.util.Comparator#compare(Object, Object)
  66  66      */
  67  67     public final int compare(Object object1, Object object2)
  68  68     {
  69  69 
  70    <      // Should be able to handle sorting objects that are not wrapped in Rows
  71    <      Object obj1 = object1;
  72    <      Object obj2 = object2;
      70>        Object obj1 = null;
      71>        Object obj2 = null;
  73  72 
  74  73         // if property is null compare using two static cell objects
  75  74         if (this.property == null)
  76  75         {
  77  76             if (object1 instanceof Row)
  78  77             {
  79  78                 obj1 = ((Row) object1).getCellList().get(this.columnIndex);
  80  79             }
  81  80             if (object2 instanceof Row)
  82  81             {
  83  82                 obj2 = ((Row) object2).getCellList().get(this.columnIndex);
  84  83             }
  85  84 
  86  85             return checkNullsAndCompare(obj1, obj2);
  87  86 
  88  87         }
  89  88         else
  90  89         {
  91  90             if (object1 instanceof Row)
  92  91             {
  93  92                 obj1 = ((Row) object1).getObject();
  94  93             }
  95  94             if (object2 instanceof Row)
  96  95             {
  97  96                 obj2 = ((Row) object2).getObject();
  98  97             }
  99  98 
 100  99             try
 101 100             {
 102 101                 Object result1 = null;
 103 102                 Object result2 = null;
 104 103 
 105 104                 // If they have supplied a decorator, then make sure and use it for the sorting as well
 106 105                 if (this.decorator != null && this.decorator.hasGetterFor(this.property))
 107 106                 {
 108 107                     // set the row before sending to the decorator
 109 108                     this.decorator.initRow(obj1, 0, 0);
 110 109 
 111 110                     result1 = LookupUtil.getBeanProperty(this.decorator, this.property);
 112 111 
 113 112                     // set the row before sending to the decorator
 114 113                     this.decorator.initRow(obj2, 0, 0);
 115 114 
 116 115                     result2 = LookupUtil.getBeanProperty(this.decorator, this.property);
 117 116                 }
 118 117                 else
 119 118                 {
 120 119                     result1 = LookupUtil.getBeanProperty(obj1, this.property);
 121 120                     result2 = LookupUtil.getBeanProperty(obj2, this.property);
 122 121                 }
 123 122 
 124 123                 return checkNullsAndCompare(result1, result2);
 125 124             }
 126 125             catch (ObjectLookupException e)
 127 126             {
 128 127                 throw new RuntimeLookupException(getClass(), this.property, e);
 129 128             }
 130 129         }
 131 130     }
 132 131 
 133 132     /**
 134 133      * Compares two given objects handlig nulls and not comparable objects are handled. Not comparable objects are
 135 134      * compared using their string representation.
 136 135      * @param object1 first object to compare
 137 136      * @param object2 second object to compare
 138 137      * @return int result
 139 138      */
 140 139     private int checkNullsAndCompare(Object object1, Object object2)
 141 140     {
 142 141         int returnValue = 0;
 143 142 
 144 143         if (object1 instanceof Comparable && object2 instanceof Comparable)
 145 144         {
 146 145             returnValue = ((Comparable) object1).compareTo(object2);
 147 146         }
 148 147         else if (object1 == null && object2 == null)
 149 148         {
 150 149             returnValue = 0;
 151 150         }
 152 151         else if (object1 == null && object2 != null)
 153 152         {
 154 153             returnValue = 1;
 155 154         }
 156 155         else if (object1 != null && object2 == null)
 157 156         {
 158 157             returnValue = -1;
 159 158         }
 160 159         else
 161 160         {
 162 161             // if object are not null and don't implement comparable, compare using string values
 163 162             returnValue = object1.toString().compareTo(object2.toString());
 164 163         }
 165 164 
 166 165         int ascendingInt = this.ascending ? 1 : -1;
 167 166         return ascendingInt * returnValue;
 168 167     }
 169 168 
 170 169     /**
 171 170      * Is this Comparator the same as another one?
 172 171      * @param object Object
 173 172      * @return boolean
 174 173      * @see java.util.Comparator#equals(Object)
 175 174      */
 176 175     public final boolean equals(Object object)
 177 176     {
 178    <        if (object instanceof RowSorterImpl)
     177>        if (object instanceof RowSorter)
 179 178         {
 180    <            return new EqualsBuilder().append(this.property, ((RowSorterImpl) object).property).append(this.columnIndex,
 181    <                ((RowSorterImpl) object).columnIndex).isEquals();
     179>            return new EqualsBuilder().append(this.property, ((RowSorter) object).property).append(this.columnIndex,
     180>                ((RowSorter) object).columnIndex).isEquals();
 182 181         }
 183 182 
 184 183         return false;
 185 184     }
 186 185 
 187 186     /**
 188 187      * @see java.lang.Object#hashCode()
 189 188      */
 190 189     public final int hashCode()
 191 190     {
 192 191         return new HashCodeBuilder(31, 33).append(this.property).append(this.columnIndex).toHashCode();
 193 192     }
 194 193 
 195 194 }

Title: Guiffy Together Differences
   1   1 package org.displaytag.model;
   2   2 
   3   3 import java.util.ArrayList;
   4   4 import java.util.Collections;
   5   5 import java.util.List;
   6   6 
   7   7 import org.apache.commons.logging.Log;
   8   8 import org.apache.commons.logging.LogFactory;
   9   9 import org.displaytag.decorator.TableDecorator;
  10  10 import org.displaytag.properties.TableProperties;
  11  11 
  12  12 
  13  13 /**
  14  14  * @author Fabrizio Giustina
  15  15  * @version $Revision: 1.10 $ ($Author: fgiust $)
  16  16  */
  17  17 public class TableModel
  18  18 {
  19  19 
  20  20     /**
  21  21      * logger.
  22  22      */
  23  23     private static Log log = LogFactory.getLog(TableModel.class);
  24  24 
  25  25     /**
  26  26      * list of HeaderCell.
  27  27      */
  28  28     private List headerCellList;
  29  29 
  30  30     /**
  31  31      * full list (contains Row objects).
  32  32      */
  33  33     private List rowListFull;
  34  34 
  35  35     /**
  36  36      * list of data to be displayed in page.
  37  37      */
  38  38     private List rowListPage;
  39  39 
  40  40     /**
  41  41      * sort order = ascending?
  42  42      */
  43  43     private boolean sortOrderAscending = true;
  44  44 
  45  45     /**
  46  46      * sort full List? (false sort only displayed page).
  47  47      */
  48  48     private boolean sortFullTable = true;
  49  49 
  50  50     /**
  51    <     * Allows for substitution of row sorter classes.
  52    <     */
  53    <    private boolean isSortDecorated = false;
  54    <
  55    <    /**
  56    <     * Allows for substitution of row sorter classes.
  57    <     */
  58    <    private String rowSorterClassname = "org.displaytag.model.RowSorterImpl";
  59    <
  60    <    /**
  61  51      * index of the sorted column (-1 if the table is not sorted).
  62  52      */
  63  53     private int sortedColumn = -1;
  64  54 
  65  55     /**
  66  56      * Table decorator.
  67  57      */
  68  58     private TableDecorator tableDecorator;
  69  59 
  70  60     /**
  71  61      * id inherited from the TableTag (needed only for logging).
  72  62      */
  73  63     private String id;
  74  64 
  75  65     /**
  76  66      * configurable table properties.
  77  67      */
  78  68     private TableProperties properties;
  79  69 
  80  70     /**
  81  71      * Constructor for TableModel.
  82  72      * @param tableProperties table properties
  83  73      */
  84  74     public TableModel(TableProperties tableProperties)
  85  75     {
  86  76         this.rowListFull = new ArrayList(20);
  87  77         this.headerCellList = new ArrayList(20);
  88  78         this.properties = tableProperties;
  89  79     }
  90  80 
  91  81     /**
  92  82      * Setter for the tablemodel id.
  93  83      * @param tableId same id of table tag, needed for logging
  94  84      */
  95  85     public void setId(String tableId)
  96  86     {
  97  87         this.id = tableId;
  98  88     }
  99  89 
 100  90     /**
 101  91      * get the full list.
 102  92      * @return the full list containing Row objects
 103  93      */
 104  94     public List getRowListFull()
 105  95     {
 106  96         return this.rowListFull;
 107  97     }
 108  98 
 109  99     /**
 110 100      * gets the partial (paginated) list.
 111 101      * @return the partial list to display in page (contains Row objects)
 112 102      */
 113 103     public List getRowListPage()
 114 104     {
 115 105         return this.rowListPage;
 116 106     }
 117 107 
 118 108     /**
 119 109      * adds a Row object to the table.
 120 110      * @param row Row
 121 111      */
 122 112     public void addRow(Row row)
 123 113     {
 124 114         row.setParentTable(this);
 125 115 
 126 116         if (log.isDebugEnabled())
 127 117         {
 128 118             log.debug("[" + this.id + "] adding row " + row);
 129 119         }
 130 120         this.rowListFull.add(row);
 131 121     }
 132 122 
 133 123     /**
 134 124      * sets the sort full table property. If true the full list is sorted, if false sorting is applied only to the
 135 125      * displayed sublist.
 136 126      * @param sortFull boolean
 137 127      */
 138 128     public void setSortFullTable(boolean sortFull)
 139 129     {
 140 130         this.sortFullTable = sortFull;
 141 131     }
 142 132 
 143 133     /**
 144 134      * return the sort full table property.
 145 135      * @return boolean true if sorting is applied to the full list
 146 136      */
 147 137     public boolean isSortFullTable()
 148 138     {
 149 139         return this.sortFullTable;
 150 140     }
 151 141 
 152 142     /**
 153 143      * return the sort order of the page.
 154 144      * @return true if sort order is ascending
 155 145      */
 156 146     public boolean isSortOrderAscending()
 157 147     {
 158 148         return this.sortOrderAscending;
 159 149 
 160 150     }
 161 151 
 162 152     /**
 163 153      * set the sort order of the list.
 164 154      * @param isSortOrderAscending true to sort in ascending order
 165 155      */
 166 156     public void setSortOrderAscending(boolean isSortOrderAscending)
 167 157     {
 168 158         this.sortOrderAscending = isSortOrderAscending;
 169 159     }
 170 160 
 171 161     /**
 172 162      * @param rowList - the new value for this.rowListPage
 173 163      */
 174 164     public void setRowListPage(List rowList)
 175 165     {
 176 166         this.rowListPage = rowList;
 177 167     }
 178 168 
 179 169     /**
 180 170      * getter for the Table Decorator.
 181 171      * @return TableDecorator
 182 172      */
 183 173     public TableDecorator getTableDecorator()
 184 174     {
 185 175         return this.tableDecorator;
 186 176     }
 187 177 
 188 178     /**
 189 179      * setter for the table decorator.
 190 180      * @param decorator - the TableDecorator object
 191 181      */
 192 182     public void setTableDecorator(TableDecorator decorator)
 193 183     {
 194 184         this.tableDecorator = decorator;
 195 185     }
 196 186 
 197 187     /**
 198 188      * returns true if the table is sorted.
 199 189      * @return boolean true if the table is sorted
 200 190      */
 201 191     public boolean isSorted()
 202 192     {
 203 193         return this.sortedColumn != -1;
 204 194     }
 205 195 
 206 196     /**
 207 197      * returns the HeaderCell for the sorted column.
 208 198      * @return HeaderCell
 209 199      */
 210 200     public HeaderCell getSortedColumnHeader()
 211 201     {
 212 202         if (this.sortedColumn < 0 || (this.sortedColumn > (this.headerCellList.size() - 1)))
 213 203         {
 214 204             return null;
 215 205         }
 216 206         return (HeaderCell) this.headerCellList.get(this.sortedColumn);
 217 207     }
 218 208 
 219 209     /**
 220 210      * return the number of columns in the table.
 221 211      * @return int number of columns
 222 212      */
 223 213     public int getNumberOfColumns()
 224 214     {
 225 215         return this.headerCellList.size();
 226 216     }
 227 217 
 228 218     /**
 229 219      * return true is the table has no columns.
 230 220      * @return boolean
 231 221      */
 232 222     public boolean isEmpty()
 233 223     {
 234 224         return this.headerCellList.size() == 0;
 235 225     }
 236 226 
 237 227     /**
 238 228      * return the index of the sorted column.
 239 229      * @return index of the sorted column or -1 if the table is not sorted
 240 230      */
 241 231     public int getSortedColumnNumber()
 242 232     {
 243 233         return this.sortedColumn;
 244 234     }
 245 235 
 246 236     /**
 247 237      * set the sorted column index.
 248 238      * @param sortIndex - the index of the sorted column
 249 239      */
 250 240     public void setSortedColumnNumber(int sortIndex)
 251 241     {
 252 242         this.sortedColumn = sortIndex;
 253 243     }
 254 244 
 255 245     /**
 256 246      * Adds a column header (HeaderCell object).
 257 247      * @param headerCell HeaderCell
 258 248      */
 259 249     public void addColumnHeader(HeaderCell headerCell)
 260 250     {
 261 251         if (this.sortedColumn == this.headerCellList.size())
 262 252         {
 263 253             headerCell.setAlreadySorted();
 264 254         }
 265 255         headerCell.setColumnNumber(this.headerCellList.size());
 266 256 
 267 257         this.headerCellList.add(headerCell);
 268 258     }
 269 259 
 270 260     /**
 271 261      * List containing headerCell objects.
 272 262      * @return List containing headerCell objects
 273 263      */
 274 264     public List getHeaderCellList()
 275 265     {
 276 266         return this.headerCellList;
 277 267     }
 278 268 
 279 269     /**
 280 270      * returns a RowIterator on the requested (full|page) list.
 281 271      * @return RowIterator
 282 272      * @param full if true returns an iterator on te full list, if false only on the
 283 273      * viewable part.
 284 274      * @see org.displaytag.model.RowIterator
 285 275      */
 286 276     public RowIterator getRowIterator(boolean full)
 287 277     {
 288 278         RowIterator iterator = new RowIterator(
 289 279             full ? this.rowListFull : this.rowListPage,
 290 280             this.headerCellList,
 291 281             this.tableDecorator);
 292 282         // copy id for logging
 293 283         iterator.setId(this.id);
 294 284         return iterator;
 295 285     }
 296 286 
 297 287     /**
 298 288      * sorts the given list of Rows. The method is called internally by sortFullList() and sortPageList().
 299 289      * @param list List
 300 290      */
 301 291     private void sortRowList(List list)
 302 292     {
 303 293         if (isSorted())
 304 294         {
 305 295             HeaderCell sortedHeaderCell = getSortedColumnHeader();
 306 296 
 307 297             if (sortedHeaderCell != null)
 308 298             {
 309 299                 // If it is an explicit value, then sort by that, otherwise sort by the property...
 310 300                 if (sortedHeaderCell.getBeanPropertyName() != null
 311 301                     || (this.sortedColumn != -1 && this.sortedColumn < this.headerCellList.size()))
 312 302                 {
 313    <                  IRowSorter rowSorter = null;
 314    <                  try {
 315    <                    Class rowSorterClass = Class.forName(this.getRowSorterClassname());
 316    <                    rowSorter = (IRowSorter)rowSorterClass.newInstance();
 317    <                  } catch (Exception e) {
 318    <                    throw new IllegalArgumentException(
 319    <                      "\"" + this.getRowSorterClassname() +
 320    <                      "\" is not a valid row sorter class: " + e
 321    <                    );
     303>                    Collections.sort(list, new RowSorter(
     304>                        this.sortedColumn,
     305>                        sortedHeaderCell.getBeanPropertyName(),
     306>                        getTableDecorator(),
     307>                        this.sortOrderAscending));
     308>                }
     309>            }
     310>
     311>        }
     312>
     313>    }
     314>
     315>    /**
     316>     * sort the list displayed in page.
     317>     */
     318>    public void sortPageList()
     319>    {
     320>        if (log.isDebugEnabled())
     321>        {

Attachment: IRowSorter.java
Description: IRowSorter.java

Reply via email to