1 /**
2 * Licensed under the Artistic License; you may not use this file
3 * except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://displaytag.sourceforge.net/license.html
7 *
8 * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12 package org.displaytag.pagination;
13
14 import java.text.MessageFormat;
15 import java.util.List;
16
17 import org.apache.commons.lang.builder.ToStringBuilder;
18 import org.apache.commons.lang.builder.ToStringStyle;
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.displaytag.Messages;
22 import org.displaytag.properties.TableProperties;
23 import org.displaytag.util.Href;
24
25
26 /**
27 * <p>
28 * Utility class that chops up a List of objects into small bite size pieces that are more suitable for display.
29 * </p>
30 * <p>
31 * This class is a stripped down version of the WebListHelper from Tim Dawson (tdawson@is.com)
32 * </p>
33 * @author epesh
34 * @author Fabrizio Giustina
35 * @version $Revision: 1109 $ ($Author: fgiust $)
36 */
37 public class SmartListHelper
38 {
39
40 /**
41 * logger.
42 */
43 private static Log log = LogFactory.getLog(SmartListHelper.class);
44
45 /**
46 * full list.
47 */
48 private List fullList;
49
50 /**
51 * sixe of the full list.
52 */
53 private int fullListSize;
54
55 /**
56 * number of items in a page.
57 */
58 private int pageSize;
59
60 /**
61 * number of pages.
62 */
63 private int pageCount;
64
65 /**
66 * the list we hold is only part of the full dataset
67 */
68 private boolean partialList;
69
70 /**
71 * index of current page.
72 */
73 private int currentPage;
74
75 /**
76 * TableProperties.
77 */
78 private TableProperties properties;
79
80 /**
81 * Creates a SmarListHelper instance that will help you chop up a list into bite size pieces that are suitable for
82 * display.
83 * @param list List
84 * @param fullSize size of the full list
85 * @param itemsInPage number of items in a page (int > 0)
86 * @param tableProperties TableProperties
87 */
88 public SmartListHelper(
89 List list,
90 int fullSize,
91 int itemsInPage,
92 TableProperties tableProperties,
93 boolean partialList)
94 {
95 if (log.isDebugEnabled())
96 {
97 log.debug(Messages.getString("SmartListHelper.debug.instantiated", //$NON-NLS-1$
98 new Object[]{new Integer(list.size()), new Integer(itemsInPage), new Integer(fullSize)}));
99 }
100
101 this.properties = tableProperties;
102 this.pageSize = itemsInPage;
103 this.fullList = list;
104 this.fullListSize = fullSize;
105 this.pageCount = computedPageCount();
106 this.currentPage = 1;
107 this.partialList = partialList;
108 }
109
110 /**
111 * Constructor that can be used by subclasses. Subclasses that use this constructor must also override all the
112 * public methods, since this constructor does nothing.
113 */
114 protected SmartListHelper()
115 {
116 }
117
118 /**
119 * Returns the computed number of pages it would take to show all the elements in the list given the pageSize we are
120 * working with.
121 * @return int computed number of pages
122 */
123 protected int computedPageCount()
124 {
125 int size = this.fullListSize;
126 int div = size / this.pageSize;
127 int result = (size % this.pageSize == 0) ? div : div + 1;
128
129 return result;
130 }
131
132 /**
133 * Returns the index into the master list of the first object that should appear on the current page that the user
134 * is viewing.
135 * @return int index of the first object that should appear on the current page
136 */
137 public int getFirstIndexForCurrentPage()
138 {
139 return getFirstIndexForPage(this.currentPage);
140 }
141
142 /**
143 * Returns the index into the master list of the last object that should appear on the current page that the user is
144 * viewing.
145 * @return int
146 */
147 protected int getLastIndexForCurrentPage()
148 {
149
150 return getLastIndexForPage(this.currentPage);
151 }
152
153 /**
154 * Returns the index into the master list of the first object that should appear on the given page.
155 * @param pageNumber page number
156 * @return int index of the first object that should appear on the given page
157 */
158 protected int getFirstIndexForPage(int pageNumber)
159 {
160 if (this.partialList)
161 {
162 return 0;
163 }
164 else
165 {
166 int retval = (pageNumber - 1) * this.pageSize;
167 return retval >= 0 ? retval : 0;
168 }
169 }
170
171 /**
172 * Returns the index into the master list of the last object that should appear on the given page.
173 * @param pageNumber page number
174 * @return int index of the last object that should appear on the given page
175 */
176 protected int getLastIndexForPage(int pageNumber)
177 {
178 if (this.partialList)
179 {
180 // return the min of pageSize or list size on the off chance they gave us more data than pageSize allows
181 return Math.min(this.pageSize - 1, this.fullList.size() - 1);
182 }
183 else
184 {
185 int firstIndex = getFirstIndexForPage(pageNumber);
186 int pageIndex = this.pageSize - 1;
187 int lastIndex = this.fullListSize - 1;
188
189 return Math.min(firstIndex + pageIndex, lastIndex);
190 }
191 }
192
193 /**
194 * Returns a subsection of the list that contains just the elements that are supposed to be shown on the current
195 * page the user is viewing.
196 * @return List subsection of the list that contains the elements that are supposed to be shown on the current page
197 */
198 public List getListForCurrentPage()
199 {
200
201 return getListForPage(this.currentPage);
202 }
203
204 /**
205 * Returns a subsection of the list that contains just the elements that are supposed to be shown on the given page.
206 * @param pageNumber page number
207 * @return List subsection of the list that contains just the elements that are supposed to be shown on the given
208 * page
209 */
210 protected List getListForPage(int pageNumber)
211 {
212 if (log.isDebugEnabled())
213 {
214 log.debug(Messages.getString("SmartListHelper.debug.sublist", //$NON-NLS-1$
215 new Object[]{new Integer(pageNumber)}));
216 }
217
218 int firstIndex = getFirstIndexForPage(pageNumber);
219 int lastIndex = getLastIndexForPage(pageNumber);
220 return this.fullList.subList(firstIndex, lastIndex + 1);
221 }
222
223 /**
224 * Set's the page number that the user is viewing.
225 * @param pageNumber page number
226 */
227 public void setCurrentPage(int pageNumber)
228 {
229 if (log.isDebugEnabled())
230 {
231 log.debug(Messages.getString("SmartListHelper.debug.currentpage", //$NON-NLS-1$
232 new Object[]{new Integer(pageNumber), new Integer(this.pageCount)}));
233 }
234
235 if (pageNumber < 1)
236 {
237 // invalid page: better don't throw an exception, since this could easily happen
238 // (list changed, user bookmarked the page)
239 this.currentPage = 1;
240 }
241 else if (pageNumber != 1 && pageNumber > this.pageCount)
242 {
243 // invalid page: set to last page
244 this.currentPage = this.pageCount;
245 }
246 else
247 {
248 this.currentPage = pageNumber;
249 }
250 }
251
252 /**
253 * Return the little summary message that lets the user know how many objects are in the list they are viewing, and
254 * where in the list they are currently positioned. The message looks like: nnn [item(s)] found, displaying nnn to
255 * nnn. [item(s)] is replaced by either itemName or itemNames depending on if it should be signular or plural.
256 * @return String
257 */
258 public String getSearchResultsSummary()
259 {
260
261 Object[] objs;
262 String message;
263
264 if (this.fullListSize == 0)
265 {
266 objs = new Object[]{this.properties.getPagingItemsName()};
267 message = this.properties.getPagingFoundNoItems();
268
269 }
270 else if (this.fullListSize == 1)
271 {
272 objs = new Object[]{this.properties.getPagingItemName()};
273 message = this.properties.getPagingFoundOneItem();
274 }
275 else if (computedPageCount() == 1)
276 {
277 objs = new Object[]{
278 new Integer(this.fullListSize),
279 this.properties.getPagingItemsName(),
280 this.properties.getPagingItemsName()};
281 message = this.properties.getPagingFoundAllItems();
282 }
283 else
284 {
285 objs = new Object[]{
286 new Integer(this.fullListSize),
287 this.properties.getPagingItemsName(),
288 new Integer(getFirstIndexForCurrentPage() + 1),
289 new Integer(getLastIndexForCurrentPage() + 1),
290 new Integer(this.currentPage),
291 new Integer(this.pageCount)};
292 message = this.properties.getPagingFoundSomeItems();
293 }
294
295 return MessageFormat.format(message, objs);
296 }
297
298 /**
299 * Returns a string containing the nagivation bar that allows the user to move between pages within the list. The
300 * urlFormatString should be a URL that looks like the following: somepage.page?page={0}
301 * @param baseHref Href used for links
302 * @param pageParameter name for the page parameter
303 * @return String
304 */
305 public String getPageNavigationBar(Href baseHref, String pageParameter)
306 {
307
308 int groupSize = this.properties.getPagingGroupSize();
309 int startPage;
310 int endPage;
311
312 Pagination pagination = new Pagination(baseHref, pageParameter);
313 pagination.setCurrent(new Integer(this.currentPage));
314
315 // if no items are found still add pagination?
316 if (this.pageCount == 0)
317 {
318 pagination.addPage(1, true);
319 }
320
321 // center the selected page, but only if there are {groupSize} pages available after it, and check that the
322 // result is not < 1
323 startPage = Math.max(Math.min(this.currentPage - groupSize / 2, this.pageCount - (groupSize - 1)), 1);
324 endPage = Math.min(startPage + groupSize - 1, this.pageCount);
325
326 if (log.isDebugEnabled())
327 {
328 log.debug("Displaying pages from " + startPage + " to " + endPage);
329 }
330
331 if (this.currentPage != 1)
332 {
333 pagination.setFirst(new Integer(1));
334 pagination.setPrevious(new Integer(this.currentPage - 1));
335 }
336
337 for (int j = startPage; j <= endPage; j++)
338 {
339 if (log.isDebugEnabled())
340 {
341 log.debug("adding page " + j); //$NON-NLS-1$
342 }
343 pagination.addPage(j, (j == this.currentPage));
344 }
345
346 if (this.currentPage != this.pageCount)
347 {
348 pagination.setNext(new Integer(this.currentPage + 1));
349 pagination.setLast(new Integer(this.pageCount));
350 }
351
352 // format for previous/next banner
353 String bannerFormat;
354
355 if (pagination.isOnePage())
356 {
357 bannerFormat = this.properties.getPagingBannerOnePage();
358 }
359 else if (pagination.isFirst())
360 {
361 bannerFormat = this.properties.getPagingBannerFirst();
362 }
363 else if (pagination.isLast())
364 {
365 bannerFormat = this.properties.getPagingBannerLast();
366 }
367 else
368 {
369 bannerFormat = this.properties.getPagingBannerFull();
370 }
371
372 return pagination.getFormattedBanner(this.properties.getPagingPageLink(), this.properties
373 .getPagingPageSelected(), this.properties.getPagingPageSeparator(), bannerFormat);
374 }
375
376 /**
377 * @see java.lang.Object#toString()
378 */
379 public String toString()
380 {
381 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) //
382 .append("fullList", this.fullList) //$NON-NLS-1$
383 .append("fullListSize", this.fullListSize) //$NON-NLS-1$
384 .append("pageSize", this.pageSize) //$NON-NLS-1$
385 .append("pageCount", this.pageCount) //$NON-NLS-1$
386 .append("properties", this.properties) //$NON-NLS-1$
387 .append("currentPage", this.currentPage) //$NON-NLS-1$
388 .append("partialList", this.partialList) //$NON-NLS-1$
389 .toString();
390 }
391 }