Source code: com/clra/web/YearViewSelectorTag.java
1 /*
2 * Copyright (c) Carnegie Lake Rowing Association 2002. All rights reserved.
3 * Distributed under the GPL license. See doc/COPYING.
4 * $RCSfile: YearViewSelectorTag.java,v $
5 * $Date: 2003/02/26 03:38:46 $
6 * $Revision: 1.3 $
7 */
8
9 package com.clra.web;
10
11 import java.io.IOException;
12 import java.io.Serializable;
13 import java.util.Calendar;
14 import java.util.GregorianCalendar;
15 import java.util.Hashtable;
16 import java.util.Iterator;
17 import javax.servlet.http.HttpServletRequest;
18 import javax.servlet.http.HttpServletResponse;
19 import javax.servlet.http.HttpUtils;
20 import javax.servlet.jsp.JspException;
21 import javax.servlet.jsp.JspWriter;
22 import javax.servlet.jsp.PageContext;
23 import javax.servlet.jsp.tagext.TagSupport;
24 import org.apache.log4j.Category;
25 import org.apache.struts.util.MessageResources;
26 import org.apache.struts.util.ResponseUtils;
27
28 /**
29 * Stuff.
30 *
31 * @author <a mailto:"rphall@pluto.njcc.com">Rick Hall</a>
32 * @version $Revision: 1.3 $ $Date: 2003/02/26 03:38:46 $
33 */
34 public class YearViewSelectorTag extends TagSupport {
35
36 private final static String base = YearViewSelectorTag.class.getName();
37 private final static Category theLog = Category.getInstance( base );
38
39 /** The message resources for this package */
40 protected static MessageResources messages =
41 MessageResources.getMessageResources("com.clra.web.clra");
42
43 /** The context-relative URI */
44 protected String page = null;
45
46 /** The currently selected year (4 digits) */
47 private Integer currentYear = null;
48
49 /** Return the context-relative URI */
50 public String getPage() {
51 return (this.page);
52 }
53
54 /** Set the context-relative URI */
55 public void setPage(String page) {
56 this.page = page;
57 }
58
59 /** Return the currently selected year (4 digits, or null) */
60 public Integer getCurrentYear() {
61 return this.currentYear;
62 }
63
64 /** Set the currently selected year (4 digits, or null) */
65 public void setCurrentYear( Integer currentYear ) throws JspException {
66 if ( currentYear != null ) {
67 if ( !hasFourDigits( currentYear ) ) {
68 String msg = messages.getMessage(
69 "yearselector.badcurrentYear", currentYear );
70 throw new JspException(msg);
71 }
72 }
73 this.currentYear = currentYear;
74 }
75
76 /**
77 * A slightly mis-named utility that checks if an Integer is
78 * <strong>positive</strong> and has four digits.
79 */
80 public static boolean hasFourDigits( Integer year ) {
81 if ( year == null ) {
82 throw new IllegalArgumentException( "null Integer" );
83 }
84 return hasFourDigits( year.intValue() );
85 }
86
87 /**
88 * A slightly mis-named utility that checks if an int is
89 * <strong>positive</strong> and has four digits.
90 */
91 public static boolean hasFourDigits( int year ) {
92 boolean retVal = 999 < year && year < 10000;
93 return retVal;
94 }
95
96 /**
97 * A utility which determines what year should be currently selected
98 * based on the page context. The algorithm is:<ol>
99 * <li>Check for a request parameter named IEventList.AN_YEAR. If
100 * found, and it is a valid Integer (4 digits), use it.</li>
101 * <li>Check for a request attribute named IEventList.AN_YEAR. If
102 * found, and it is valid, use it.</li>
103 * <li>Check for a session attribute named IEventList.AN_YEAR. If
104 * found, and it is valid, use it.</li>
105 * <li>If a valid year hasn't been determined yet, return the current
106 * calendar year</li>
107 * </ol>
108 */
109 public static Integer yearFromContext( PageContext context ) {
110
111 final String NAME = IEventList.AN_YEAR;
112 Integer retVal = null;
113
114 // Try request parameter first
115 try {
116 String s = context.getRequest().getParameter(NAME);
117 if ( s != null ) {
118 retVal = Integer.valueOf(s);
119 if ( !hasFourDigits( retVal ) ) {
120 throw new IllegalArgumentException( "bad year == " + retVal );
121 }
122 }
123 }
124 catch( Exception x ) {
125 String msg = "requestParameter: " + NAME + "; "
126 + x.getClass().getName() + ": " + x.getMessage();
127 theLog.error( msg, x );
128 }
129
130 // Try request attribute second
131 try {
132 if ( retVal == null ) {
133 Object o = context.getRequest().getAttribute(NAME);
134 if ( o != null && o instanceof Integer ) {
135 retVal = (Integer) o;
136 if ( !hasFourDigits( retVal ) ) {
137 throw new IllegalArgumentException( "bad year == " + retVal );
138 } // if invalid Integer
139 } // if o
140 } // if null retVal
141 } // try
142 catch( Exception x ) {
143 String msg = "requestAttribute: " + NAME + "; "
144 + x.getClass().getName() + ": " + x.getMessage();
145 theLog.error( msg, x );
146 }
147
148 // Try session attribute third
149 try {
150 if ( retVal == null ) {
151 Object o = context.getSession().getAttribute(NAME);
152 if ( o != null && o instanceof Integer ) {
153 retVal = (Integer) o;
154 if ( !hasFourDigits( retVal ) ) {
155 throw new IllegalArgumentException( "bad year == " + retVal );
156 } // if valid Integer
157 } // if o
158 } // if null retVal
159 } // try
160 catch( Exception x ) {
161 String msg = "sessionAttribute: " + NAME + "; "
162 + x.getClass().getName() + ": " + x.getMessage();
163 theLog.error( msg, x );
164 }
165
166 // Use the current year as a fallback
167 if ( retVal == null ) {
168 int year = new GregorianCalendar().get( Calendar.YEAR );
169 retVal = new Integer( year );
170 }
171
172 return retVal;
173 } // yearFromContext(PageContext)
174
175 /**
176 * A utility which resets request and session attributes so that only
177 * the session attribute AN_YEAR holds the currently selected year.
178 * The algorithm is:<ol>
179 * <li>Check for a request attribute named IEventList.AN_YEAR. If
180 * found, remove it.</li>
181 * <li>Check for a session attribute named IEventList.AN_YEAR. If
182 * found, reset to the specified value; otherwise create and set it.</li>
183 * </ol>
184 * If the specified year is invalid (not 4 digits),
185 * no action is taken and an error is logged.
186 */
187 public static void resetYearInContexts(PageContext context, Integer year) {
188
189 // Check the context and year.
190 // If either is invalid, log an error and null both as a flag.
191 if ( context == null || year == null || !hasFourDigits(year) ) {
192 theLog.error( "invalid context/year == " + context + "/" + year );
193 context = null;
194 year = null;
195 }
196
197 final String NAME = IEventList.AN_YEAR;
198
199 // Reset the request attribute
200 if ( context != null ) {
201 context.getRequest().removeAttribute(NAME);
202 }
203
204 // Set the session attribute
205 if ( context != null ) {
206 context.getSession().setAttribute(NAME,year);
207 }
208
209 return;
210 } // resetYearInContexts(PageContext,Integer)
211
212 /** Form a query string from current parameter and the specified year */
213 protected String createQueryString( int year, Hashtable queryParams ) {
214
215 // FIXME consolidate code with MonthViewSelectorTag.createQueryString(..)
216
217 // Preconditions
218 if ( !hasFourDigits(year) ) {
219 throw new IllegalArgumentException( "invalid year == " + year );
220 }
221 if ( queryParams == null ) {
222 throw new IllegalArgumentException( "null queryParams" );
223 }
224
225 StringBuffer sb = new StringBuffer();
226 sb.append( "?" );
227
228 // Overwrite existing query params related to this control
229 queryParams.put(
230 IEventList.AN_ISRESTRICTED, new Boolean(true).toString() );
231 queryParams.put(
232 IEventList.AN_YEAR, new Integer(year).toString() );
233
234 // Form the query string
235 boolean isFirst = true;
236 Iterator keys = queryParams.keySet().iterator();
237 while ( keys.hasNext() ) {
238
239 if ( isFirst ) {
240 isFirst = false;
241 }
242 else {
243 sb.append( "&" );
244 }
245
246 String key = (String) keys.next();
247 Object value = queryParams.get( key );
248 if ( value instanceof String ) {
249 sb.append( key + "=" + value );
250 }
251 else {
252 String[] values = (String[]) value;
253 for ( int i=0; i<values.length; i++ ) {
254 if ( i > 0 ) {
255 sb.append( "&" );
256 }
257 sb.append( key + "=" + values[i] );
258 } // for String[]
259 } // else String[]
260
261 } // while keys
262
263 return sb.toString();
264 } // createQueryString(int,Hashtable)
265
266 /** Create a link from the specified page */
267 protected String createLink( int year,
268 String contextPath, String page ) throws JspException {
269
270 // Preconditions
271 if ( contextPath == null ) {
272 throw new IllegalArgumentException( "null contextPath" );
273 }
274 if ( page == null ) {
275 throw new IllegalArgumentException( "null page" );
276 }
277
278 StringBuffer sb = new StringBuffer( contextPath );
279 sb.append(page);
280
281 Hashtable queryParams;
282 String tmp = sb.toString();
283 int idx = tmp.indexOf("?");
284 if ( idx > -1 && idx < page.length()-1 ) {
285 String s = tmp.substring( idx+1 );
286 queryParams = HttpUtils.parseQueryString(s);
287 }
288 else {
289 queryParams = new Hashtable();
290 }
291 String queryString = createQueryString( year, queryParams );
292
293 String url;
294 if ( idx < 0 ) {
295 url = tmp + "?";
296 }
297 else {
298 url = sb.substring( 0, idx );
299 }
300 url = url + queryString;
301
302 return url;
303 } // createLink(int,String,String)
304
305 /** Creates a link from the currentYear request */
306 protected String createLink( int year, HttpServletRequest request ) {
307
308 StringBuffer url = HttpUtils.getRequestURL( request );
309
310 String s = request.getQueryString();
311 Hashtable queryParams;
312 if ( s != null ) {
313 queryParams = HttpUtils.parseQueryString(s);
314 }
315 else {
316 queryParams = new Hashtable();
317 }
318
319 String queryString = createQueryString( year, queryParams );
320 url.append( queryString );
321
322 return url.toString();
323 } // createLink(int,HttpServletRequest)
324
325 protected void doStartLink( int year ) throws JspException {
326
327 // Create a link
328 HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
329 String url = null;
330 if ( this.page != null ) {
331 // Use the specified page
332 String contextPath = request.getContextPath();
333 url = createLink( year, contextPath, page );
334 }
335 else {
336 // Link back to the currentYear page by default
337 url = createLink( year, request );
338 }
339
340 // Generate the hyperlink start element
341 HttpServletResponse response =
342 (HttpServletResponse) pageContext.getResponse();
343 StringBuffer results = new StringBuffer("<a href=\"");
344 results.append( response.encodeURL(url) );
345 results.append("\">");
346
347 // Print this element to our output writer
348 JspWriter writer = pageContext.getOut();
349 try {
350 writer.print(results.toString());
351 } catch (IOException e) {
352 throw new JspException
353 (messages.getMessage("yearselector.io", e.toString()));
354 }
355
356 return;
357 } // doStartLink(int)
358
359 protected void doEndLink() throws JspException {
360
361 // Print the ending element to our output writer
362 JspWriter writer = pageContext.getOut();
363 try {
364 writer.print("</a>");
365 } catch (IOException e) {
366 throw new JspException
367 (messages.getMessage("yearselector.io", e.toString()));
368 }
369
370 return;
371 } // doEndLink()
372
373 /** Writes a Calendar-based year (4 digits) as user-friendly text */
374 protected void doYearText( int year ) throws JspException {
375
376 JspWriter writer = pageContext.getOut();
377 try {
378 writer.print("" + year );
379 } catch (IOException e) {
380 throw new JspException(
381 messages.getMessage("yearselector.io", e.toString()) );
382 }
383
384 return;
385 } // doYearText(int)
386
387 /** Writes the text that separates year links */
388 protected void doYearLeadingSeparationText(int unused) throws JspException{
389
390 JspWriter writer = pageContext.getOut();
391 try {
392 writer.print( " " );
393 } catch (IOException e) {
394 throw new JspException(
395 messages.getMessage("yearselector.io", e.toString()) );
396 }
397
398 return;
399 } // doYearLeadingSeparationText(int)
400
401 /** Writes the text that separates year links */
402 protected void doYearTrailingSeparationText(int unused) throws JspException{
403
404 JspWriter writer = pageContext.getOut();
405 try {
406 writer.print( " " );
407 } catch (IOException e) {
408 throw new JspException(
409 messages.getMessage("yearselector.io", e.toString()) );
410 }
411
412 return;
413 } // doYearTrailingSeparationText(int)
414
415 /** Write a year link */
416 protected void doYearLink( int year ) throws JspException {
417
418 doYearLeadingSeparationText( year );
419 if ( currentYear == null ) {
420 doStartLink( year );
421 }
422 else if ( currentYear != null && year != currentYear.intValue() ) {
423 doStartLink( year );
424 }
425 doYearText( year );
426 if ( currentYear == null ) {
427 doEndLink();
428 }
429 else if ( currentYear != null && year != currentYear.intValue() ) {
430 doEndLink();
431 }
432 doYearTrailingSeparationText( year );
433
434 return;
435 } // doYearLink()
436
437 /** Writes several year links */
438 protected void doYearLinks() throws JspException {
439
440 // Preconditions
441 if ( currentYear == null ) {
442 throw new IllegalStateException( "current year is null" );
443 }
444 final int cy = currentYear.intValue();
445
446 for ( int year=cy-1; year<cy+2; year++ ) {
447 doYearLink( year );
448 }
449
450 return;
451 } // doYearLinks(int)
452
453 /** Render the beginning of the selector */
454 public int doStartTag() throws JspException {
455 return (EVAL_BODY_INCLUDE);
456 }
457
458 /** Render the end of the selector */
459 public int doEndTag() throws JspException {
460 doYearLinks();
461 return (EVAL_PAGE);
462 }
463
464 /**
465 * Release any acquired resources.
466 */
467 public void release() {
468 super.release();
469 this.page = null;
470 return;
471 }
472
473 } // YearViewSelector
474
475 /*
476 * $Log: YearViewSelectorTag.java,v $
477 * Revision 1.3 2003/02/26 03:38:46 rphall
478 * Added copyright and GPL license
479 *
480 * Revision 1.2 2002/02/18 18:07:33 rphall
481 * Ran dos2unix to remove ^M (carriage return) from end of lines
482 *
483 * Revision 1.1.1.1 2002/01/03 21:57:28 rphall
484 * Initial load, 5th try, Jan-03-2002 4:57 PM
485 *
486 * Revision 1.2 2002/01/01 03:40:33 rphall
487 * Moved getName() from MemberName to MemberView
488 *
489 * Revision 1.1 2001/12/14 00:47:33 rphall
490 * YearViewSelectorTag JSP tag controls year display
491 *
492 */
493
494