KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > beehive > netui > tags > html > CheckBoxGroup


1 /*
2  * Copyright 2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * $Header:$
17  */

18 package org.apache.beehive.netui.tags.html;
19
20 import org.apache.beehive.netui.util.internal.InternalStringBuilder;
21
22 import org.apache.beehive.netui.pageflow.ProcessPopulate;
23 import org.apache.beehive.netui.script.common.DataAccessProviderStack;
24 import org.apache.beehive.netui.tags.naming.FormDataNameInterceptor;
25 import org.apache.beehive.netui.tags.naming.IndexedNameInterceptor;
26 import org.apache.beehive.netui.tags.naming.PrefixNameInterceptor;
27 import org.apache.beehive.netui.tags.rendering.*;
28 import org.apache.beehive.netui.util.Bundle;
29 import org.apache.beehive.netui.util.iterator.IteratorFactory;
30 import org.apache.beehive.netui.util.logging.Logger;
31 import org.apache.beehive.netui.util.tags.GroupOption;
32
33 import javax.servlet.ServletRequest JavaDoc;
34 import javax.servlet.http.HttpServletRequest JavaDoc;
35 import javax.servlet.jsp.JspException JavaDoc;
36 import java.util.*;
37
38 /**
39  * Groups a collection of CheckBoxOptions, and handles databinding of their values.
40  *
41  * CheckBoxGroup binds to an Iterator of Strings.
42  *
43  * If CheckBoxGroup uses any Format tags, it must have those tags come before any nested
44  * CheckBoxOption tags.
45  * @jsptagref.tagdescription Renders a collection of checkbox options as <input type="checkbox">
46  * and handles the data binding.
47  *
48  * <p><b>Submitting Data</b></p>
49  * <p>The &lt;netui:checkBoxGroup> submits data in the form of a String[] object.
50  * For example, if the &lt;netui:checkBoxGroup> submits data to a Form Bean field...
51  *
52  * <pre> &lt;netui:checkBoxGroup
53  * dataSource="actionForm.userSelections"
54  * optionsDataSource="${pageFlow.availableSelections}" /></pre>
55  *
56  * ...then the Form Bean field must be a String[] object...
57  *
58  * <pre> public static class SubmitForm extends FormData
59  * {
60  * private String[] userSelections;
61  *
62  * public void setUserSelections(String[] userSelections)
63  * {
64  * this.userSelections = userSelections;
65  * }
66  *
67  * public String[] getUserSelections()
68  * {
69  * return this.userSelections;
70  * }
71  * }</pre>
72  *
73  * <p><b>Dynamically Defined Checkboxes</b></p>
74  * You can dynamically define a set of checkboxes by pointing the <code>optionsDataSource</code> attribute
75  * at a String[] object. When the &lt;netui:checkBoxGroup> is rendered in the browser, a
76  * corresponding set of
77  * checkboxes will be genereated from the String[] object.
78  *
79  * <p>For example, if you define a String[] object and get method in the Controller file...
80  *
81  * <pre> public String[] availableOptions = {"option1", "option2", "option3"};
82  *
83  * public String[] getAvailableOptions()
84  * {
85  * return this.availableOptions;
86  * }</pre>
87  *
88  * ...and reference this String[] from the <code>optionsDataSource</code> attribute...
89  *
90  * <pre> &lt;netui:checkBoxGroup
91  * dataSource="actionForm.userSelections"
92  * optionsDataSource="${pageFlow.availableSelections}" /></pre>
93  *
94  * ...then the appropriate checkboxes will be rendered in the browser.
95  *
96  * <pre> &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.userSelections}" value="option1">option1&lt;/input>
97  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.userSelections}" value="option2">option2&lt;/input>
98  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.userSelections}" value="option3">option3&lt;/input></pre>
99  *
100  * For checkboxes to be rendered, either the <code>optionsDataSource</code> attribute must be provided
101  * (and point to a String[] object) or the &lt;netui:checkBoxGroup> must have children
102  * &lt;netuiCheckBoxOption> tags.
103  *
104  * <p><b>Setting Default Options</b></p>
105  * <p>The <code>defaultValue</code> attribute can be used to determine which checkboxs are checked
106  * when they are first rendered in the browser. The <code>defaultValue</code> attribute
107  * should point to a String, if only one checkbox should appear checked, or to a String[] object,
108  * if multiple checkboxes should appear checked.
109  * @example In this first sample, the &lt;netui:checkBoxGroup>
110  * submits data to the Form Bean field <code>preferredColors</code>.
111  *
112  * <pre> &lt;netui:checkBoxGroup
113  * dataSource="actionForm.preferredColors"
114  * optionsDataSource="${pageFlow.colors}" /></pre>
115  *
116  * The <code>optionsDataSource</code> attribute points to a get method for a String[] on the Controller file:
117  *
118  * <pre> String[] colors = new String[] {"Red", "Blue", "Green", "Yellow", "White", "Black"};
119  *
120  * public String[] getColors()
121  * {
122  * return colors;
123  * }</pre>
124  *
125  * This automatically renders the appropriate set of checkbox options within the &lt;checkBoxGroup>:
126  *
127  * <pre> &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="Red">Red&lt;/input>
128  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="Blue">Blue&lt;/input>
129  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="Green">Green&lt;/input>
130  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="Yellow">Yellow&lt;/input>
131  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="White">White&lt;/input>
132  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="Black">Black&lt;/input></pre>
133  *
134  * The <code>defaultValue</code> attribute may point to a String or a String[].
135  *
136  * <pre> &lt;netui:checkBoxGroup
137  * dataSource="actionForm.preferredColors"
138  * optionsDataSource="${pageFlow.colors}"
139  * defaultValue="${pageFlow.defaultColor}" /></pre>
140  *
141  * And in the Controller:
142  * <pre> String defaultColor = new String ("Blue");
143  * ...</pre>
144  * or
145  * <pre> String[] defaultColor = new String[] {"Red", "Blue"};
146  * ...</pre>
147  *
148  * In either case, the appropriate
149  * checkbox options will appear checked in the browser.
150  *
151  * <pre> &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="Red" checked="true">Red&lt;/input>
152  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="Blue" checked="true">Blue&lt;/input>
153  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="Green">Green&lt;/input>
154  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="Yellow">Yellow&lt;/input>
155  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="White">White&lt;/input>
156  * &lt;input type="checkbox" name="wlw-checkbox_group_key:{actionForm.preferredColors}" value="Black">Black&lt;/input></pre>
157  * @netui:tag name="checkBoxGroup" description="Groups a collection of CheckBoxOptions, and handles databinding of their values."
158  */

