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 }