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