159 public class CheckBoxGroup
160         extends HtmlGroupBaseTag
161 {
162     // @todo: seems like we should write out the hidden field even if the option is disabled.
163
private static final Logger logger = Logger.getInstance(CheckBoxGroup.class);
164
165     /**
166      * This is the name of the prefixHandler for the checkbox grup.
167      */

168     public static final String JavaDoc CHECKBOXGROUP_KEY = "checkbox_group_key";
169
170     private static final String JavaDoc OLDVALUE_SUFFIX = "OldValue";
171
172     private InputHiddenTag.State _state = new InputHiddenTag.State();
173     private InputHiddenTag.State _hiddenState = new InputHiddenTag.State();
174
175     private List _defaultSelections;
176     private boolean _defaultSingleton = false;
177     private boolean _defaultSingleValue = false;
178
179     private String JavaDoc[] _match; // The actual values we will match against, calculated in doStartTag().
180
private Object JavaDoc _dynamicAttrs; // the Object
181
private InternalStringBuilder _saveBody;
182
183     private static final List _internalNamingChain;
184     private WriteRenderAppender _writer;
185
186     static
187     {
188         List l = new ArrayList(3);
189         l.add(new FormDataNameInterceptor());
190         l.add(new IndexedNameInterceptor());
191         l.add(new PrefixNameInterceptor(CHECKBOXGROUP_KEY));
192         _internalNamingChain = Collections.unmodifiableList(l);
193     }
194
195     static
196     {
197         org.apache.beehive.netui.pageflow.ProcessPopulate.registerPrefixHandler(CHECKBOXGROUP_KEY, new CheckboxGroupPrefixHandler());
198     }
199
200     /**
201      * The handler for naming and indexing the CheckBoxGroup.
202      */

203     public static class CheckboxGroupPrefixHandler
204             implements org.apache.beehive.netui.pageflow.RequestParameterHandler
205     {
206         /**
207          * Determines the current state of the CheckBoxGroup based on the Request.
208          */

209         public void process(HttpServletRequest JavaDoc request, String JavaDoc key, String JavaDoc expr,
210                             ProcessPopulate.ExpressionUpdateNode node)
211         {
212             String JavaDoc[] returnArray = null;
213
214             if (!key.endsWith(OLDVALUE_SUFFIX)) {
215                 //This select has values and should stay that way
216
returnArray = request.getParameterValues(key);
217             }
218             else {
219                 //Check the request to see if select also exists
220
String JavaDoc newKey = key.substring(0, key.indexOf(OLDVALUE_SUFFIX));
221                 String JavaDoc[] select = request.getParameterValues(newKey);
222                 if (select != null) {
223                     returnArray = select;
224                 }
225                 else {
226                     returnArray = new String JavaDoc[0]; //null;
227
}
228             }
229
230             if (node.expression.endsWith(OLDVALUE_SUFFIX)) {
231                 node.expression = node.expression.substring(0, node.expression.indexOf(OLDVALUE_SUFFIX));
232             }
233             node.values = returnArray;
234
235             if (logger.isDebugEnabled()) {
236                 logger.debug("\n*********************************************\n" +
237                         "process with key \"" + key + "\" and expression \"" + node.expression + "\"" + "and result size: "
238                         + (returnArray != null ? "" + returnArray.length : null) + "\n" +
239                         "*********************************************\n");
240             }
241         }
242     }
243
244     public CheckBoxGroup()
245     {
246         super();
247     }
248
249     /**
250      * Return the name of the Tag.
251      */

252     public String JavaDoc getTagName()
253     {
254         return "CheckBoxGroup";
255     }
256
257     /**
258      * Return an <code>ArrayList</code> which represents a chain of <code>INameInterceptor</code>
259      * objects. This method by default returns <code>null</code> and should be overridden
260      * by objects that support naming.
261      * @return an <code>ArrayList</code> that will contain <code>INameInterceptor</code> objects.
262      */

263     protected List getNamingChain()
264     {
265         return _internalNamingChain;
266     }
267
268     /**
269      * Overrided method to return a list of the possible default values. The method always return either
270      * a <code>List</code> or null.
271      * @return a <code>List</code> that represents the default value.
272      */

273     private Object JavaDoc evaluateDefaultValue()
274     {
275         Object JavaDoc val = _defaultValue;
276
277         List defaults = null;
278         if (val instanceof String JavaDoc) {
279             if ("checked".equals(val)) {
280                 _defaultSingleton = true;
281                 _defaultSingleValue = true;
282                 return null;
283             }
284             else if ("unchecked".equals(val)) {
285                 _defaultSingleton = true;
286                 _defaultSingleValue = false;
287                 return null;
288             }
289
290             defaults = new ArrayList();
291             defaults.add(val);
292             return defaults;
293         }
294
295         Iterator optionsIterator = null;
296         optionsIterator = IteratorFactory.createIterator(val);
297
298         // log an error, default value is optional so only warn
299
if (optionsIterator == null && _defaultValue != null) {
300             logger.warn(Bundle.getString("Tags_IteratorError",
301                     new Object JavaDoc[]{getTagName(), "defaultValue", _defaultValue}));
302         }
303         if (optionsIterator == null)
304             optionsIterator = IteratorFactory.EMPTY_ITERATOR;
305
306         defaults = new ArrayList();
307         while (optionsIterator.hasNext()) {
308             defaults.add(optionsIterator.next());
309         }
310
311         return defaults;
312     }
313
314     /**
315      * Checks whether the given value matches one of the CheckBoxGroup's selected
316      * CheckBoxOptions.
317      * @param value Value to be compared
318      */

319     public boolean isMatched(String JavaDoc value, Boolean JavaDoc defaultValue)
320     {
321         if (value == null)
322             return false;
323
324         if (_match != null) {
325             for (int i = 0; i < _match.length; i++) {
326                 if (value.equals(_match[i]))
327                     return true;
328             }
329         }
330         else {
331             // a provided default value will override the group
332
if (defaultValue != null)
333                 return defaultValue.booleanValue();
334
335             // if we have a singleton definition then use that
336
if (_defaultSingleton)
337                 return _defaultSingleValue;
338
339             // check to see if we have a default arraylist with the value in it
340
if (_defaultSelections != null)
341                 return _defaultSelections.contains(value);
342         }
343
344         return false;
345     }
346
347
348     /**
349      * Determine the set of matches for the CheckBoxGroup
350      * @throws JspException if a JSP exception has occurred
351      */

352     public int doStartTag() throws JspException JavaDoc
353     {
354         ServletRequest JavaDoc req = pageContext.getRequest();
355         if (_cr == null)
356             _cr = TagRenderingBase.Factory.getConstantRendering(req);
357
358         // get the evaluated dataSource and default values
359
Object JavaDoc val = evaluateDataSource();
360         _defaultSelections = (List) evaluateDefaultValue();
361         if (hasErrors()) {
362             return SKIP_BODY;
363         }
364
365         // process the default values and create the matching array...
366
if (val != null) {
367             buildMatch(val);
368             if (hasErrors()) {
369                 return SKIP_BODY;
370             }
371         }
372
373         // if the checkbox group is disabled do not write out the
374
// hidden field.
375
_writer = new WriteRenderAppender(pageContext);
376         if (!_repeater && !_disabled) {
377
378             //Create hidden field for state tracking
379
_state.clear();
380             String JavaDoc hiddenParamName = null;
381             hiddenParamName = getQualifiedDataSourceName() + OLDVALUE_SUFFIX;
382             _state.name = hiddenParamName;
383             _state.value = "true";
384
385             TagRenderingBase hiddenTag = TagRenderingBase.Factory.getRendering(TagRenderingBase.INPUT_HIDDEN_TAG, req);
386             hiddenTag.doStartTag(_writer, _state);
387             hiddenTag.doEndTag(_writer);
388         }
389
390         if (isVertical())
391             _cr.TABLE(_writer);
392
393         // if this is a repeater then we shouid prime the pump...
394
_dynamicAttrs = evaluateOptionsDataSource();
395         assert (_dynamicAttrs != null);
396         assert (_dynamicAttrs instanceof Map ||
397                 _dynamicAttrs instanceof Iterator);
398
399         if (_repeater) {
400             if (_dynamicAttrs instanceof Map) {
401                 _dynamicAttrs = ((Map) _dynamicAttrs).entrySet().iterator();
402             }
403             if (!(_dynamicAttrs instanceof Iterator)) {
404                 String JavaDoc s = Bundle.getString("Tags_OptionsDSIteratorError");
405                 registerTagError(s, null);
406                 return SKIP_BODY;
407             }
408             while (((Iterator) _dynamicAttrs).hasNext()) {
409                 _repCurItem = ((Iterator) _dynamicAttrs).next();
410                 if (_repCurItem != null)
411                     break;
412             }
413             if (isVertical())
414                 _cr.TR_TD(_writer);
415
416             DataAccessProviderStack.addDataAccessProvider(this, pageContext);
417         }
418         _saveBody = new InternalStringBuilder(128);
419
420         // Continue processing this page
421
return EVAL_BODY_BUFFERED;
422
423     }
424
425     /**
426      * Save any body content of this tag, which will generally be the
427      * option(s) representing the values displayed to the user.
428      * @throws JspException if a JSP exception has occurred
429      */

430     public int doAfterBody() throws JspException JavaDoc
431     {
432         StringBuilderRenderAppender writer = new StringBuilderRenderAppender(_saveBody);
433         if (bodyContent != null) {
434             String JavaDoc value = bodyContent.getString();
435             bodyContent.clearBody();
436             if (value == null)
437                 value = "";
438             _saveBody.append(value);
439         }
440
441         if (_repeater) {
442             ServletRequest JavaDoc req = pageContext.getRequest();
443             if (_cr == null)
444                 _cr = TagRenderingBase.Factory.getConstantRendering(req);
445
446             if (isVertical())
447                 _cr.end_TD_TR(writer);
448
449             while (((Iterator) _dynamicAttrs).hasNext()) {
450                 _repCurItem = ((Iterator) _dynamicAttrs).next();
451                 if (_repCurItem != null) {
452                     _repIdx++;
453                     if (isVertical())
454                         _cr.TR_TD(writer);
455
456                     return EVAL_BODY_AGAIN;
457                 }
458             }
459         }
460
461         return SKIP_BODY;
462     }
463
464     /**
465      * Render the set of CheckBoxOptions.
466      * @throws JspException if a JSP exception has occurred
467      */

468     public int doEndTag()
469             throws JspException JavaDoc
470     {
471         if (hasErrors())
472             return reportAndExit(EVAL_PAGE);
473
474         ServletRequest JavaDoc req = pageContext.getRequest();
475         if (_cr == null)
476             _cr = TagRenderingBase.Factory.getConstantRendering(req);
477
478         String JavaDoc idScript = null;
479         String JavaDoc altText = null;
480         char accessKey = 0x00;
481
482
483         // Render a tag representing the end of our current form
484
if (_saveBody != null)
485             write(_saveBody.toString());
486
487         // if this is a repeater then we have created the content in the body so we write that
488
if (_repeater) {
489             // Render a tag representing the end of our current form
490
if (isVertical())
491                 _cr.end_TABLE(_writer);
492
493             if (idScript != null)
494                 write(idScript);
495
496             localRelease();
497             return EVAL_PAGE;
498         }
499
500         // non repeater working against the options data source
501
assert(_dynamicAttrs != null);
502         if (_dynamicAttrs instanceof Map) {
503             Map dynamicCheckboxesMap = (Map) _dynamicAttrs;
504             Iterator keyIterator = dynamicCheckboxesMap.keySet().iterator();
505             int idx = 0;
506             while (keyIterator.hasNext()) {
507                 Object JavaDoc optionValue = keyIterator.next();
508                 String JavaDoc optionDisplay = "";
509                 if (dynamicCheckboxesMap.get(optionValue) != null)
510                     optionDisplay = dynamicCheckboxesMap.get(optionValue).toString();
511                 addOption(_writer, INPUT_CHECKBOX, optionValue.toString(), optionDisplay, idx++, altText, accessKey, _disabled);
512
513                 if (hasErrors()) {
514                     reportErrors();
515                     if (isVertical()) {
516                         _cr.end_TABLE(_writer);
517                     }
518                     localRelease();
519                     return EVAL_PAGE;
520                 }
521                 write("\n");
522             }
523         }
524         else {
525             assert(_dynamicAttrs instanceof Iterator);
526             Iterator it = (Iterator) _dynamicAttrs;
527             int idx = 0;
528             while (it.hasNext()) {
529                 Object JavaDoc o = it.next();
530                 if (o == null)
531                     continue;
532
533                 if (o instanceof GroupOption) {
534                     GroupOption go = (GroupOption) o;
535                     addOption(_writer, INPUT_CHECKBOX, go.getValue(), go.getName(), idx++, go.getAlt(), go.getAccessKey(), _disabled);
536                 }
537                 else {
538                     String JavaDoc checkboxValue = o.toString();
539                     addOption(_writer, INPUT_CHECKBOX, checkboxValue, checkboxValue, idx++, altText, accessKey, _disabled);
540                 }
541
542                 if (hasErrors()) {
543                     reportErrors();
544                     if (isVertical()) {
545                         _cr.end_TABLE(_writer);
546                     }
547                     localRelease();
548                     return EVAL_PAGE;
549                 }
550                 write("\n");
551             }
552         }
553
554         if (isVertical())
555             _cr.end_TABLE(_writer);
556
557         if (idScript != null)
558             write(idScript);
559
560         localRelease();
561         return EVAL_PAGE;
562     }
563
564     public void createHiddenField(AbstractRenderAppender results)
565             throws JspException JavaDoc
566     {
567         if (_repIdx == 0 && !_disabled) {
568
569             ServletRequest JavaDoc req = pageContext.getRequest();
570
571             //Create hidden field for state tracking
572
String JavaDoc hiddenParamName = null;
573             hiddenParamName = getQualifiedDataSourceName() + OLDVALUE_SUFFIX;
574             _hiddenState.name = hiddenParamName;
575             _hiddenState.value = "true";
576
577             TagRenderingBase hiddenTag = TagRenderingBase.Factory.getRendering(TagRenderingBase.INPUT_HIDDEN_TAG, req);
578             hiddenTag.doStartTag(results, _hiddenState);
579             hiddenTag.doEndTag(results);
580         }
581
582     }
583
584     /**
585      * Release any acquired resources.
586      */

587     protected void localRelease()
588     {
589         // cleanup the context variables used for binding during repeater
590
if (_repeater)
591             DataAccessProviderStack.removeDataAccessProvider(pageContext);
592
593         super.localRelease();
594
595         _defaultSelections = null;
596         _match = null;
597         _dynamicAttrs = null;
598         _saveBody = null;
599         _defaultSingleton = false;
600         _defaultSingleValue = false;
601     }
602
603     // This method will build the match list, should this be a hashmap?
604
private void buildMatch(Object JavaDoc val)
605     {
606         if (val instanceof String JavaDoc[]) {
607             _match = (String JavaDoc[]) val;
608         }
609         else {
610             Iterator matchIterator = null;
611             // this should return null, but we should handle it it does
612
matchIterator = IteratorFactory.createIterator(val);
613             if (matchIterator == null)
614                 matchIterator = IteratorFactory.EMPTY_ITERATOR;
615
616             List matchList = new ArrayList();
617             while (matchIterator.hasNext()) {
618                 Object JavaDoc o = matchIterator.next();
619                 if (o != null)
620                     matchList.add(o.toString());
621             }
622             int size = matchList.size();
623
624             _match = new String JavaDoc[size];
625             for (int i = 0; i < size; i++) {
626                 _match[i] = matchList.get(i).toString();
627             }
628         }
629
630         if (logger.isDebugEnabled()) {
631             logger.debug("****** CheckboxGroup Matches ******");
632             if (_match != null) {
633                 for (int i = 0; i < _match.length; i++) {
634                     logger.debug(i + ": " + _match[i]);
635                 }
636             }
637         }
638     }
639 }
640
Popular Tags