Source code: jreceiver/util/taglibs/PageNavigationTag.java
1 /* $Header: /cvsroot/jreceiver/jreceiver/src/jreceiver/util/taglibs/PageNavigationTag.java,v 1.5 2002/07/31 11:29:43 reedesau Exp $ */
2
3 package jreceiver.util.taglibs;
4
5 import java.io.IOException;
6 import java.text.MessageFormat;
7 import javax.servlet.jsp.*;
8 import javax.servlet.jsp.tagext.*;
9
10 import org.apache.commons.logging.*;
11
12 /**
13 * JSP Tag <b>pageNavigation</b>, used to display an array of
14 * page numbers to traverse a large set of data.
15 * <p>
16 * Required attributes are <b>total_pages</b> and <b>page_no</b>.
17 *
18 * <b>url_pattern</b> is optional and defaults to page{0}.html which
19 * produces page1.html, page2.html, etc. It uses a MessageFormat
20 * pattern where {0} is replaced by the page number (pageNo) and {1}
21 * is replaced by the page count (totalPages).
22 * <p>
23 * Example of default output for page numbers 1 through 16:<br>
24 * <pre>
25 * <<- 1 2 3 4 .. 16 ->>
26 * <<- 1 2 3 4 5 .. 16 ->>
27 * <<- 1 2 3 4 5 6 .. 16 ->>
28 * <<- 1 2 3 4 5 6 7 .. 16 ->>
29 * <<- 1 2 3 4 5 6 7 8 .. 16 ->>
30 * <<- 1 2 3 4 5 6 7 8 9 .. 16 ->>
31 * <<- 1 .. 4 5 6 7 8 9 10 .. 16 ->>
32 * <<- 1 .. 5 6 7 8 9 10 11 .. 16 ->>
33 * <<- 1 .. 6 7 8 9 10 11 12 .. 16 ->>
34 * <<- 1 .. 7 8 9 10 11 12 13 .. 16 ->>
35 * <<- 1 .. 8 9 10 11 12 13 14 15 16 ->>
36 * <<- 1 .. 9 10 11 12 13 14 15 16 ->>
37 * <<- 1 .. 10 11 12 13 14 15 16 ->>
38 * <<- 1 .. 11 12 13 14 15 16 ->>
39 * <<- 1 .. 12 13 14 15 16 ->>
40 * <<- 1 .. 13 14 15 16 ->>
41 * </pre>
42 * <p>
43 * Example usage:
44 * <p>
45 * <pre>
46 * <jrec:pageNavigation pageNo="1"
47 * totalPages="16"
48 * urlPattern="food{0}.jsp?pages={1}"
49 * prev="&laquo;" next="&raquo;"/>
50 * </pre>
51 *
52 * @author Reed Esau
53 * @version $Revision: 1.5 $ $Date: 2002/07/31 11:29:43 $
54 */
55 public class PageNavigationTag extends TagSupport {
56
57 private static final int DEFAULT_BLOCK_SIZE = 7;
58 private static final int DEFAULT_THRESHOLD = 4;
59 private static final String DEFAULT_PREV = "<<-";
60 private static final String DEFAULT_NEXT = "->>";
61 private static final String DEFAULT_URL_PATTERN = "page{0}.html"; // where {0} is the replaceable param
62
63 /**
64 * The current page number, 1-indexed
65 */
66 private int page_no = 0;
67 /**
68 * The size of the floating block of numbers, with (block_size/2)
69 * number on either side of the current page no.
70 * <p>
71 * Must be an odd number, three or greater.
72 */
73 private int block_size = DEFAULT_BLOCK_SIZE;
74 /**
75 * The point at which diresis (sp?) will begin to show up on either
76 * side of the list of numbers as the floating block moves away from
77 * the edge.
78 * <p>
79 * Must be greater than 3.
80 */
81 private int threshold = DEFAULT_THRESHOLD;
82 /**
83 * The string (or image) to represent the left arrow
84 */
85 private String prev = DEFAULT_PREV;
86 /**
87 * The string (or image) to represent the right arrow
88 */
89 private String next = DEFAULT_NEXT;
90 /**
91 * The number of pages we provide navigation over
92 */
93 private int total_pages = 0;
94 /**
95 * The template used to generate links for each page number we display
96 */
97 private MessageFormat url_format = null;
98
99 /**
100 * The default text if the tag or number given is invalid/null
101 */
102 private String default_text = "Invalid navigation";
103
104 /**
105 * Method called at start of tag -- extract attributes
106 */
107 public final int doStartTag() throws JspException {
108 if (log.isDebugEnabled())
109 log.debug("doStartTag, page_no=" + page_no + " total_pages=" + total_pages);
110
111 if ( page_no == 0 ) {
112 Object attr = pageContext.findAttribute("pageNo");
113 if ( attr == null )
114 throw new JspException("pageNo is a required attribute");
115 try {
116 if (log.isDebugEnabled())
117 log.debug("getting page_no: " + attr.toString());
118 page_no = Integer.parseInt( attr.toString() );
119 }
120 catch (NumberFormatException nfe) {
121 throw new JspException("number problem with pageNo: " + nfe.getMessage());
122 }
123 }
124
125 if ( total_pages == 0 ) {
126 Object attr = pageContext.findAttribute("totalPages");
127 if ( attr == null )
128 throw new JspException("totalPages is a required attribute");
129 try {
130 total_pages = Integer.parseInt( attr.toString() );
131 }
132 catch (NumberFormatException nfe) {
133 throw new JspException("number problem with totalPages: " + nfe.getMessage());
134 }
135 }
136
137 if ( url_format == null ) {
138 Object attr = pageContext.findAttribute("urlPattern");
139 if ( attr != null )
140 url_format = new MessageFormat(attr.toString());
141 else
142 url_format = new MessageFormat(DEFAULT_URL_PATTERN);
143 }
144
145 return SKIP_BODY;
146 }
147
148 /**
149 * Method called at end of Tag
150 *
151 * @return EVAL_PAGE
152 */
153 public final int doEndTag() throws JspException {
154
155 if (log.isDebugEnabled())
156 log.debug("doEndTag: page_no=" + page_no
157 + " total_pages=" + total_pages);
158
159 if (page_no <= 0 || total_pages < 0)
160 throw new JspException("bad pageNo or totalPages");
161
162 if (total_pages < 2) // don't show navigation if user can't do anything
163 return EVAL_PAGE;
164
165 try {
166 JspWriter writer = pageContext.getOut();
167
168 // show left arrow
169 if (prev != null) {
170 if (page_no == 1)
171 writer.write(prev);
172 else
173 writer.write(getHref(page_no-1, prev));
174 writer.write(" ");
175 }
176
177 // determine range
178 int min, max;
179 {
180 int left = page_no - (block_size/2);
181 int right = page_no + (block_size/2);
182
183 if (left < threshold) { // if far left
184 min = 1;
185 max = right < total_pages ? right : total_pages;
186 }
187 else if (right > total_pages - threshold + 1) { // if far right
188 min = left;
189 max = total_pages;
190 }
191 else {
192 min = left;
193 max = right;
194 }
195
196 //max = right < total_pages ? right : total_pages;
197 }
198
199 if (log.isDebugEnabled())
200 log.debug("doEndTag: min=" + min + " max=" + max);
201
202 // show first page no
203 if (page_no == 1)
204 writer.write("<b>1</b>");
205 else
206 writer.write(getHref(1, new Integer(1)));
207 writer.write(" ");
208
209 // show diresis (sp?) if necessary
210 if ( min > 1 )
211 writer.write(".. ");
212
213 // show array of page nos
214 for (int i = min; i <= max; i++) {
215
216 if (i == 1 || i == total_pages)
217 continue; // we do these anyway
218
219 if (i == page_no)
220 writer.write("<b>" + i + "</b>");
221 else
222 writer.write(getHref(i, new Integer(i)));
223 writer.write(" "); // space them
224 }
225
226 // show diresis (sp?) if necessary
227 if ( max < total_pages )
228 writer.write(".. ");
229
230 // show last page no, if not the same as first
231 if (min != max) {
232 if (page_no == total_pages)
233 writer.write("<b>" + total_pages + "</b>");
234 else
235 writer.write(getHref(total_pages, new Integer(total_pages)));
236 writer.write(" ");
237 }
238
239 // show right arrow
240 if (next != null) {
241 if (page_no == total_pages)
242 writer.write(next);
243 else
244 writer.write(getHref(page_no+1, next));
245 }
246 }
247 catch (IOException e) {
248 throw new JspException("io-problem writing navigation tag: " + e.getMessage());
249 }
250
251 return EVAL_PAGE;
252 }
253
254
255 /**
256 * Do a MessageFormat of the url_pattern with the following
257 * optional parameters:
258 * <p>
259 * <pre>
260 * {0} the page number
261 * {1} total pages
262 * </pre>
263 * <p>
264 *
265 * @param param
266 * @return
267 */
268 private String getHref(int page_no, Object displayed) {
269
270 StringBuffer buf = new StringBuffer(32);
271 Object[] params = new Object[2];
272 params[0] = Integer.toString(page_no); // pass as a string to avoid "6,666" formatting
273 params[1] = Integer.toString(total_pages); // ...
274
275 buf.append("<a href='")
276 .append( url_format.format(params) )
277 .append("'>")
278 .append(displayed)
279 .append("</a>");
280
281 return buf.toString();
282 }
283
284
285 /**
286 * Assign the threshold
287 */
288 public final void setThreshold(int threshold) throws JspException {
289 log.debug("setThreshold");
290 if (threshold < 3)
291 throw new JspException("threshold must be 3 or greater");
292 this.threshold = threshold;
293 }
294
295 /**
296 * Assign the block size
297 */
298 public final void setBlockSize(int block_size) throws JspException {
299 log.debug("setBlockSize");
300 if (block_size < 3 || (block_size % 2 != 1))
301 throw new JspException("blockSize must be an odd number 3 or greater");
302 this.block_size = block_size;
303 }
304
305 /**
306 * Set the left arrow text or image
307 */
308 public final void setPrev(String prev) {
309 log.debug("setPrev");
310 this.prev = prev;
311 }
312
313 /**
314 * Set the right arrow text or image
315 */
316 public final void setNext(String next) {
317 log.debug("setNext");
318 this.next = next;
319 }
320
321 /**
322 * Assign the current page number
323 */
324 public final void setPageNo(int page_no) throws JspException {
325 if (log.isDebugEnabled())
326 log.debug("setPageNo: " + page_no);
327 if (page_no < 1)
328 throw new JspException("page number must be 1 or greater");
329 this.page_no = page_no;
330 }
331
332 /**
333 * Assign the total pages
334 */
335 public final void setTotalPages(int total_pages) throws JspException {
336 log.debug("setTotalPages");
337 if (total_pages < 1)
338 throw new JspException("total pages must be 1 or greater");
339 this.total_pages = total_pages;
340 }
341
342 /**
343 * Set the url template
344 */
345 public final void setUrlPattern(String url_pattern) {
346 log.debug("setUrlTemplate");
347 this.url_format = new MessageFormat(url_pattern);
348 }
349
350 /**
351 * Set the default text if an invalid number or no tag body is given
352 *
353 * @param String to use as default text
354 */
355 public final void setDefault(String default_text) {
356 log.debug("setDefault");
357 this.default_text = default_text;
358 }
359
360
361 /**
362 * logging sink
363 */
364 protected static Log log = LogFactory.getLog(PageNavigationTag.class);
365 }
366 /*
367 JRECEIVER MODIFIED BSD LICENSE
368
369 Copyright (c) 2001-2002, Reed Esau (reed.esau@pobox.com) All rights reserved.
370
371 Redistribution and use in source and binary forms, with or without
372 modification, are permitted provided that the following conditions are
373 met:
374
375 Redistributions of source code must retain the above copyright notice,
376 this list of conditions and the following disclaimer.
377
378 Redistributions in binary form must reproduce the above copyright notice,
379 this list of conditions and the following disclaimer in the documentation
380 and/or other materials provided with the distribution.
381
382 Neither the name of the JReceiver Project
383 (http://jreceiver.sourceforge.net) nor the names of its contributors may
384 be used to endorse or promote products derived from this software without
385 specific prior written permission.
386
387 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
388 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
389 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
390 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
391 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
392 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
393 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
394 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
395 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
396 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
397 POSSIBILITY OF SUCH DAMAGE.
398 */
399