001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.kns.web.struts.form;
017
018import org.apache.commons.beanutils.BeanComparator;
019import org.apache.commons.beanutils.PropertyUtils;
020import org.apache.commons.lang.StringUtils;
021import org.kuali.rice.kns.util.TableRenderUtil;
022
023import java.util.Collections;
024import java.util.Comparator;
025import java.util.Date;
026import java.util.List;
027
028/**
029 * This class holds the metadata necessary to render a table when displaytag is not being used.
030 *
031 * @deprecated KNS Struts deprecated, use KRAD and the Spring MVC framework.
032 */
033@Deprecated
034public class KualiTableRenderFormMetadata {
035    private int viewedPageNumber;
036    private int totalNumberOfPages;
037    private int firstRowIndex;
038    private int lastRowIndex;
039    private int switchToPageNumber;
040
041    /**
042     * The number of rows that match the query criteria
043     */
044    private int resultsActualSize;
045
046    /**
047     * The number of rows that match the query criteria or
048     *  the max results limit size (if applicable), whichever is less
049     */
050    private int resultsLimitedSize;
051
052    /**
053     * when the looked results screen was rendered, the index of the column that the results were sorted on.  -1 for unknown, index numbers
054     * starting at 0
055     */
056    private int previouslySortedColumnIndex;
057
058    /**
059     * Comment for <code>columnToSortIndex</code>
060     */
061    private int columnToSortIndex;
062
063    /**
064     * 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
065     */
066    private String columnToSortName;
067
068    /**
069     * When the screen was last rendered, the column name on which it was previously sorted -- this is important for toggling between ascending and descending
070     * sort orders
071     */
072    private String previouslySortedColumnName;
073
074    private boolean sortDescending;
075
076    public KualiTableRenderFormMetadata() {
077        sortDescending = false;
078    }
079
080    /**
081     * Gets the columnToSortIndex attribute.
082     * @return Returns the columnToSortIndex.
083     */
084    public int getColumnToSortIndex() {
085        return columnToSortIndex;
086    }
087
088    /**
089     * Sets the columnToSortIndex attribute value.
090     * @param columnToSortIndex The columnToSortIndex to set.
091     */
092    public void setColumnToSortIndex(int columnToSortIndex) {
093        this.columnToSortIndex = columnToSortIndex;
094    }
095
096    /**
097     * Gets the previouslySortedColumnIndex attribute.
098     * @return Returns the previouslySortedColumnIndex.
099     */
100    public int getPreviouslySortedColumnIndex() {
101        return previouslySortedColumnIndex;
102    }
103
104    /**
105     * Sets the previouslySortedColumnIndex attribute value.
106     * @param previouslySortedColumnIndex The previouslySortedColumnIndex to set.
107     */
108    public void setPreviouslySortedColumnIndex(int previouslySortedColumnIndex) {
109        this.previouslySortedColumnIndex = previouslySortedColumnIndex;
110    }
111
112    /**
113     * Gets the resultsActualSize attribute.
114     * @return Returns the resultsActualSize.
115     */
116    public int getResultsActualSize() {
117        return resultsActualSize;
118    }
119
120    /**
121     * Sets the resultsActualSize attribute value.
122     * @param resultsActualSize The resultsActualSize to set.
123     */
124    public void setResultsActualSize(int resultsActualSize) {
125        this.resultsActualSize = resultsActualSize;
126    }
127
128    /**
129     * Gets the resultsLimitedSize attribute.
130     * @return Returns the resultsLimitedSize.
131     */
132    public int getResultsLimitedSize() {
133        return resultsLimitedSize;
134    }
135
136    /**
137     * Sets the resultsLimitedSize attribute value.
138     * @param resultsLimitedSize The resultsLimitedSize to set.
139     */
140    public void setResultsLimitedSize(int resultsLimitedSize) {
141        this.resultsLimitedSize = resultsLimitedSize;
142    }
143
144    /**
145     * Gets the switchToPageNumber attribute.
146     * @return Returns the switchToPageNumber.
147     */
148    public int getSwitchToPageNumber() {
149        return switchToPageNumber;
150    }
151
152    /**
153     * Sets the switchToPageNumber attribute value.
154     * @param switchToPageNumber The switchToPageNumber to set.
155     */
156    public void setSwitchToPageNumber(int switchToPageNumber) {
157        this.switchToPageNumber = switchToPageNumber;
158    }
159
160    /**
161     * Gets the viewedPageNumber attribute.
162     * @return Returns the viewedPageNumber.
163     */
164    public int getViewedPageNumber() {
165        return viewedPageNumber;
166    }
167
168    /**
169     * Sets the viewedPageNumber attribute value.
170     * @param viewedPageNumber The viewedPageNumber to set.
171     */
172    public void setViewedPageNumber(int viewedPageNumber) {
173        this.viewedPageNumber = viewedPageNumber;
174    }
175
176    /**
177     * Gets the totalNumberOfPages attribute.
178     * @return Returns the totalNumberOfPages.
179     */
180    public int getTotalNumberOfPages() {
181        return totalNumberOfPages;
182    }
183
184    /**
185     * Sets the totalNumberOfPages attribute value.
186     * @param totalNumberOfPages The totalNumberOfPages to set.
187     */
188    public void setTotalNumberOfPages(int totalNumberOfPages) {
189        this.totalNumberOfPages = totalNumberOfPages;
190    }
191
192    /**
193     * Gets the firstRowIndex attribute.
194     * @return Returns the firstRowIndex.
195     */
196    public int getFirstRowIndex() {
197        return firstRowIndex;
198    }
199
200    /**
201     * Sets the firstRowIndex attribute value.
202     * @param firstRowIndex The firstRowIndex to set.
203     */
204    public void setFirstRowIndex(int firstRowIndex) {
205        this.firstRowIndex = firstRowIndex;
206    }
207
208    /**
209     * Gets the lastRowIndex attribute.
210     * @return Returns the lastRowIndex.
211     */
212    public int getLastRowIndex() {
213        return lastRowIndex;
214    }
215
216    /**
217     * Sets the lastRowIndex attribute value.
218     * @param lastRowIndex The lastRowIndex to set.
219     */
220    public void setLastRowIndex(int lastRowIndex) {
221        this.lastRowIndex = lastRowIndex;
222    }
223
224    /**
225     * Gets the sortDescending attribute.
226     * @return Returns the sortDescending.
227     */
228    public boolean isSortDescending() {
229        return sortDescending;
230    }
231
232    /**
233     * Sets the sortDescending attribute value.
234     * @param sortDescending The sortDescending to set.
235     */
236    public void setSortDescending(boolean sortDescending) {
237        this.sortDescending = sortDescending;
238    }
239
240        /**
241         * @return the columnToSortName
242         */
243        public String getColumnToSortName() {
244                return this.columnToSortName;
245        }
246
247        /**
248         * @param columnToSortName the columnToSortName to set
249         */
250        public void setColumnToSortName(String columnToSortName) {
251                this.columnToSortName = columnToSortName;
252        }
253
254        /**
255         * @return the previouslySortedColumnName
256         */
257        public String getPreviouslySortedColumnName() {
258                return this.previouslySortedColumnName;
259        }
260
261        /**
262         * @param previouslySortedColumnName the previouslySortedColumnName to set
263         */
264        public void setPreviouslySortedColumnName(String previouslySortedColumnName) {
265                this.previouslySortedColumnName = previouslySortedColumnName;
266        }
267
268
269    /**
270     * Sets the paging form parameters to go to the first page of the list
271     *
272     * @param listSize size of table being rendered
273     * @param maxRowsPerPage
274     */
275    public void jumpToFirstPage(int listSize, int maxRowsPerPage) {
276        jumpToPage(0, listSize, maxRowsPerPage);
277    }
278
279    /**
280     * Sets the paging form parameters to go to the last page of the list
281     *
282     * @param listSize size of table being rendered
283     * @param maxRowsPerPage
284     */
285    public void jumpToLastPage(int listSize, int maxRowsPerPage) {
286        jumpToPage(TableRenderUtil.computeTotalNumberOfPages(listSize, maxRowsPerPage) - 1, listSize, maxRowsPerPage);
287    }
288
289    /**
290     * Sets the paging form parameters to go to the specified page of the list
291     *
292     * @param pageNumber first page is 0, must be non-negative.  If the list is not large enough to have the page specified, then
293     *   this method will be equivalent to calling jumpToLastPage.
294     * @param listSize size of table being rendered
295     * @param maxRowsPerPage
296     *
297     * @see KualiTableRenderFormMetadata#jumpToLastPage(int, int)
298     */
299    public void jumpToPage(int pageNumber, int listSize, int maxRowsPerPage) {
300        int totalPages = TableRenderUtil.computeTotalNumberOfPages(listSize, maxRowsPerPage);
301        setTotalNumberOfPages(totalPages);
302        if (pageNumber >= totalPages) {
303            pageNumber = totalPages - 1;
304        }
305        setViewedPageNumber(pageNumber);
306        setFirstRowIndex(TableRenderUtil.computeStartIndexForPage(pageNumber, listSize, maxRowsPerPage));
307        setLastRowIndex(TableRenderUtil.computeLastIndexForPage(pageNumber, listSize, maxRowsPerPage));
308    }
309
310    /**
311     * Sorts a list on the form according to the form metadata (sortColumName, previouslySortedColumnName)
312     *
313     * @param memberTableMetadata
314     * @param items
315     * @param maxRowsPerPage
316     * @throws org.kuali.rice.kew.api.exception.WorkflowException
317     */
318    public void sort(List<?> items, int maxRowsPerPage) {
319
320        // Don't bother to sort null, empty or singleton lists
321        if (items == null || items.size() <= 1)
322                return;
323
324        String columnToSortOn = getColumnToSortName();
325
326        // Don't bother to sort if no column to sort on is provided
327        if (StringUtils.isEmpty(columnToSortOn))
328                return;
329
330        String previouslySortedColumnName = getPreviouslySortedColumnName();
331
332        // We know members isn't null or empty from the check above
333        Object firstItem = items.get(0);
334        // Need to decide if the comparator is for a bean property or a mapped key on the qualififer attribute set
335        Comparator comparator = null;
336        Comparator subComparator = new Comparator<Object>() {
337
338                public int compare(Object o1, Object o2) {
339                        if (o1 == null)
340                                return -1;
341                        if (o2 == null)
342                                return 1;
343
344                        if (o1 instanceof java.util.Date && o2 instanceof java.util.Date) {
345                                Date d1 = (Date)o1;
346                                Date d2 = (Date)o2;
347                                return d1.compareTo(d2);
348                        }
349
350                        String s1 = o1.toString();
351                        String s2 = o2.toString();
352                        int n1=s1.length(), n2=s2.length();
353                        for (int i1=0, i2=0; i1<n1 && i2<n2; i1++, i2++) {
354                                char c1 = s1.charAt(i1);
355                                char c2 = s2.charAt(i2);
356                                if (c1 != c2) {
357                                        c1 = Character.toUpperCase(c1);
358                                        c2 = Character.toUpperCase(c2);
359                                        if (c1 != c2) {
360                                                c1 = Character.toLowerCase(c1);
361                                                c2 = Character.toLowerCase(c2);
362                                                if (c1 != c2) {
363                                                        return c1 - c2;
364                                                }
365                                        }
366                                }
367                        }
368                        return n1 - n2;
369                }
370        };
371        // 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,
372        // otherwise it's a mapped property -- syntax for BeanComparator is "name" and "name(key)", respectively
373        if (PropertyUtils.isReadable(firstItem, columnToSortOn))
374                comparator = new BeanComparator(columnToSortOn, subComparator);
375        else
376                comparator = new BeanComparator(new StringBuilder().append("qualifierAsMap(").append(columnToSortOn).append(")").toString(), subComparator);
377
378
379        // 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
380        if (!StringUtils.isEmpty(columnToSortOn) && !StringUtils.isEmpty(previouslySortedColumnName) && columnToSortOn.equals(previouslySortedColumnName)) {
381            // we're already sorted on the same column that the user clicked on, so we reverse the list
382            if (isSortDescending())
383                comparator = Collections.reverseOrder(comparator);
384
385            setSortDescending(!isSortDescending());
386        } else {
387                // Track which column we're currently sorting, so that the above logic will work on the next sort
388                setPreviouslySortedColumnName(columnToSortOn);
389                setSortDescending(true);
390        }
391
392        //if the user is just going between pages no need to sort
393        if (getSwitchToPageNumber() == getViewedPageNumber()) {
394            Collections.sort(items, comparator);
395        }
396
397                jumpToFirstPage(items.size(), maxRowsPerPage);
398    }
399
400}