484 lines
18 KiB
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 <netui:radioButtonGroup> tag can generate a set of
|
|
* radiobutton options in two ways:
|
|
*
|
|
* <blockquote>
|
|
* <ol>
|
|
* <li>they can be dynamically generated by pointing the
|
|
* <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 <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 <netui:radioButtonGroup>
|
|
* at the Map object using the <code>optionsDataSource</code> attribute.
|
|
*
|
|
* <pre> <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> <input type="radio" name="wlw-radio_button_group_key:{actionForm.selection}" value="value1">Display Text 1</input>
|
|
* <input type="radio" name="wlw-radio_button_group_key:{actionForm.selection}" value="value2">Display Text 2</input>
|
|
* <input type="radio" name="wlw-radio_button_group_key:{actionForm.selection}" value="value3">Display Text 3</input></pre>
|
|
*
|
|
* <p>Note that you can point the <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 <netui:radioButtonOption> tags inside
|
|
* the <netui:radioButtonGroup> tag.
|
|
*
|
|
* <pre> <netui:radioButtonGroup dataSource="actionForm.selection">
|
|
* <netui:radioButtonOption value="value1">Display Text 1</netui:radioButtonOption><br>
|
|
* <netui:radioButtonOption value="value2">Display Text 2</netui:radioButtonOption><br>
|
|
* <netui:radioButtonOption value="value3">Display Text 3</netui:radioButtonOption><br>
|
|
* </netui:radioButtonGroup></pre>
|
|
*
|
|
* <p><b>Submitting Data</b></p>
|
|
*
|
|
* <p>A <netui:radioButtonGroup> is submitted as a String value. Use the <code>dataSource</code> attribute
|
|
* to submit to a String object.
|
|
*
|
|
* <pre> <netui:radioButtonGroup dataSource="actionForm.selection"></pre>
|
|
*
|
|
* <p>In this case, the <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 <netui:radioButtonGroup>
|
|
* submits data to the Form Bean field <code>preferredColors</code>.
|
|
*
|
|
* <pre> <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> <input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="Red">Red</input>
|
|
* <input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="Blue">Blue</input>
|
|
* <input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="Green">Green</input>
|
|
* <input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="Yellow">Yellow</input>
|
|
* <input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="White">White</input>
|
|
* <input type="radio" name="wlw-radio_button_group_key:{actionForm.preferredColors}" value="Black">Black</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;
|
|
}
|
|
}
|