JdbcMapper/beehive-netui-tags/src/main/java/org/apache/beehive/netui/tags/html/RadioButtonGroup.java

484 lines
18 KiB
Java

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Header:$
*/
package org.apache.beehive.netui.tags.html;
import org.apache.beehive.netui.util.internal.InternalStringBuilder;
import org.apache.beehive.netui.pageflow.ProcessPopulate;
import org.apache.beehive.netui.script.common.DataAccessProviderStack;
import org.apache.beehive.netui.tags.naming.FormDataNameInterceptor;
import org.apache.beehive.netui.tags.naming.IndexedNameInterceptor;
import org.apache.beehive.netui.tags.naming.PrefixNameInterceptor;
import org.apache.beehive.netui.tags.rendering.StringBuilderRenderAppender;
import org.apache.beehive.netui.tags.rendering.TagRenderingBase;
import org.apache.beehive.netui.tags.rendering.WriteRenderAppender;
import org.apache.beehive.netui.util.Bundle;
import org.apache.beehive.netui.util.logging.Logger;
import org.apache.beehive.netui.util.tags.GroupOption;
import javax.servlet.ServletRequest;
import javax.servlet.jsp.JspException;
import java.util.*;
/**
* Groups a collection of RadioButtonOptions, and handles databinding of their values.
*
* If RadioButtonGroup uses any Format tags, it must have those tags come before above any nested
* RadioButtonOption tags.
* @jsptagref.tagdescription Renders a collection of radiobutton options
* as <input type="radio"> and handles the data binding of their values.
*
* <p>The &lt;netui:radioButtonGroup> tag can generate a set of
* radiobutton options in two ways:
*
* <blockquote>
* <ol>
* <li>they can be dynamically generated by pointing the
* &lt;netui:radioButtonGroup> tag at a {@link java.util.HashMap java.util.HashMap}
* or String[] object</li>
* <li>they can be statically generated by providing a set of children
* {@link RadioButtonOption}
* tags</li>
* </ol>
* </blockquote>
*
* <p><b>Dynamically Generated Radiobutton Options</b>
*
* <p>You can dynamically generate a set of radionbutton options by
* pointing the &lt;netui:radioButtonGroup> tag at a HashMap
* (or any object that implements the {@link java.util.Map java.util.Map} interface).
*
* <p>For example, if you define a HashMap object and get method in the Controller file...
*
* <pre> public HashMap hashMap = new HashMap();
*
* protected HashMap getHashMap()
* {
* return hashMap;
* }
*
* protected void onCreate()
* {
* hashMap.put("value1", "Display Text 1");
* hashMap.put("value2", "Display Text 2");
* hashMap.put("value3", "Display Text 3");
* }</pre>
*
* ...point the &lt;netui:radioButtonGroup>
* at the Map object using the <code>optionsDataSource</code> attribute.
*
* <pre> &lt;netui:radioButtonGroup
* dataSource="actionForm.selection"
* optionsDataSource="${pageFlow.hashMap}"></pre>
*
* <p>In the generated radiobutton options, the display text and the
* submitted value can be made to differ. The HashMap keys will
* form the submitted values, while the HashMap entries will form
* the display texts.
*
* <pre> &lt;input type="radio" name="wlw-radio_button_group_key:{actionForm.selection}" value="value1">Display Text 1&lt;/input>
* &lt;input type="radio" name="wlw-radio_button_group_key:{actionForm.selection}" value="value2">Display Text 2&lt;/input>
* &lt;input type="radio" name="wlw-radio_button_group_key:{actionForm.selection}" value="value3">Display Text 3&lt;/input></pre>
*
* <p>Note that you can point the &lt;netui:radioButtonGroup> tag at a
* String[] object. A set of radiobutton options will be generated,
* but there will be no difference between the
* display texts and the submitted values.
*
* <p><b>Statically Generated Radiobutton Options</b></p>
*
* <p>To statically generate radiobutton options, place a set of &lt;netui:radioButtonOption> tags inside
* the &lt;netui:radioButtonGroup> tag.
*
* <pre> &lt;netui:radioButtonGroup dataSource="actionForm.selection">
* &lt;netui:radioButtonOption value="value1">Display Text 1&lt;/netui:radioButtonOption>&lt;br>
* &lt;netui:radioButtonOption value="value2">Display Text 2&lt;/netui:radioButtonOption>&lt;br>
* &lt;netui:radioButtonOption value="value3">Display Text 3&lt;/netui:radioButtonOption>&lt;br>
* &lt;/netui:radioButtonGroup></pre>
*
* <p><b>Submitting Data</b></p>
*
* <p>A &lt;netui:radioButtonGroup> is submitted as a String value. Use the <code>dataSource</code> attribute
* to submit to a String object.
*
* <pre> &lt;netui:radioButtonGroup dataSource="actionForm.selection"></pre>
*
* <p>In this case, the &lt;netui:radioButtonGroup> submits to a String field of a Form Bean.
*
* <pre> public static class ProcessDataForm extends FormData
* {
* private String selection;
*
* public void setSelection(String selection)
* {
* this.selection = selection;
* }
*
* public String getSelection()
* {
* return this.selection;
* }
* }</pre>
* @example In this sample, the &lt;netui:radioButtonGroup>
* submits data to the Form Bean field <code>preferredColors</code>.
*
* <pre> &lt;netui:radioButtonGroup
* dataSource="actionForm.preferredColors"
* optionsDataSource="${pageFlow.colors}" /></pre>
*
* The <code>optionsDataSource</code> attribute points to a get method for a String[] on the Controller file:
*
* <pre> String[] colors = new String[] {"Red", "Blue", "Green", "Yellow", "White", "Black"};
*
* public String[] getColors()
* {
* return colors;
* }</pre>
*
* This automatically renders the appropriate set of radionbutton options:
*
* <pre> &lt;input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="Red">Red&lt;/input>
* &lt;input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="Blue">Blue&lt;/input>
* &lt;input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="Green">Green&lt;/input>
* &lt;input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="Yellow">Yellow&lt;/input>
* &lt;input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="White">White&lt;/input>
* &lt;input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="Black">Black&lt;/input></pre>
* @netui:tag name="radioButtonGroup" description="Defines a group of netui:radioButtonOption elements."
*/
public class RadioButtonGroup
extends HtmlGroupBaseTag
{
// @todo: selection may not work with options in repeater.
private static final Logger logger = Logger.getInstance(RadioButtonGroup.class);
public static final String RADIOBUTTONGROUP_KEY = "radio_button_group_key";
private String _match; // The actual values we will match against, calculated in doStartTag().
private String _defaultRadio; //
private Object _dynamicAttrs; // The optionsDataSource object
private InternalStringBuilder _saveBody; // The body text
private WriteRenderAppender _writer;
private static final List _internalNamingChain;
static
{
List l = new ArrayList(3);
l.add(new FormDataNameInterceptor());
l.add(new IndexedNameInterceptor());
l.add(new PrefixNameInterceptor(RADIOBUTTONGROUP_KEY));
_internalNamingChain = Collections.unmodifiableList(l);
}
static
{
org.apache.beehive.netui.pageflow.ProcessPopulate.registerPrefixHandler(RADIOBUTTONGROUP_KEY, new RadioButtonGroupPrefixHandler());
}
/**
* The handler for naming and indexing the RadioButtonGroup.
*/
public static class RadioButtonGroupPrefixHandler
implements org.apache.beehive.netui.pageflow.RequestParameterHandler
{
public void process(javax.servlet.http.HttpServletRequest request, String key,
String expr, ProcessPopulate.ExpressionUpdateNode node)
{
if (logger.isDebugEnabled()) {
logger.debug("*********************************************\n" +
"process with key \"" + key + "\" and expression \"" + node.expression + "\"" +
"*********************************************\n");
}
}
}
public RadioButtonGroup()
{
super();
}
/**
* Return the name of the Tag.
*/
public String getTagName()
{
return "RadioButtonGroup";
}
/**
* Return an <code>ArrayList</code> which represents a chain of <code>INameInterceptor</code>
* objects. This method by default returns <code>null</code> and should be overridden
* by objects that support naming.
* @return an <code>ArrayList</code> that will contain <code>INameInterceptor</code> objects.
*/
protected List getNamingChain()
{
return _internalNamingChain;
}
/**
* Override the default value to return a string or the empty string if the default value results in a
* <code>null</code> value.
* @return the value returned from <code>super.evaluteDefaultValue</code> or the empty string.
*/
private String evaluateDefaultValue()
{
Object val = _defaultValue;
if (val != null)
return val.toString();
return "";
}
/**
* Does the specified value match one of those we are looking for?
* @param value Value to be compared
*/
public boolean isMatched(String value, Boolean defaultValue)
{
// @todo: there isn't a defaultValue for radio button, what should we do here?
if (value == null)
return false;
if (_match != null)
return value.equals(_match);
if (_defaultRadio != null)
return value.equals(_defaultRadio);
return false;
}
/**
* Determine the match for the RadioButtonGroup
* @throws JspException if a JSP exception has occurred
*/
public int doStartTag()
throws JspException
{
// evaluate the datasource and disabled state.
Object val = evaluateDataSource();
if (val != null)
_match = val.toString();
// Store this tag itself as a page attribute
pageContext.setAttribute(RADIOBUTTONGROUP_KEY, this);
_defaultRadio = evaluateDefaultValue();
// see if there are errors in the evaluation
if (hasErrors())
return SKIP_BODY;
ServletRequest req = pageContext.getRequest();
if (_cr == null)
_cr = TagRenderingBase.Factory.getConstantRendering(req);
_writer = new WriteRenderAppender(pageContext);
if (isVertical()) {
_cr.TABLE(_writer);
}
// if this is a repeater then we shouid prime the pump...
_dynamicAttrs = evaluateOptionsDataSource();
assert (_dynamicAttrs != null);
assert (_dynamicAttrs instanceof Map ||
_dynamicAttrs instanceof Iterator);
if (_repeater) {
if (_dynamicAttrs instanceof Map) {
_dynamicAttrs = ((Map) _dynamicAttrs).entrySet().iterator();
}
if (!(_dynamicAttrs instanceof Iterator)) {
String s = Bundle.getString("Tags_OptionsDSIteratorError");
registerTagError(s, null);
return SKIP_BODY;
}
while (((Iterator) _dynamicAttrs).hasNext()) {
_repCurItem = ((Iterator) _dynamicAttrs).next();
if (_repCurItem != null)
break;
}
if (isVertical())
_cr.TR_TD(_writer);
DataAccessProviderStack.addDataAccessProvider(this, pageContext);
}
//write(results.toString());
// This is basically this is if enough for 5 options
_saveBody = new InternalStringBuilder(640);
return EVAL_BODY_INCLUDE;
}
/**
* Save any body content of this tag, which will generally be the
* option(s) representing the values displayed to the user.
* @throws JspException if a JSP exception has occurred
*/
public int doAfterBody() throws JspException
{
StringBuilderRenderAppender writer = new StringBuilderRenderAppender(_saveBody);
if (bodyContent != null) {
String value = bodyContent.getString();
bodyContent.clearBody();
if (value == null)
value = "";
_saveBody.append(value);
}
if (_repeater) {
ServletRequest req = pageContext.getRequest();
if (_cr == null)
_cr = TagRenderingBase.Factory.getConstantRendering(req);
if (isVertical())
_cr.end_TD_TR(writer);
while (((Iterator) _dynamicAttrs).hasNext()) {
_repCurItem = ((Iterator) _dynamicAttrs).next();
if (_repCurItem != null) {
_repIdx++;
if (isVertical())
_cr.TR_TD(writer);
return EVAL_BODY_AGAIN;
}
}
}
return SKIP_BODY;
}
/**
* Render the set of RadioButtonOptions.
* @throws JspException if a JSP exception has occurred
*/
public int doEndTag() throws JspException
{
if (hasErrors())
return reportAndExit(EVAL_PAGE);
String idScript = null;
String altText = null;
char accessKey = 0x00;
// Remove the page scope attributes we created
pageContext.removeAttribute(RADIOBUTTONGROUP_KEY);
ServletRequest req = pageContext.getRequest();
if (_cr == null)
_cr = TagRenderingBase.Factory.getConstantRendering(req);
//InternalStringBuilder results = new InternalStringBuilder(128);
if (_saveBody != null)
write(_saveBody.toString());
// if this is a repeater we output the content during the body processing
if (_repeater) {
// Render a tag representing the end of our current form
if (isVertical())
_cr.end_TABLE(_writer);
if (idScript != null)
write(idScript);
//write(results.toString());
localRelease();
return EVAL_PAGE;
}
// Render a tag representing the end of our current form
if (_dynamicAttrs instanceof Map) {
Map dynamicRadiosMap = (Map) _dynamicAttrs;
Iterator keyIterator = dynamicRadiosMap.keySet().iterator();
int idx = 0;
while (keyIterator.hasNext()) {
Object optionValue = keyIterator.next();
String optionDisplay = null;
if (dynamicRadiosMap.get(optionValue) != null) {
optionDisplay = dynamicRadiosMap.get(optionValue).toString();
}
else {
optionDisplay = "";
}
if (optionValue != null) {
addOption(_writer, INPUT_RADIO, optionValue.toString(), optionDisplay, idx++, altText, accessKey, _disabled);
}
if (hasErrors()) {
reportErrors();
localRelease();
return EVAL_PAGE;
}
write("\n");
}
}
else {
assert(_dynamicAttrs instanceof Iterator);
Iterator it = (Iterator) _dynamicAttrs;
int idx = 0;
while (it.hasNext()) {
Object o = it.next();
if (o == null)
continue;
if (o instanceof GroupOption) {
GroupOption go = (GroupOption) o;
addOption(_writer, INPUT_RADIO, go.getValue(), go.getName(), idx++, go.getAlt(), go.getAccessKey(), _disabled);
}
else {
String radioValue = o.toString();
addOption(_writer, INPUT_RADIO, radioValue, radioValue, idx++, altText, accessKey, _disabled);
}
if (hasErrors()) {
reportErrors();
localRelease();
return EVAL_PAGE;
}
write("\n");
}
}
if (isVertical()) {
_cr.end_TABLE(_writer);
}
if (idScript != null)
write(idScript);
//write(results.toString());
localRelease();
return EVAL_PAGE;
}
/**
* Release any acquired resources.
*/
protected void localRelease()
{
// remove the context allowing binding to container.item during binding
if (_repeater)
DataAccessProviderStack.removeDataAccessProvider(pageContext);
super.localRelease();
_match = null;
_defaultRadio = null;
}
}