View Javadoc

1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kns.web.struts.form;
17  
18  import org.apache.commons.beanutils.BeanComparator;
19  import org.apache.commons.beanutils.PropertyUtils;
20  import org.apache.commons.lang.StringUtils;
21  import org.kuali.rice.kns.util.TableRenderUtil;
22  
23  import java.util.Collections;
24  import java.util.Comparator;
25  import java.util.Date;
26  import java.util.List;
27  
28  /**
29   * This class holds the metadata necessary to render a table when displaytag is not being used.
30   */
31  public class KualiTableRenderFormMetadata {
32      private int viewedPageNumber;
33      private int totalNumberOfPages;
34      private int firstRowIndex;
35      private int lastRowIndex;
36      private int switchToPageNumber;
37  
38      /**
39       * The number of rows that match the query criteria
40       */
41      private int resultsActualSize;
42  
43      /**
44       * The number of rows that match the query criteria or
45       *  the max results limit size (if applicable), whichever is less
46       */
47      private int resultsLimitedSize;
48  
49      /**
50       * when the looked results screen was rendered, the index of the column that the results were sorted on.  -1 for unknown, index numbers
51       * starting at 0
52       */
53      private int previouslySortedColumnIndex;
54  
55      /**
56       * Comment for <code>columnToSortIndex</code>
57       */
58      private int columnToSortIndex;
59  
60      /**
61       * If it is not feasible to use an index for lookup, as with mapped properties in an Map<String, String>, it may be necessary to store a string value
62       */
63      private String columnToSortName;
64  
65      /**
66       * When the screen was last rendered, the column name on which it was previously sorted -- this is important for toggling between ascending and descending
67       * sort orders
68       */
69      private String previouslySortedColumnName;
70  
71      private boolean sortDescending;
72  
73      public KualiTableRenderFormMetadata() {
74          sortDescending = false;
75      }
76  
77      /**
78       * Gets the columnToSortIndex attribute.
79       * @return Returns the columnToSortIndex.
80       */
81      public int getColumnToSortIndex() {
82          return columnToSortIndex;
83      }
84  
85      /**
86       * Sets the columnToSortIndex attribute value.
87       * @param columnToSortIndex The columnToSortIndex to set.
88       */
89      public void setColumnToSortIndex(int columnToSortIndex) {
90          this.columnToSortIndex = columnToSortIndex;
91      }
92  
93      /**
94       * Gets the previouslySortedColumnIndex attribute.
95       * @return Returns the previouslySortedColumnIndex.
96       */
97      public int getPreviouslySortedColumnIndex() {
98          return previouslySortedColumnIndex;
99      }
100 
101     /**
102      * Sets the previouslySortedColumnIndex attribute value.
103      * @param previouslySortedColumnIndex The previouslySortedColumnIndex to set.
104      */
105     public void setPreviouslySortedColumnIndex(int previouslySortedColumnIndex) {
106         this.previouslySortedColumnIndex = previouslySortedColumnIndex;
107     }
108 
109     /**
110      * Gets the resultsActualSize attribute.
111      * @return Returns the resultsActualSize.
112      */
113     public int getResultsActualSize() {
114         return resultsActualSize;
115     }
116 
117     /**
118      * Sets the resultsActualSize attribute value.
119      * @param resultsActualSize The resultsActualSize to set.
120      */
121     public void setResultsActualSize(int resultsActualSize) {
122         this.resultsActualSize = resultsActualSize;
123     }
124 
125     /**
126      * Gets the resultsLimitedSize attribute.
127      * @return Returns the resultsLimitedSize.
128      */
129     public int getResultsLimitedSize() {
130         return resultsLimitedSize;
131     }
132 
133     /**
134      * Sets the resultsLimitedSize attribute value.
135      * @param resultsLimitedSize The resultsLimitedSize to set.
136      */
137     public void setResultsLimitedSize(int resultsLimitedSize) {
138         this.resultsLimitedSize = resultsLimitedSize;
139     }
140 
141     /**
142      * Gets the switchToPageNumber attribute.
143      * @return Returns the switchToPageNumber.
144      */
145     public int getSwitchToPageNumber() {
146         return switchToPageNumber;
147     }
148 
149     /**
150      * Sets the switchToPageNumber attribute value.
151      * @param switchToPageNumber The switchToPageNumber to set.
152      */
153     public void setSwitchToPageNumber(int switchToPageNumber) {
154         this.switchToPageNumber = switchToPageNumber;
155     }
156 
157     /**
158      * Gets the viewedPageNumber attribute.
159      * @return Returns the viewedPageNumber.
160      */
161     public int getViewedPageNumber() {
162         return viewedPageNumber;
163     }
164 
165     /**
166      * Sets the viewedPageNumber attribute value.
167      * @param viewedPageNumber The viewedPageNumber to set.
168      */
169     public void setViewedPageNumber(int viewedPageNumber) {
170         this.viewedPageNumber = viewedPageNumber;
171     }
172 
173     /**
174      * Gets the totalNumberOfPages attribute.
175      * @return Returns the totalNumberOfPages.
176      */
177     public int getTotalNumberOfPages() {
178         return totalNumberOfPages;
179     }
180 
181     /**
182      * Sets the totalNumberOfPages attribute value.
183      * @param totalNumberOfPages The totalNumberOfPages to set.
184      */
185     public void setTotalNumberOfPages(int totalNumberOfPages) {
186         this.totalNumberOfPages = totalNumberOfPages;
187     }
188 
189     /**
190      * Gets the firstRowIndex attribute.
191      * @return Returns the firstRowIndex.
192      */
193     public int getFirstRowIndex() {
194         return firstRowIndex;
195     }
196 
197     /**
198      * Sets the firstRowIndex attribute value.
199      * @param firstRowIndex The firstRowIndex to set.
200      */
201     public void setFirstRowIndex(int firstRowIndex) {
202         this.firstRowIndex = firstRowIndex;
203     }
204 
205     /**
206      * Gets the lastRowIndex attribute.
207      * @return Returns the lastRowIndex.
208      */
209     public int getLastRowIndex() {
210         return lastRowIndex;
211     }
212 
213     /**
214      * Sets the lastRowIndex attribute value.
215      * @param lastRowIndex The lastRowIndex to set.
216      */
217     public void setLastRowIndex(int lastRowIndex) {
218         this.lastRowIndex = lastRowIndex;
219     }
220 
221     /**
222      * Gets the sortDescending attribute.
223      * @return Returns the sortDescending.
224      */
225     public boolean isSortDescending() {
226         return sortDescending;
227     }
228 
229     /**
230      * Sets the sortDescending attribute value.
231      * @param sortDescending The sortDescending to set.
232      */
233     public void setSortDescending(boolean sortDescending) {
234         this.sortDescending = sortDescending;
235     }
236 
237 	/**
238 	 * @return the columnToSortName
239 	 */
240 	public String getColumnToSortName() {
241 		return this.columnToSortName;
242 	}
243 
244 	/**
245 	 * @param columnToSortName the columnToSortName to set
246 	 */
247 	public void setColumnToSortName(String columnToSortName) {
248 		this.columnToSortName = columnToSortName;
249 	}
250 
251 	/**
252 	 * @return the previouslySortedColumnName
253 	 */
254 	public String getPreviouslySortedColumnName() {
255 		return this.previouslySortedColumnName;
256 	}
257 
258 	/**
259 	 * @param previouslySortedColumnName the previouslySortedColumnName to set
260 	 */
261 	public void setPreviouslySortedColumnName(String previouslySortedColumnName) {
262 		this.previouslySortedColumnName = previouslySortedColumnName;
263 	}
264 
265 
266     /**
267      * Sets the paging form parameters to go to the first page of the list
268      *
269      * @param listSize size of table being rendered
270      * @param maxRowsPerPage
271      */
272     public void jumpToFirstPage(int listSize, int maxRowsPerPage) {
273         jumpToPage(0, listSize, maxRowsPerPage);
274     }
275 
276     /**
277      * Sets the paging form parameters to go to the last page of the list
278      *
279      * @param listSize size of table being rendered
280      * @param maxRowsPerPage
281      */
282     public void jumpToLastPage(int listSize, int maxRowsPerPage) {
283         jumpToPage(TableRenderUtil.computeTotalNumberOfPages(listSize, maxRowsPerPage) - 1, listSize, maxRowsPerPage);
284     }
285 
286     /**
287      * Sets the paging form parameters to go to the specified page of the list
288      *
289      * @param pageNumber first page is 0, must be non-negative.  If the list is not large enough to have the page specified, then
290      *   this method will be equivalent to calling jumpToLastPage.
291      * @param listSize size of table being rendered
292      * @param maxRowsPerPage
293      *
294      * @see KualiTableRenderFormMetadata#jumpToLastPage(int, int)
295      */
296     public void jumpToPage(int pageNumber, int listSize, int maxRowsPerPage) {
297         int totalPages = TableRenderUtil.computeTotalNumberOfPages(listSize, maxRowsPerPage);
298         setTotalNumberOfPages(totalPages);
299         if (pageNumber >= totalPages) {
300             pageNumber = totalPages - 1;
301         }
302         setViewedPageNumber(pageNumber);
303         setFirstRowIndex(TableRenderUtil.computeStartIndexForPage(pageNumber, listSize, maxRowsPerPage));
304         setLastRowIndex(TableRenderUtil.computeLastIndexForPage(pageNumber, listSize, maxRowsPerPage));
305     }
306 
307     /**
308      * Sorts a list on the form according to the form metadata (sortColumName, previouslySortedColumnName)
309      *
310      * @param memberTableMetadata
311      * @param items
312      * @param maxRowsPerPage
313      * @throws org.kuali.rice.kew.api.exception.WorkflowException
314      */
315     public void sort(List<?> items, int maxRowsPerPage) {
316 
317     	// Don't bother to sort null, empty or singleton lists
318     	if (items == null || items.size() <= 1)
319     		return;
320 
321         String columnToSortOn = getColumnToSortName();
322 
323         // Don't bother to sort if no column to sort on is provided
324         if (StringUtils.isEmpty(columnToSortOn))
325         	return;
326 
327         String previouslySortedColumnName = getPreviouslySortedColumnName();
328 
329         // We know members isn't null or empty from the check above
330     	Object firstItem = items.get(0);
331     	// Need to decide if the comparator is for a bean property or a mapped key on the qualififer attribute set
332     	Comparator comparator = null;
333     	Comparator subComparator = new Comparator<Object>() {
334 
335     		public int compare(Object o1, Object o2) {
336     			if (o1 == null)
337     				return -1;
338     			if (o2 == null)
339     				return 1;
340 
341     			if (o1 instanceof java.util.Date && o2 instanceof java.util.Date) {
342     				Date d1 = (Date)o1;
343     				Date d2 = (Date)o2;
344     				return d1.compareTo(d2);
345     			}
346 
347     			String s1 = o1.toString();
348     			String s2 = o2.toString();
349     			int n1=s1.length(), n2=s2.length();
350     			for (int i1=0, i2=0; i1<n1 && i2<n2; i1++, i2++) {
351     				char c1 = s1.charAt(i1);
352     				char c2 = s2.charAt(i2);
353     				if (c1 != c2) {
354     					c1 = Character.toUpperCase(c1);
355     					c2 = Character.toUpperCase(c2);
356     					if (c1 != c2) {
357     						c1 = Character.toLowerCase(c1);
358     						c2 = Character.toLowerCase(c2);
359     						if (c1 != c2) {
360     							return c1 - c2;
361     						}
362     					}
363     				}
364     			}
365     			return n1 - n2;
366     		}
367     	};
368     	// If the columnName is a readable bean property on the first member, then it's safe to say we need a simple bean property comparator,
369     	// otherwise it's a mapped property -- syntax for BeanComparator is "name" and "name(key)", respectively
370     	if (PropertyUtils.isReadable(firstItem, columnToSortOn))
371     		comparator = new BeanComparator(columnToSortOn, subComparator);
372     	else
373     		comparator = new BeanComparator(new StringBuilder().append("qualifierAsMap(").append(columnToSortOn).append(")").toString(), subComparator);
374 
375 
376         // If the user has decided to resort by the same column that the list is currently sorted by, then assume that s/he wants to reverse the order of the sort
377         if (!StringUtils.isEmpty(columnToSortOn) && !StringUtils.isEmpty(previouslySortedColumnName) && columnToSortOn.equals(previouslySortedColumnName)) {
378             // we're already sorted on the same column that the user clicked on, so we reverse the list
379             if (isSortDescending())
380                 comparator = Collections.reverseOrder(comparator);
381 
382             setSortDescending(!isSortDescending());
383         } else {
384         	// Track which column we're currently sorting, so that the above logic will work on the next sort
385         	setPreviouslySortedColumnName(columnToSortOn);
386         	setSortDescending(true);
387         }
388 
389         //if the user is just going between pages no need to sort
390         if (getSwitchToPageNumber() == getViewedPageNumber()) {
391             Collections.sort(items, comparator);
392         }
393 
394 		jumpToFirstPage(items.size(), maxRowsPerPage);
395     }
396 
397 }