1357 lines
49 KiB
Java
1357 lines
49 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.pageflow.RequestParameterHandler;
|
|
import org.apache.beehive.netui.script.common.DataAccessProviderStack;
|
|
import org.apache.beehive.netui.script.common.IDataAccessProvider;
|
|
import org.apache.beehive.netui.tags.ByRef;
|
|
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.*;
|
|
import org.apache.beehive.netui.util.Bundle;
|
|
import org.apache.beehive.netui.util.iterator.ArrayIterator;
|
|
import org.apache.beehive.netui.util.iterator.IteratorFactory;
|
|
import org.apache.beehive.netui.util.logging.Logger;
|
|
|
|
import javax.servlet.ServletRequest;
|
|
import javax.servlet.jsp.JspException;
|
|
import java.util.*;
|
|
|
|
|
|
/**
|
|
* Renders a select containing a set of SelectOptions.
|
|
*
|
|
* Select binds to an Iterator of Strings.
|
|
*
|
|
* If Select uses any Format tags, it must have those tags come before any nested
|
|
* SelectOption tags.
|
|
* @jsptagref.tagdescription Renders an HTML <select> tag containing a set of selectable options.
|
|
*
|
|
* <p>The <netui:select> tag can generate a set of
|
|
* selectable options in two ways:
|
|
*
|
|
* <blockquote>
|
|
* <ol>
|
|
* <li>they can be dynamically generated by pointing the
|
|
* <netui:select> tag at a String[] object or
|
|
* {@link java.util.HashMap java.util.HashMap}</li>
|
|
* <li>they can be statically generated by providing a set of children
|
|
* {@link SelectOption}
|
|
* tags</li>
|
|
* </ol>
|
|
* </blockquote>
|
|
*
|
|
* <p><b>Dynamically Generated Options</b>
|
|
*
|
|
* <p>You can dynamically generate a set of selectable options by
|
|
* pointing the <netui:select> tag at a String[].
|
|
*
|
|
* <pre> public String[] colors = {"red", "green", "blue", "orange", "pink", "aqua", "black", "brown", "tan"};
|
|
*
|
|
* public String[] getColors()
|
|
* {
|
|
* return colors;
|
|
* }</pre>
|
|
*
|
|
* <p>To point the <netui:select> tag at the String[] object use the
|
|
* <code>optionsDataSource</code> attribute.</p>
|
|
*
|
|
* <pre> <netui:select dataSource="actionForm.selection"
|
|
* optionsDataSource="${pageFlow.colors}"/></pre>
|
|
*
|
|
* Note that you can make the display value and the submitted value differ by pointing the
|
|
* optionsDataSource attribute of the <netui:select> tag at a HashMap object.
|
|
* (Any object that implements the {@link java.util.Map java.util.Map} interface will work.)
|
|
*
|
|
* <pre> public HashMap optionsMap = new HashMap();
|
|
*
|
|
* protected HashMap getOptionsMap()
|
|
* {
|
|
* return optionsMap;
|
|
* }
|
|
*
|
|
* protected void onCreate()
|
|
* {
|
|
* optionsMap.put("#ff3333", "red");
|
|
* optionsMap.put("#3333ff", "blue");
|
|
* optionsMap.put("#33ff33", "green");
|
|
* }</pre>
|
|
*
|
|
* <p>However, you cannot use a Map object if you choose to use the Select as a repeater
|
|
* (setting the attribute repeater="true").</p>
|
|
*
|
|
* <p>Point the <netui:select> at the Map object using the <code>optionsDataSource</code> attribute.</p>
|
|
*
|
|
* <pre> <netui:select dataSource="actionForm.selection"
|
|
* optionsDataSource="${pageFlow.optionsMap}"/></pre>
|
|
*
|
|
* The following HTML will be generated.
|
|
*
|
|
* <pre> <select name="wlw-select_key:{actionForm.selection}">
|
|
* <option value="#3333ff">blue</option>
|
|
* <option value="#33ff33">green</option>
|
|
* <option value="#ff3333">red</option>
|
|
* </select></pre>
|
|
*
|
|
* <p><b>Statically Generated Options</b></p>
|
|
*
|
|
* <p>To statically generate selecable options, place a set of <netui:selectOption> tags inside
|
|
* the <netui:select> tag.
|
|
*
|
|
* <pre> <netui:select dataSource="actionForm.selection" size="5">
|
|
* <netui:selectOption value="red" />
|
|
* <netui:selectOption value="blue" />
|
|
* <netui:selectOption value="green" />
|
|
* <netui:selectOption value="yellow" />
|
|
* <netui:selectOption value="orange" />
|
|
* </netui:select></pre>
|
|
*
|
|
* <p><b>Submitting Selections</b></p>
|
|
*
|
|
* <p>A <netui:select> is submitted as a String or String[] object, depending on whether the
|
|
* <code>multiple</code> attribute is set to true. In the following example, the <code>dataSource</code>
|
|
* attribute points at a String[] object.</p>
|
|
*
|
|
* <pre> </netui:select dataSource="actionForm.selections"...</pre>
|
|
*
|
|
* <p>In this case, the <netui:select> tag submits to a String[] field of a Form Bean.</p>
|
|
*
|
|
* <pre> public static class SubmitForm extends FormData
|
|
* {
|
|
* private String[] selections;
|
|
*
|
|
* public void setSelections(String[] selections)
|
|
* {
|
|
* this.selections = selections;
|
|
* }
|
|
*
|
|
* public String[] getSelections()
|
|
* {
|
|
* return this.selections;
|
|
* }
|
|
* }</pre>
|
|
*
|
|
* <p><b>Use Select as a Repeater with Multiple Repeating Types</b></p>
|
|
*
|
|
* <p>Optionally, use the <netui:select> tag as a repeater to render multiple options
|
|
* from the <code>dataSource</code> and <code>defaultValue</code> attributes as well as
|
|
* the <code>optionsDataSource</code>. The <netui:select> element can dynamically generate
|
|
* option elements for different repeating types of "option", "dataSource", "default",
|
|
* (optionsDataSource, dataSource, and defaultValue attributes respectively) and "null".
|
|
* The Select <code>repeatingOrder</code> attribute sets the order that repeating types
|
|
* are generated. The <code>repeatingType</code> attribute on the <netui:selectOption>
|
|
* tag identifies each of the types to be rendered.</p>
|
|
*
|
|
* <p>Use JSTL boolean conditional tags with the <netui:selectOption> elements
|
|
* to help manage repeaters of different data types.
|
|
* For example, the <code>dataSource</code> could point to a String[] while
|
|
* the <code>optionsDataSource</code> points to an Object[] where each object has
|
|
* name and value fields...</p>
|
|
*
|
|
* <pre> <netui:select dataSource="actionForm.selections"
|
|
* optionsDataSource="${pageFlow.options}"
|
|
* repeatingOrder="dataSource,option"
|
|
* repeater="true" multiple="true">
|
|
* <c:if test="${container.metadata.dataSourceStage}">
|
|
* <netui:selectOption repeatingType="dataSource" value="${container.item}">
|
|
* <netui:span value="${container.item}" />
|
|
* </netui:selectOption>
|
|
* </c:if>
|
|
* <c:if test="${container.metadata.optionStage}">
|
|
* <netui:selectOption repeatingType="option" value="${container.item.name}">
|
|
* <netui:span value="${container.item.value}" />
|
|
* </netui:selectOption>
|
|
* </c:if>
|
|
* </netui:select>
|
|
* </pre>
|
|
* @example The following sample uses the <code>optionsDataSource</code> attribute to reference a
|
|
* dynamically generated dropdown list.
|
|
*
|
|
* <pre>
|
|
* <netui:select dataSource="actionForm.selectedOption"
|
|
* optionsDataSource="${actionForm.itemOptions}" />
|
|
* </pre>
|
|
*
|
|
* <p>Assume that the <code>optionsDataSource</code> attribute refers to
|
|
* a <code>java.util.Map</code> object.
|
|
* The Map object will be rendered as a series
|
|
* of <option> tags. HTML that is similar to the following will be
|
|
* rendered in the browser:</p>
|
|
*
|
|
* <pre> <select name="wlw-select_key:{actionForm.itemOptions}">
|
|
* <option value="633">Aurora Bridge</option>
|
|
* <option value="631">FA-18 fighter jet</option>
|
|
* <option value="635">Space Needle</option>
|
|
* <option value="642">Thin Mints</option>
|
|
* ...
|
|
* </select></pre>
|
|
* @netui:tag name="select" description="Defines a multiple-choice menu or drop-down list within a netui:form element."
|
|
* @netui:attribute name="onSelect" hide="true" description=""
|
|
*/
|
|
public class Select extends HtmlOptionsDataSourceTag
|
|
implements IDataAccessProvider, IFormattable
|
|
{
|
|
// @todo: needs to create DRT tests for: verification of errors, verirication of data sources matching,
|
|
// @todo: verification of formating inside a repeater.
|
|
// @todo: on the null tag, we need to default the null value to NULL_VALUE
|
|
// @todo: need to handle null, in the options
|
|
// @todo: should handle no optionDataSource when repeating...
|
|
|
|
private static final Logger logger = Logger.getInstance(Select.class);
|
|
|
|
private SelectTag.State _state = new SelectTag.State();
|
|
private OptionTag.State _optionState = new OptionTag.State();
|
|
private InputHiddenTag.State _hiddenState = new InputHiddenTag.State();
|
|
private boolean _formatterError = false;
|
|
|
|
private static Object[] NULL_INSTANCE = {null};
|
|
|
|
|
|
/**
|
|
* This enum defines stages through the possible option values.
|
|
*/
|
|
public static class RepeatingStages
|
|
{
|
|
private static final int INT_BEFORE = 0;
|
|
private static final int INT_OPTION = 1;
|
|
private static final int INT_DEFAULT = 2;
|
|
private static final int INT_DATASOURCE = 3;
|
|
private static final int INT_NULL = 4;
|
|
private static final int INT_DONE = 5;
|
|
|
|
static final RepeatingStages BEFORE = new RepeatingStages(INT_BEFORE);
|
|
static final RepeatingStages OPTION = new RepeatingStages(INT_OPTION);
|
|
static final RepeatingStages DEFAULT = new RepeatingStages(INT_DEFAULT);
|
|
static final RepeatingStages DATASOURCE = new RepeatingStages(INT_DATASOURCE);
|
|
static final RepeatingStages NULL = new RepeatingStages(INT_NULL);
|
|
static final RepeatingStages DONE = new RepeatingStages(INT_DONE);
|
|
|
|
/**
|
|
* These are the publically exposed stages, <code>REPEATING_OPTION, REPEATING_DEFAULT,
|
|
* REPEATING_DATASOURCE and REPEATING_NULL</code>.
|
|
*/
|
|
public static final String REPEATING_OPTION = "option";
|
|
public static final String REPEATING_DEFAULT = "default";
|
|
public static final String REPEATING_DATASOURCE = "dataSource";
|
|
public static final String REPEATING_NULL = "null";
|
|
|
|
public int value;
|
|
|
|
// prevent construction...
|
|
private RepeatingStages(int val)
|
|
{
|
|
value = val;
|
|
}
|
|
|
|
int getValue()
|
|
{
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Returns the String value that can be used to order the selection.
|
|
* @return The String Value.
|
|
*/
|
|
public String toString()
|
|
{
|
|
switch (value) {
|
|
case INT_OPTION:
|
|
return REPEATING_OPTION;
|
|
case INT_DEFAULT:
|
|
return REPEATING_DEFAULT;
|
|
case INT_DATASOURCE:
|
|
return REPEATING_DATASOURCE;
|
|
case INT_NULL:
|
|
return REPEATING_NULL;
|
|
default:
|
|
return "Unknown Stage";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given a String value defined above, return the enum value for it.
|
|
* @param value a String value matching one of the public Strings defined for the class.
|
|
* @return the matching RepeatingStages or null.
|
|
*/
|
|
public static RepeatingStages parseString(String value)
|
|
{
|
|
if (REPEATING_OPTION.equals(value))
|
|
return OPTION;
|
|
if (REPEATING_DEFAULT.equals(value))
|
|
return DEFAULT;
|
|
if (REPEATING_DATASOURCE.equals(value))
|
|
return DATASOURCE;
|
|
if (REPEATING_NULL.equals(value))
|
|
return NULL;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This defines the default order of processing the options when repeating.
|
|
*/
|
|
private static final RepeatingStages[] DEFAULT_ORDER = {RepeatingStages.BEFORE,
|
|
RepeatingStages.OPTION,
|
|
RepeatingStages.DATASOURCE,
|
|
RepeatingStages.DEFAULT,
|
|
RepeatingStages.NULL};
|
|
|
|
/**
|
|
* Default value of the options <code>value</code> attribute.
|
|
*/
|
|
public static final String NULL_VALUE = "netui_null";
|
|
|
|
/**
|
|
* Constant value of the <code>repeatingType</code> attribute for options handling the <code>null</code> option.
|
|
*/
|
|
//public static final String REPEATING_NULL = "Null";
|
|
|
|
private static final String SELECT_KEY = "select_key";
|
|
private static final String OLDVALUE_SUFFIX = "OldValue";
|
|
|
|
// IDataAccessProvider support
|
|
private int _repIdx = 0; // The current index for repeating over the optionsDataSource
|
|
private RepeatingStages _repCurStage = RepeatingStages.BEFORE; // The current stage defined by the stage constants above
|
|
private boolean _repeater; // Boolean flag indicating if this is a repeater or not
|
|
private Object _repCurItem; // The current item access by the IDataAccessProvider
|
|
private Iterator _repeaterIterator; // The iterator being used to output the options.
|
|
private RepeatingStages[] _order = DEFAULT_ORDER;
|
|
|
|
private Object _dynamicOptions; // The interator (or map) for the options data source, repeating this is current var
|
|
|
|
private String _saveBody;
|
|
private String _nullableOptionText;
|
|
|
|
private List _defaultSelections;
|
|
private ArrayList _formatters;
|
|
private ArrayList _optionList;
|
|
private String[] _match; // The actual values we will match against
|
|
private boolean _nullable;
|
|
private TagRenderingBase _optRb;
|
|
|
|
private static final List _internalNamingChain;
|
|
|
|
static
|
|
{
|
|
List l = new ArrayList(3);
|
|
l.add(new FormDataNameInterceptor());
|
|
l.add(new IndexedNameInterceptor());
|
|
l.add(new PrefixNameInterceptor(SELECT_KEY));
|
|
_internalNamingChain = Collections.unmodifiableList(l);
|
|
|
|
org.apache.beehive.netui.pageflow.ProcessPopulate.registerPrefixHandler(SELECT_KEY, new SelectPrefixHandler());
|
|
}
|
|
|
|
/**
|
|
*/
|
|
public static class SelectPrefixHandler
|
|
implements RequestParameterHandler
|
|
{
|
|
public void process(javax.servlet.http.HttpServletRequest request, String key,
|
|
String expr, ProcessPopulate.ExpressionUpdateNode node)
|
|
{
|
|
String[] returnArray = null;
|
|
|
|
if (!key.endsWith(OLDVALUE_SUFFIX)) {
|
|
//This select has values and should stay that way
|
|
returnArray = request.getParameterValues(key);
|
|
}
|
|
else {
|
|
//Check the request to see if select also exists
|
|
String newKey = key.substring(0, key.indexOf(OLDVALUE_SUFFIX));
|
|
String[] select = request.getParameterValues(newKey);
|
|
if (select != null) {
|
|
returnArray = select;
|
|
}
|
|
else {
|
|
returnArray = new String[0]; //null;
|
|
}
|
|
}
|
|
|
|
if (node.expression.endsWith(OLDVALUE_SUFFIX)) {
|
|
node.expression = node.expression.substring(0, node.expression.indexOf(OLDVALUE_SUFFIX));
|
|
}
|
|
|
|
//Check for the NULL_VALUE, replace it with null
|
|
for (int i = 0; i < returnArray.length; i++) {
|
|
if (returnArray[i].equals(NULL_VALUE)) {
|
|
returnArray[i] = null;
|
|
}
|
|
}
|
|
|
|
node.values = returnArray;
|
|
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("\n*********************************************\n" +
|
|
"process with key \"" + key + "\" and expression \"" + node.expression + "\"" + "and result size: "
|
|
+ (returnArray != null ? "" + returnArray.length : null) + "\n" +
|
|
"*********************************************\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
public Select()
|
|
{
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* Return the name of the Tag.
|
|
*/
|
|
public String getTagName()
|
|
{
|
|
return "Select";
|
|
}
|
|
|
|
public String getDataSource()
|
|
{
|
|
return "{" + _dataSource.toString() + "}";
|
|
}
|
|
|
|
/**
|
|
* This method will return the state associated with the tag. This is used by this
|
|
* base class to access the individual state objects created by the tags.
|
|
* @return a subclass of the <code>AbstractHtmlState</code> class.
|
|
*/
|
|
protected AbstractHtmlState getState()
|
|
{
|
|
return _state;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Evaluate the defaultValues
|
|
*/
|
|
protected Object evaluateDefaultValue()
|
|
throws JspException
|
|
{
|
|
Object val = _defaultValue;
|
|
|
|
List defaults = null;
|
|
if (val instanceof String) {
|
|
defaults = new ArrayList();
|
|
defaults.add(val);
|
|
}
|
|
else {
|
|
Iterator optionsIterator = null;
|
|
optionsIterator = IteratorFactory.createIterator(val);
|
|
|
|
// default value is optional so only warn
|
|
if (optionsIterator == null && _defaultValue != null)
|
|
logger.warn(Bundle.getString("Tags_IteratorError",
|
|
new Object[]{getTagName(), "defaultValue", _defaultValue}));
|
|
|
|
if (optionsIterator == null)
|
|
optionsIterator = IteratorFactory.EMPTY_ITERATOR;
|
|
|
|
defaults = new ArrayList();
|
|
while (optionsIterator.hasNext()) {
|
|
Object o = optionsIterator.next();
|
|
defaults.add(o.toString());
|
|
}
|
|
}
|
|
|
|
return defaults;
|
|
}
|
|
|
|
/**
|
|
* Set whether multiple selections are allowed.
|
|
* @param multiple the multiple value ("true" or "false")
|
|
* @jsptagref.attributedescription Boolean. Whether or not multi-selection is enabled.
|
|
* If multiple selection is enabled, a null option will not be displayed, even if
|
|
* the <code>nullable</code> is set to true.
|
|
* @jsptagref.databindable false
|
|
* @jsptagref.attributesyntaxvalue <i>boolean_multipleSelectEnabled</i>
|
|
* @netui:attribute required="false" rtexprvalue="true" type="boolean"
|
|
* description="Whether or not multi-selection is enabled.
|
|
* If multiple selection is enabled, a null option will not be displayed, even if
|
|
* the nullable is set to true."
|
|
*/
|
|
public void setMultiple(boolean multiple)
|
|
{
|
|
_state.multiple = multiple;
|
|
}
|
|
|
|
/**
|
|
* Set whether repeating of contained options is on.
|
|
* @param repeater the repeater value ("true" or "false")
|
|
* @jsptagref.attributedescription Set whether repeating of contained options is on.
|
|
* @jsptagref.databindable false
|
|
* @jsptagref.attributesyntaxvalue <i>boolean_repeater</i>
|
|
* @netui:attribute required="false" rtexprvalue="true" type="boolean"
|
|
* description="Set whether repeating of contained options is on."
|
|
*/
|
|
public void setRepeater(boolean repeater)
|
|
{
|
|
_repeater = repeater;
|
|
}
|
|
|
|
/**
|
|
* Gets whether a repeating contained options is on.
|
|
* @return the repeater value
|
|
*/
|
|
public boolean isRepeater()
|
|
{
|
|
return _repeater;
|
|
}
|
|
|
|
/**
|
|
* This method will set the order of the options generated in the select. It must contain a
|
|
* comma separated string listing the order or the stages that the repeating types are processed.
|
|
* These values are "option", "dataSource", "default", and "null".
|
|
* @param order comma separated ordering of items when there is a repeating select.
|
|
* @jsptagref.attributedescription Define the order of options generated for a repeating Select.
|
|
* It must contain a comma separated string listing the order or the stages that the repeating types
|
|
* are processed. These values are "option", "dataSource", "default", and "null". For example,
|
|
* <pre> repeatingOrder="dataSource,option"</pre>
|
|
*
|
|
* Then a <netui:selectOption> element could set the repeatingType attribute to "dataSource"
|
|
* while another is defined for "option".
|
|
* @jsptagref.databindable false
|
|
* @jsptagref.attributesyntaxvalue <i>string_order</i>
|
|
* @netui:attribute required="false" rtexprvalue="true"
|
|
* description="Define the order of options for a repeating Select"
|
|
*/
|
|
public void setRepeatingOrder(String order)
|
|
throws JspException
|
|
{
|
|
String[] options = order.split(",");
|
|
RepeatingStages[] stageOrder = new RepeatingStages[options.length + 1];
|
|
stageOrder[0] = RepeatingStages.BEFORE;
|
|
for (int i = 0; i < options.length; i++) {
|
|
String opt = options[i].trim();
|
|
stageOrder[i + 1] = RepeatingStages.parseString(opt);
|
|
if (stageOrder[i + 1] == null) {
|
|
String s = Bundle.getString("Tags_SelectBadRepeatingStage", new Object[]{opt});
|
|
registerTagError(s, null);
|
|
}
|
|
}
|
|
_order = stageOrder;
|
|
}
|
|
|
|
/**
|
|
* Set whether a null option is desired.
|
|
* @param nullable the nullable value
|
|
* @jsptagref.attributedescription Boolean.
|
|
* Whether a option with the value null should be added to the bottom of the list.
|
|
* If <select> has the multiple <code>attribute</code> set to true, the null option won't be shown.
|
|
* @jsptagref.databindable false
|
|
* @jsptagref.attributesyntaxvalue <i>boolean_nullable</i>
|
|
* @netui:attribute required="false" rtexprvalue="true" type="boolean"
|
|
* description="Whether a option with the value null should be added to the bottom of the list.
|
|
* If <select> has the multiple attribute set to true, the null option won't be shown."
|
|
*/
|
|
public void setNullable(boolean nullable)
|
|
{
|
|
_nullable = nullable;
|
|
}
|
|
|
|
/**
|
|
* Gets the options datasource value (an expression).
|
|
* @return the options datasource
|
|
*/
|
|
public Object getOptionsDataSource()
|
|
{
|
|
return _optionsDataSource;
|
|
}
|
|
|
|
/**
|
|
* Set the text of the nullable option.
|
|
* If the <code>nullable<code> option is true, this is
|
|
* the text of that option. The default is "";
|
|
* @jsptagref.attributedescription Boolean.
|
|
* If the <code>nullable</code> attribute is set to true, then the <code>nullableOptionText</code>
|
|
* attribute determines the display text of the null option.
|
|
* The default is to use the empty string, "", as the display text.
|
|
* @jsptagref.databindable false
|
|
* @jsptagref.attributesyntaxvalue <i>boolean_nullableOptionText</i>
|
|
* @netui:attribute required="false" rtexprvalue="true" type="boolean"
|
|
* description="If the nullable attribute is set to true, then the nullableOptionText
|
|
* attribute determines the display text of the null option.
|
|
*/
|
|
public void setNullableOptionText(String nullableOptionText)
|
|
{
|
|
_nullableOptionText = nullableOptionText;
|
|
}
|
|
|
|
/**
|
|
* This method will return the object representing the <code>optionsDataSource</code>. This
|
|
* is overridden from the base class, because there are only two types which will be
|
|
* retunred from the method. The <code>optionsDataSource</code> will either be a instance of a <code>Map</code>
|
|
* or and instanceof a <code>Iterator</code>.
|
|
* @return the object instance object representing the objectsDataSource. This may be null.
|
|
* @throws JspException on an error
|
|
*/
|
|
protected Object evaluateOptionsDataSource()
|
|
throws JspException
|
|
{
|
|
Object val = _optionsDataSource;
|
|
if (val == null) {
|
|
// optionsDataSource is option so this is a warning
|
|
if (_optionsDataSource != null)
|
|
logger.warn(Bundle.getString("Tags_IteratorError",
|
|
new Object[]{getTagName(), "optionsDataSource", _optionsDataSource}));
|
|
return null;
|
|
}
|
|
|
|
if (val instanceof Map)
|
|
return val;
|
|
|
|
Iterator options = null;
|
|
options = IteratorFactory.createIterator(val);
|
|
if (options == null)
|
|
options = IteratorFactory.EMPTY_ITERATOR;
|
|
|
|
return options;
|
|
}
|
|
|
|
/**
|
|
* Sets how many options are displayed.
|
|
* @param size the size (a number)
|
|
* @jsptagref.attributedescription The number of visible options
|
|
* @jsptagref.databindable false
|
|
* @jsptagref.attributesyntaxvalue <i>integer_size</i>
|
|
* @netui:attribute required="false" rtexprvalue="true" type="int"
|
|
* description="The number of visible options"
|
|
*/
|
|
public void setSize(int size)
|
|
{
|
|
_state.size = size;
|
|
}
|
|
|
|
/**
|
|
* Does the specified value match one of those we are looking for?
|
|
* @param value Value to be compared
|
|
*/
|
|
public boolean isMatched(String value)
|
|
{
|
|
if (value == null)
|
|
return false;
|
|
if ((_match != null)) {
|
|
for (int i = 0; i < _match.length; i++) {
|
|
if (value.equals(_match[i]))
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
if (_defaultSelections != null) {
|
|
return (_defaultSelections.contains(value));
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
//********************************** IDataAccessProvider Interface ******************************
|
|
// setDataSource is implemented by the HtmlDataSourceTag class
|
|
// getDataSource is implemented by the HtmlDataSourceTag class
|
|
|
|
/**
|
|
* Get the current index in this iteration. This should be a
|
|
* zero based integer that increments after each iteration.
|
|
* @return the current index of iteration or 0
|
|
*/
|
|
public int getCurrentIndex()
|
|
{
|
|
return _repIdx;
|
|
}
|
|
|
|
/**
|
|
* Get the current data item in this IDataAccessProvider.
|
|
* @return the current data item or <code>null</code>
|
|
*/
|
|
public Object getCurrentItem()
|
|
{
|
|
return _repCurItem;
|
|
}
|
|
|
|
/**
|
|
* Get a metadata object for the current item. This interface
|
|
* is optional, and implementations of this interface are
|
|
* provided by the IDataAccessProvider interface. See these
|
|
* implementations for information about their support for
|
|
* current item metadata.
|
|
* @return the current metadata or <code>null</code> if no metadata can be
|
|
* found or metadata is not supported by a IDataAccessProvider implementation
|
|
*/
|
|
public Object getCurrentMetadata()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Get the parent IDataAccessProvider of a IDataAccessProvider. A IDataAccessProvider
|
|
* implementation may be able to nest IDataAccessProviders. In this case,
|
|
* it can be useful to be able to also nest access to data from parent
|
|
* providers. Implementations of this interface are left with having
|
|
* to discover and export parents. The return value from this call
|
|
* on an implementing Object can be <code>null</code>.
|
|
* @return the parent IDataAccessProvider or <code>null</code> if this method
|
|
* is not supported or the parent can not be found.
|
|
*/
|
|
public IDataAccessProvider getProviderParent()
|
|
{
|
|
return (IDataAccessProvider) findAncestorWithClass(this, IDataAccessProvider.class);
|
|
}
|
|
|
|
/**
|
|
* Return the enum value of the currently repeating stage.
|
|
* @return The currently repeating stage.
|
|
*/
|
|
public RepeatingStages getRepeatingStage()
|
|
{
|
|
return _repCurStage;
|
|
}
|
|
|
|
/**
|
|
* Boolean indicating that we are processing the optionsDataSource.
|
|
* @return <code>true</code> if we are processing the optionsDataSource.
|
|
*/
|
|
public boolean isOptionStage()
|
|
{
|
|
return _repCurStage == RepeatingStages.OPTION;
|
|
}
|
|
|
|
/**
|
|
* Boolean indicating that we are processing the defaultValue.
|
|
* @return <code>true</code> if we are processing the defaultValue.
|
|
*/
|
|
public boolean isDefaultStage()
|
|
{
|
|
return _repCurStage == RepeatingStages.DEFAULT;
|
|
}
|
|
|
|
/**
|
|
* Boolean indicating that we are processing the dataSource.
|
|
* @return <code>true</code> if we are processing the dataSource.
|
|
*/
|
|
public boolean isDataSourceStage()
|
|
{
|
|
return _repCurStage == RepeatingStages.DATASOURCE;
|
|
}
|
|
|
|
/**
|
|
* Boolean indicating that we are processing the defined null value.
|
|
* @return <code>true</code> if we are processing the defined null value.
|
|
*/
|
|
public boolean isNullStage()
|
|
{
|
|
return _repCurStage == RepeatingStages.NULL;
|
|
}
|
|
|
|
/**
|
|
* Render the beginning of this select.
|
|
* @throws JspException if a JSP exception has occurred
|
|
*/
|
|
public int doStartTag() throws JspException
|
|
{
|
|
Object val = evaluateDataSource();
|
|
_defaultSelections = (List) evaluateDefaultValue();
|
|
|
|
// if there were expression errors report them
|
|
if (hasErrors())
|
|
return SKIP_BODY;
|
|
|
|
buildMatch(val);
|
|
if (hasErrors())
|
|
return SKIP_BODY;
|
|
|
|
|
|
_formatters = new ArrayList();
|
|
_optionList = new ArrayList();
|
|
|
|
// Walk the options data source
|
|
_dynamicOptions = evaluateOptionsDataSource();
|
|
if (_repeater) {
|
|
_repCurStage = _order[0];
|
|
boolean valid = doRepeaterAfterBody();
|
|
if (!valid)
|
|
return SKIP_BODY;
|
|
DataAccessProviderStack.addDataAccessProvider(this, pageContext);
|
|
}
|
|
|
|
// Continue processing this page
|
|
return EVAL_BODY_BUFFERED;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
if (hasErrors()) {
|
|
return SKIP_BODY;
|
|
}
|
|
|
|
// if this is a repeater we need to repeater over the body...
|
|
if (_repeater) {
|
|
if (doRepeaterAfterBody())
|
|
return EVAL_BODY_AGAIN;
|
|
}
|
|
|
|
if (bodyContent != null) {
|
|
String value = bodyContent.getString();
|
|
bodyContent.clearBody();
|
|
if (value == null)
|
|
value = "";
|
|
_saveBody = value.trim();
|
|
}
|
|
return SKIP_BODY;
|
|
}
|
|
|
|
/**
|
|
* Render the end of this select.
|
|
* @throws JspException if a JSP exception has occurred
|
|
*/
|
|
public int doEndTag() throws JspException
|
|
{
|
|
ServletRequest req = pageContext.getRequest();
|
|
|
|
String fmtErrors = null;
|
|
if (_formatterError) {
|
|
fmtErrors = getErrorsFromBody();
|
|
}
|
|
if (hasErrors())
|
|
return reportAndExit(EVAL_PAGE);
|
|
|
|
_state.disabled = isDisabled();
|
|
|
|
//Create hidden field for state tracking
|
|
ByRef ref = new ByRef();
|
|
nameHtmlControl(_state, ref);
|
|
|
|
if (hasErrors())
|
|
return reportAndExit(EVAL_PAGE);
|
|
|
|
// Only write out the hidden field if the select is not
|
|
// disabled. If it is disabled, then nothing will be posted
|
|
// back from this.
|
|
WriteRenderAppender writer = new WriteRenderAppender(pageContext);
|
|
if (!_state.disabled) {
|
|
_hiddenState.clear();
|
|
String hiddenParamName = null;
|
|
hiddenParamName = _state.name + OLDVALUE_SUFFIX;
|
|
_hiddenState.name = hiddenParamName;
|
|
_hiddenState.value = "true";
|
|
|
|
TagRenderingBase hiddenTag = TagRenderingBase.Factory.getRendering(TagRenderingBase.INPUT_HIDDEN_TAG, req);
|
|
hiddenTag.doStartTag(writer, _hiddenState);
|
|
hiddenTag.doEndTag(writer);
|
|
write("\n");
|
|
}
|
|
|
|
// Render any formatting errors that may have occurred.
|
|
if (fmtErrors != null)
|
|
write(fmtErrors);
|
|
|
|
|
|
TagRenderingBase br = TagRenderingBase.Factory.getRendering(TagRenderingBase.SELECT_TAG, req);
|
|
br.doStartTag(writer, _state);
|
|
|
|
// Render the content of the body, these would be the options
|
|
if (_saveBody != null) {
|
|
write(_saveBody);
|
|
}
|
|
|
|
// if we are repeating then the body contained the options so we can exit here
|
|
if (_repeater) {
|
|
|
|
if (hasErrors())
|
|
return reportAndExit(EVAL_PAGE);
|
|
|
|
br.doEndTag(writer);
|
|
if (!ref.isNull())
|
|
write((String) ref.getRef());
|
|
|
|
// Continue processing this page
|
|
localRelease();
|
|
return EVAL_PAGE;
|
|
}
|
|
|
|
// All of the code below will pass through the optionsDataSource, the dataSource and defaultValue and
|
|
// create a full Select.
|
|
if (_dynamicOptions != null) {
|
|
if (_dynamicOptions instanceof Map) {
|
|
Map dynamicOptionsMap = (Map) _dynamicOptions;
|
|
Iterator keyIterator = dynamicOptionsMap.keySet().iterator();
|
|
while (keyIterator.hasNext()) {
|
|
Object optionValue = keyIterator.next();
|
|
String optionDisplay = null;
|
|
if (dynamicOptionsMap.get(optionValue) != null) {
|
|
optionDisplay = dynamicOptionsMap.get(optionValue).toString();
|
|
}
|
|
|
|
if (optionValue != null) {
|
|
addOption(req, optionValue.toString(), optionDisplay);
|
|
}
|
|
}
|
|
}
|
|
else if (_dynamicOptions instanceof Iterator) {
|
|
Iterator dynamicOptionsIterator = (Iterator) evaluateOptionsDataSource();
|
|
while (dynamicOptionsIterator.hasNext()) {
|
|
Object o = dynamicOptionsIterator.next();
|
|
if (o != null) {
|
|
String optionValue = o.toString();
|
|
addOption(req, optionValue, optionValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// add the value from the DataSource and Default value
|
|
addDatasourceIfNeeded(req);
|
|
addDefaultsIfNeeded(req);
|
|
if (_nullable && !isMultiple()) {
|
|
String txt = (_nullableOptionText != null) ? _nullableOptionText : "";
|
|
addOption(req, NULL_VALUE, txt);
|
|
}
|
|
|
|
br.doEndTag(writer);
|
|
if (!ref.isNull())
|
|
write((String) ref.getRef());
|
|
|
|
// Continue processing this page
|
|
localRelease();
|
|
return EVAL_PAGE;
|
|
}
|
|
|
|
/**
|
|
* Release any acquired resources.
|
|
*/
|
|
protected void localRelease()
|
|
{
|
|
if (_repeater)
|
|
DataAccessProviderStack.removeDataAccessProvider(pageContext);
|
|
|
|
super.localRelease();
|
|
_state.clear();
|
|
|
|
_defaultSelections = null;
|
|
_formatters = null;
|
|
_match = null;
|
|
_saveBody = null;
|
|
_nullable = false;
|
|
_nullableOptionText = null;
|
|
_optionList = null;
|
|
|
|
_repIdx = 0;
|
|
_repeater = false;
|
|
_repCurItem = null;
|
|
_repCurStage = RepeatingStages.BEFORE;
|
|
_dynamicOptions = null;
|
|
_formatterError = false;
|
|
_optRb = null;
|
|
|
|
_order = DEFAULT_ORDER;
|
|
}
|
|
|
|
private String getErrorsFromBody()
|
|
{
|
|
final String END_TOKEN = "</span>";
|
|
assert(_saveBody != null);
|
|
InternalStringBuilder body = new InternalStringBuilder(_saveBody.length());
|
|
InternalStringBuilder error = new InternalStringBuilder(_saveBody.length());
|
|
|
|
// pull out all of the spans These should be legally constructed, otherwise we will ignore them.
|
|
int len = _saveBody.length();
|
|
int pos = 0;
|
|
while (pos < len) {
|
|
|
|
// find the start of a span, if we dont' find one then it's over....
|
|
int start = _saveBody.indexOf("<span", pos);
|
|
if (start == -1)
|
|
break;
|
|
|
|
// if we don't find the end of the <span> then we don't have a legal span so ignore it
|
|
int end = _saveBody.indexOf(END_TOKEN);
|
|
if (end == -1)
|
|
break;
|
|
|
|
// copy the pos to start into the body
|
|
int realEnd = end + END_TOKEN.length() + 1;
|
|
body.append(_saveBody.substring(pos, start));
|
|
error.append(_saveBody.substring(start, realEnd));
|
|
pos = realEnd;
|
|
}
|
|
|
|
// recreate the remainder of the body, everything not left
|
|
body.append(_saveBody.substring(pos, len));
|
|
_saveBody = body.toString();
|
|
|
|
// return the error
|
|
return error.toString();
|
|
}
|
|
|
|
/**
|
|
* This method will side affects the <code>_repCurItem</code> to insure that it
|
|
* is set to the next item in the iteration set. It will return <code>true</code>
|
|
* if there is a next item, and <code>false</code> when we are done with the iteration
|
|
* @return returns <code>true</code> when <code>_repCurItem</code> contains the next item and
|
|
* <code>false</code> when we are done.
|
|
* @throws JspException
|
|
*/
|
|
private boolean doRepeaterAfterBody()
|
|
throws JspException
|
|
{
|
|
switch (_repCurStage.getValue()) {
|
|
case RepeatingStages.INT_BEFORE:
|
|
if (!moveNext())
|
|
return false;
|
|
return doRepeaterAfterBody();
|
|
case RepeatingStages.INT_OPTION:
|
|
assert (_repeaterIterator instanceof Iterator);
|
|
while (_repeaterIterator.hasNext()) {
|
|
_repCurItem = _repeaterIterator.next();
|
|
if (_repCurItem != null) {
|
|
_optionList.add(_repCurItem);
|
|
return true;
|
|
}
|
|
}
|
|
if (!moveNext())
|
|
return false;
|
|
return doRepeaterAfterBody();
|
|
|
|
case RepeatingStages.INT_DEFAULT:
|
|
case RepeatingStages.INT_DATASOURCE:
|
|
case RepeatingStages.INT_NULL:
|
|
assert (_repeaterIterator instanceof Iterator);
|
|
while (_repeaterIterator.hasNext()) {
|
|
_repCurItem = _repeaterIterator.next();
|
|
if (!_optionList.contains(_repCurItem)) {
|
|
_optionList.add(_repCurItem);
|
|
return true;
|
|
}
|
|
}
|
|
if (!moveNext())
|
|
return false;
|
|
return doRepeaterAfterBody();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* This method will move to the next iteration type. The order of the
|
|
* iteration is defined by the <code>_order</code> array. The result
|
|
* is side affecting the _repeaterIterator by initializing it. If there
|
|
* is nothing further, then we will return false, otherwise we return true.
|
|
* @return
|
|
* @throws JspException
|
|
*/
|
|
private boolean moveNext()
|
|
throws JspException
|
|
{
|
|
// increment the current position, if we are beyond the end of the array return
|
|
_repIdx++;
|
|
if (_repIdx == _order.length)
|
|
return false;
|
|
|
|
// Get the next stage and clear the _repeaterIterator
|
|
_repCurStage = _order[_repIdx];
|
|
_repeaterIterator = null;
|
|
|
|
// process each type of iteration...
|
|
// Each will recursively call moveNext, if that stage doesn't support iteration
|
|
switch (_repCurStage.getValue()) {
|
|
case RepeatingStages.INT_BEFORE:
|
|
break;
|
|
case RepeatingStages.INT_OPTION:
|
|
// This produces an error if the optionsDataSource is an instance of an iterator
|
|
if (!(_dynamicOptions instanceof Iterator)) {
|
|
String s = Bundle.getString("Tags_OptionsDSIteratorError");
|
|
registerTagError(s, null);
|
|
return false;
|
|
}
|
|
|
|
assert(_dynamicOptions instanceof Iterator);
|
|
_repeaterIterator = (Iterator) _dynamicOptions;
|
|
break;
|
|
|
|
case RepeatingStages.INT_DEFAULT:
|
|
if (_defaultSelections != null)
|
|
_repeaterIterator = _defaultSelections.iterator();
|
|
break;
|
|
case RepeatingStages.INT_DATASOURCE:
|
|
if (_match != null)
|
|
_repeaterIterator = Arrays.asList(_match).iterator();
|
|
break;
|
|
case RepeatingStages.INT_NULL:
|
|
if (_nullable)
|
|
_repeaterIterator = new ArrayIterator(NULL_INSTANCE);
|
|
break;
|
|
}
|
|
|
|
// return true when we set the iterator, otherwise move to the next stage.
|
|
return (_repeaterIterator != null) ? true : moveNext();
|
|
}
|
|
|
|
/**
|
|
* This method builds the list of selected items so that they can be marked as selected.
|
|
* @param val The <code>dataSource</code>
|
|
*/
|
|
private void buildMatch(Object val)
|
|
{
|
|
// create the match data
|
|
if (val != null) {
|
|
if (val instanceof String) {
|
|
_match = new String[]{(String) val};
|
|
}
|
|
else if (val instanceof String[]) {
|
|
String[] s = (String[]) val;
|
|
int cnt = 0;
|
|
for (int i = 0; i < s.length; i++) {
|
|
if (s[i] != null)
|
|
cnt++;
|
|
}
|
|
if (cnt == s.length)
|
|
_match = s;
|
|
else {
|
|
if (cnt > 0) {
|
|
_match = new String[cnt];
|
|
cnt = 0;
|
|
for (int i = 0; i < s.length; i++) {
|
|
if (s[i] != null) {
|
|
_match[cnt++] = s[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Iterator matchIterator = null;
|
|
// val is never null so this would be an error
|
|
matchIterator = IteratorFactory.createIterator(val);
|
|
if (matchIterator == null) {
|
|
matchIterator = IteratorFactory.EMPTY_ITERATOR;
|
|
}
|
|
|
|
ArrayList matchList = new ArrayList();
|
|
while (matchIterator.hasNext()) {
|
|
Object o = matchIterator.next();
|
|
if (o == null)
|
|
continue;
|
|
matchList.add(o);
|
|
}
|
|
|
|
int size = matchList.size();
|
|
_match = new String[size];
|
|
for (int i = 0; i < size; i++) {
|
|
assert (matchList.get(i) != null);
|
|
assert (matchList.get(i).toString() != null);
|
|
_match[i] = matchList.get(i).toString();
|
|
}
|
|
}
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("****** Select Matches ******");
|
|
if (_match != null) {
|
|
for (int i = 0; i < _match.length; i++) {
|
|
logger.debug(i + ": " + _match[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (_nullable && !isMultiple() && (_defaultSelections == null || _defaultSelections.size() == 0)) {
|
|
_match = new String[]{NULL_VALUE};
|
|
}
|
|
}
|
|
}
|
|
|
|
// add the default values specified in the tag if they are needed.
|
|
private void addDefaultsIfNeeded(ServletRequest req)
|
|
throws JspException
|
|
{
|
|
if (_defaultSelections != null) {
|
|
Iterator iterator = _defaultSelections.iterator();
|
|
while (iterator.hasNext()) {
|
|
Object selection = iterator.next();
|
|
if (!_optionList.contains(selection)) {
|
|
addOption(req, selection.toString(), selection.toString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean isMultiple()
|
|
{
|
|
return _state.multiple;
|
|
}
|
|
|
|
// add dthe datasource values if needed.
|
|
private void addDatasourceIfNeeded(ServletRequest req)
|
|
throws JspException
|
|
{
|
|
if (_match == null)
|
|
return;
|
|
|
|
for (int i = 0; i < _match.length; i++) {
|
|
if (!_optionList.contains(_match[i])) {
|
|
if (!_match[i].equals(NULL_VALUE))
|
|
addOption(req, _match[i], _match[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addOption(ServletRequest req, String optionValue, String optionDisplay)
|
|
throws JspException
|
|
{
|
|
assert(optionValue != null);
|
|
assert(optionDisplay != null);
|
|
|
|
write("\n");
|
|
_optionState.clear();
|
|
_optionState.value = optionValue;
|
|
_optionState.style = _state.style;
|
|
_optionState.styleClass = _state.styleClass;
|
|
|
|
if (isMatched(optionValue)) {
|
|
_optionState.selected = true;
|
|
}
|
|
|
|
WriteRenderAppender writer = new WriteRenderAppender(pageContext);
|
|
if (_optRb == null)
|
|
_optRb = TagRenderingBase.Factory.getRendering(TagRenderingBase.OPTION_TAG, req);
|
|
_optRb.doStartTag(writer, _optionState);
|
|
|
|
|
|
if (optionDisplay != null) {
|
|
write(formatText(optionDisplay));
|
|
}
|
|
else {
|
|
write("<");
|
|
write(optionValue);
|
|
write(">");
|
|
}
|
|
|
|
_optRb.doEndTag(writer);
|
|
|
|
addOptionToList(optionValue);
|
|
}
|
|
|
|
/**
|
|
* Adds a FormatTag.Formatter to the Select's set of formatters
|
|
* @param formatter a FormatTag.Formatter added by a child FormatTag.
|
|
*/
|
|
public void addFormatter(FormatTag.Formatter formatter)
|
|
{
|
|
_formatters.add(formatter);
|
|
}
|
|
|
|
/**
|
|
* Indicate that a formatter has reported an error so the formatter should output it's
|
|
* body text.
|
|
*/
|
|
public void formatterHasError()
|
|
{
|
|
_formatterError = true;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
public void addOptionToList(String value)
|
|
{
|
|
_optionList.add(value);
|
|
}
|
|
|
|
/**
|
|
* Apply the Select's set of formatters to the given text
|
|
* @param text the text to format.
|
|
* @return the formatted text
|
|
*/
|
|
public String formatText(Object text)
|
|
throws JspException
|
|
{
|
|
int cnt = _formatters.size();
|
|
for (int i = 0; i < cnt; i++) {
|
|
FormatTag.Formatter currentFormatter = (FormatTag.Formatter) _formatters.get(i);
|
|
try {
|
|
text = currentFormatter.format(text);
|
|
}
|
|
catch (JspException e) {
|
|
registerTagError(e.getMessage(), e);
|
|
}
|
|
}
|
|
return text.toString();
|
|
}
|
|
|
|
/* ==================================================================
|
|
*
|
|
* This tag's publically exposed HTML, CSS, and JavaScript attributes
|
|
*
|
|
* ==================================================================
|
|
*/
|
|
|
|
/**
|
|
* Sets the accessKey attribute value. This should key value of the
|
|
* keyboard navigation key. It is recommended not to use the following
|
|
* values because there are often used by browsers <code>A, C, E, F, G,
|
|
* H, V, left arrow, and right arrow</code>.
|
|
* @param accessKey the accessKey value.
|
|
* @jsptagref.attributedescription The keyboard navigation key for the element.
|
|
* The following values are not recommended because they
|
|
* are often used by browsers: <code>A, C, E, F, G,
|
|
* H, V, left arrow, and right arrow</code>
|
|
* @jsptagref.databindable false
|
|
* @jsptagref.attributesyntaxvalue <i>string_accessKey</i>
|
|
* @netui:attribute required="false" rtexprvalue="true" type="char"
|
|
* description="The keyboard navigation key for the element.
|
|
* The following values are not recommended because they
|
|
* are often used by browsers: A, C, E, F, G,
|
|
* H, V, left arrow, and right arrow"
|
|
*/
|
|
public void setAccessKey(char accessKey)
|
|
{
|
|
_state.registerAttribute(AbstractHtmlState.ATTR_GENERAL, ACCESSKEY, Character.toString(accessKey));
|
|
}
|
|
|
|
/**
|
|
* Sets the tabIndex of the rendered html tag.
|
|
* @param tabindex the tab index.
|
|
* @jsptagref.attributedescription The tabIndex of the rendered HTML tag. This attribute determines the position of the
|
|
* tag in the sequence of page elements that the user may advance through by pressing the TAB key.
|
|
* @jsptagref.databindable false
|
|
* @jsptagref.attributesyntaxvalue <i>string_tabIndex</i>
|
|
* @netui:attribute required="false" rtexprvalue="true" type="int"
|
|
* description="The tabIndex of the rendered HTML tag. This attribute determines the position of the
|
|
* tag in the sequence of page elements that the user may advance through by pressing the TAB key."
|
|
*/
|
|
public void setTabindex(int tabindex)
|
|
{
|
|
_state.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TABINDEX, Integer.toString(tabindex));
|
|
}
|
|
}
|
|
|