001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.tags.html;
020:
021: import org.apache.beehive.netui.script.common.IDataAccessProvider;
022: import org.apache.beehive.netui.tags.AbstractClassicTag;
023: import org.apache.beehive.netui.tags.ExpressionHandling;
024: import org.apache.beehive.netui.tags.IAttributeConsumer;
025: import org.apache.beehive.netui.tags.rendering.*;
026: import org.apache.beehive.netui.util.Bundle;
027: import org.apache.beehive.netui.util.iterator.IteratorFactory;
028: import org.apache.beehive.netui.util.logging.Logger;
029:
030: import javax.servlet.ServletRequest;
031: import javax.servlet.jsp.JspException;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Map;
036:
037: /**
038: * Abstract base class that provides the <code>dataSource</code>, <code>defaultValue</code>, and
039: * <code>optionsDataSource</code> attributes.
040: */
041: abstract public class HtmlGroupBaseTag extends AbstractClassicTag
042: implements IDataAccessProvider, HtmlConstants,
043: IAttributeConsumer {
044: private static final Logger logger = Logger
045: .getInstance(HtmlGroupBaseTag.class);
046:
047: /**
048: * Constant defining a horizontal layout of the options.
049: */
050: public final String HORIZONTAL_VALUE = "horizontal";
051:
052: /**
053: * Constant defining a vertical layout of the options.
054: */
055: public final String VERTICAL_VALUE = "vertical";
056:
057: private InputBooleanTag.State _inputState = new InputBooleanTag.State();
058: private SpanTag.State _spanState = new SpanTag.State();
059: protected ConstantRendering _cr;
060:
061: private HashMap _attrs;
062:
063: protected String _dataSource; // The attribute value for the dataSource
064: protected Object _defaultValue; // A String that contains or references the data to render for the default value of this tag.
065: protected Object _optionsDataSource; // The value of the options data source.
066: protected boolean _disabled;
067:
068: private String _orientation; // legal values "horizontal, vertical"
069: private Boolean _orientVal; // Three state boolean if we are doing virtical layout
070:
071: private String _realName; // The name that is the result of do naming
072:
073: private String _style; // The style attribute
074: private String _class; // The class attribute
075:
076: protected String _labelStyle = null;
077: protected String _labelStyleClass = null;
078:
079: protected boolean _repeater; // Boolean flag indicating if this is a repeater or not
080:
081: // These variables are protected explicitly so they can be accessed by subclasses
082: protected int _repIdx = 0; // The current index for repeating over the optionsDataSource
083: protected Object _repCurItem; // The current item access by the IDataAccessProvider
084:
085: public HtmlGroupBaseTag() {
086: super ();
087: }
088:
089: /**
090: * @param value
091: * @return boolean
092: */
093: public abstract boolean isMatched(String value, Boolean defaultValue);
094:
095: /**
096: * Base support for the attribute tag. This is overridden to prevent setting the <code>href</code>
097: * attribute. The <code>checkBoxGroup</code> and <code>radioButtonGroup</code> support two facets,
098: * <code>input</code> and <code>span</code>. The <code>input</code> is the default and will attach
099: * attributes to the <input> element. The <code>span</code> facet will attach attributes to the
100: * <span> elements which represents the label.
101: * @param name The name of the attribute. This value may not be null or the empty string.
102: * @param value The value of the attribute. This may contain an expression.
103: * @param facet The name of a facet to which the attribute will be applied. This is optional.
104: * @throws JspException A JspException may be thrown if there is an error setting the attribute.
105: */
106: public void setAttribute(String name, String value, String facet)
107: throws JspException {
108: // validate the name attribute, in the case of an error simply return.
109: if (name == null || name.length() <= 0) {
110: String s = Bundle.getString("Tags_AttributeNameNotSet");
111: registerTagError(s, null);
112: return;
113: }
114:
115: // it's not legal to set the id or name attributes this way
116: if (name.equals(ID) || name.equals(NAME)) {
117: String s = Bundle.getString("Tags_AttributeMayNotBeSet",
118: new Object[] { name });
119: registerTagError(s, null);
120: return;
121: }
122:
123: // handle the facet. Span will place stuff into the spanState input is the default
124: // so we don't need to do anything.
125: if (facet != null) {
126: if (facet.equals("span")) {
127: _spanState.registerAttribute(
128: AbstractHtmlState.ATTR_GENERAL, name, value);
129: return;
130: } else if (facet.equals("input")) {
131: // do nothing...
132: } else {
133: String s = Bundle.getString(
134: "Tags_AttributeFacetNotSupported",
135: new Object[] { facet });
136: registerTagError(s, null);
137: return;
138: }
139: }
140:
141: // don't set the value on the input
142: if (name.equals(VALUE)) {
143: String s = Bundle.getString("Tags_AttributeMayNotBeSet",
144: new Object[] { name });
145: registerTagError(s, null);
146: return;
147: }
148:
149: // we place the state into the special attribute map.
150: if (_attrs == null) {
151: _attrs = new HashMap();
152: }
153: _attrs.put(name, value);
154: }
155:
156: /**
157: * Return the qualified name of the checkBoxGroup. The qualified name is the created
158: * by calling <code>doNaming()</code>.
159: * @return the qualified name based upon the <code>dataSource</code> name.
160: * @throws JspException
161: */
162: public final String getQualifiedDataSourceName()
163: throws JspException {
164: if (_realName == null)
165: _realName = doNaming();
166: return _realName;
167: }
168:
169: /**
170: * Returns the boolean value or expression indicating the
171: * disable state of the RadioButtonGroup.
172: * @return the disabled state (true or false) or an expression
173: */
174: public boolean isDisabled() {
175: return _disabled;
176: }
177:
178: /**
179: * Set the disable state either with the literal "true" or "false"
180: * or with an expression.
181: * @param disabled true or false or an expression
182: * @jsptagref.attributedescription Set the disable state either with the literal "true"
183: * or "false" or with an expression.
184: * @jsptagref.databindable false
185: * @jsptagref.attributesyntaxvalue <i>boolean_disabled</i>
186: * @netui:attribute required="false" rtexprvalue="true" type="boolean"
187: * description="Set the disable state either with the literal "true" or "false"
188: * or with an expression."
189: */
190: public void setDisabled(boolean disabled) {
191: _disabled = disabled;
192: }
193:
194: /**
195: * Set the orientation of the resulting options group. The layout will be either <code>horizontal</code>
196: * or <code>vertical</code>. The default is <code>horizontal</code>.
197: * @param orientation "vertical" or "horizontal"
198: * @jsptagref.attributedescription Set the orientation of the resulting options group. Either "<code>horizontal</code>" or "<code>vertical</code>". The default is <code>horizontal</code>.
199: * @jsptagref.databindable false
200: * @jsptagref.attributesyntaxvalue <i>string_orientation</i>
201: * @netui:attribute required="false" rtexprvalue="true"
202: * description="Set the orientation of the resulting options group. Either
203: * <code>horizontal</code> or <code>vertical</code>. The default is <code>horizontal</code>."
204: */
205: public void setOrientation(String orientation) {
206: _orientation = setNonEmptyValueAttribute(orientation);
207: }
208:
209: /**
210: * Returns <code>true</code> if vertical layout is set.
211: * @return <code>true</code> if vertical layout is set
212: */
213: public boolean isVertical() {
214: if (_orientVal == null) {
215: boolean val = VERTICAL_VALUE.equalsIgnoreCase(_orientation);
216: _orientVal = new Boolean(val);
217: }
218: return _orientVal.booleanValue();
219: }
220:
221: /**
222: * Set whether repeating of contained options is on.
223: * @param repeater the repeater value ("true" or "false")
224: * @jsptagref.attributedescription Set whether repeating of contained options is on.
225: * @jsptagref.databindable false
226: * @jsptagref.attributesyntaxvalue <i>boolean_repeater</i>
227: * @netui:attribute required="false" rtexprvalue="true" type="boolean"
228: * description="Set whether repeating of contained options is on."
229: */
230: public void setRepeater(boolean repeater) {
231: _repeater = repeater;
232: }
233:
234: /**
235: * Gets whether a repeating contained options is on.
236: * @return the repeater value
237: */
238: public boolean isRepeater() {
239: return _repeater;
240: }
241:
242: /**
243: * Sets the style of the rendered html tag.
244: * @param style the html style.
245: * @jsptagref.attributedescription Specifies style information for the current element.
246: * @jsptagref.databindable false
247: * @jsptagref.attributesyntaxvalue <i>string_style</i>
248: * @netui:attribute required="false" rtexprvalue="true"
249: * description="Sets the style of the rendered html tag."
250: */
251: public void setStyle(String style) {
252: _style = setNonEmptyValueAttribute(style);
253: }
254:
255: /**
256: * Sets the style class of the rendered html tag.
257: * @param styleClass the html style class.
258: * @jsptagref.attributedescription The style class (a style sheet selector).
259: * @jsptagref.databindable false
260: * @jsptagref.attributesyntaxvalue <i>string_styleClass</i>
261: * @netui:attribute required="false" rtexprvalue="true"
262: * description="Sets the style class of the rendered html tag."
263: */
264: public void setStyleClass(String styleClass) {
265: _class = setNonEmptyValueAttribute(styleClass);
266: }
267:
268: /**
269: * Return the label style for each contained CheckBoxOption..
270: * @return the label style
271: */
272: public String getLabelStyle() {
273: return _labelStyle;
274: }
275:
276: /**
277: * Set the label style for each contained CheckBoxOption.
278: * @param labelStyle the label style
279: * @jsptagref.attributedescription Set the label style for each contained CheckBoxOption.
280: * @jsptagref.databindable false
281: * @jsptagref.attributesyntaxvalue <i>string_labelStyle</i>
282: * @netui:attribute required="false" rtexprvalue="true"
283: * description="Set the label style for each contained CheckBoxOption."
284: */
285: public void setLabelStyle(String labelStyle) {
286: _labelStyle = setNonEmptyValueAttribute(labelStyle);
287: }
288:
289: /**
290: * Return the label style class for each contained CheckBoxOption..
291: * @return the label style
292: */
293: public String getLabelStyleClass() {
294: return _labelStyleClass;
295: }
296:
297: /**
298: * Set the label style class for each contained CheckBoxOption.
299: * @param labelStyleClass the label style
300: * @jsptagref.attributedescription Set the label style class for each contained CheckBoxOption.
301: * @jsptagref.databindable false
302: * @jsptagref.attributesyntaxvalue <i>string_labelStyleClass</i>
303: * @netui:attribute required="false" rtexprvalue="true"
304: * description="Set the label style class for each contained CheckBoxOption."
305: */
306: public void setLabelStyleClass(String labelStyleClass) {
307: _labelStyleClass = setNonEmptyValueAttribute(labelStyleClass);
308: }
309:
310: /**
311: * Sets the tag's data source (can be an expression).
312: * @param dataSource the data source
313: * @jsptagref.attributedescription An expression to be evaluated. It determines
314: * the source of populating data for the tag
315: * @jsptagref.databindable false
316: * @jsptagref.attributesyntaxvalue <i>string_dataSource</i>
317: * @netui:attribute required="true"
318: * description="Sets the tag's data source."
319: */
320: public void setDataSource(String dataSource) throws JspException {
321: _dataSource = dataSource;
322: }
323:
324: /**
325: * Gets the tag's data source (can be an expression).
326: * @return the data source
327: */
328: public String getDataSource() {
329: return "{" + _dataSource + "}";
330: }
331:
332: //********************************** IDataAccessProvider Interface ******************************
333: // getDataSource is implemented above
334:
335: /**
336: * Get the current index in this iteration. This should be a
337: * zero based integer that increments after each iteration.
338: * @return the current index of iteration or 0
339: */
340: public int getCurrentIndex() {
341: return _repIdx;
342: }
343:
344: /**
345: * Get the current data item in this IDataAccessProvider.
346: * @return the current data item or <code>null</code>
347: */
348: public Object getCurrentItem() {
349: return _repCurItem;
350: }
351:
352: /**
353: * Get a metadata object for the current item. This interface
354: * is optional, and implementations of this interface are
355: * provided by the IDataAccessProvider interface. See these
356: * implementations for information about their support for
357: * current item metadata.
358: * @return the current metadata or <code>null</code> if no metadata can be
359: * found or metadata is not supported by a IDataAccessProvider implementation
360: */
361: public Object getCurrentMetadata() {
362: return null;
363: }
364:
365: /**
366: * Get the parent IDataAccessProvider of a IDataAccessProvider. A IDataAccessProvider
367: * implementation may be able to nest IDataAccessProviders. In this case,
368: * it can be useful to be able to also nest access to data from parent
369: * providers. Implementations of this interface are left with having
370: * to discover and export parents. The return value from this call
371: * on an implementing Object can be <code>null</code>.
372: * @return the parent IDataAccessProvider or <code>null</code> if this method
373: * is not supported or the parent can not be found.
374: */
375: public IDataAccessProvider getProviderParent() {
376: return (IDataAccessProvider) findAncestorWithClass(this ,
377: IDataAccessProvider.class);
378: }
379:
380: /**
381: * Return an <code>ArrayList</code> which represents a chain of <code>INameInterceptor</code>
382: * objects. This method by default returns <code>null</code> and should be overridden
383: * by objects that support naming.
384: * @return an <code>ArrayList</code> that will contain <code>INameInterceptor</code> objects.
385: */
386: protected List getNamingChain() {
387: return AbstractClassicTag.DefaultNamingChain;
388: }
389:
390: /**
391: * Return the Object that is represented by the specified data source.
392: * @return Object
393: * @throws JspException
394: */
395: protected Object evaluateDataSource() throws JspException {
396: ExpressionHandling expr = new ExpressionHandling(this );
397: String dataSource = "{" + _dataSource + "}";
398: String ds = expr.ensureValidExpression(dataSource,
399: "dataSource", "DataSourceError");
400: if (ds == null)
401: return null;
402:
403: // have a valid expression
404: return expr.evaluateExpression(dataSource, "dataSource",
405: pageContext);
406: }
407:
408: protected String doNaming() throws JspException {
409: String dataSource = "{" + _dataSource + "}";
410: return applyNamingChain(dataSource);
411: }
412:
413: /**
414: * Sets the default value (can be an expression).
415: * @param defaultValue the default value
416: * @jsptagref.attributedescription The String literal or expression to be used as the default value.
417: * @jsptagref.databindable false
418: * @jsptagref.attributesyntaxvalue <i>string_or_expression_default</i>
419: * @netui:attribute required="false" rtexprvalue="true"
420: * description="The String literal or expression to be used as the default value."
421: */
422: public void setDefaultValue(Object defaultValue)
423: throws JspException {
424: if (defaultValue == null) {
425: String s = Bundle.getString("Tags_AttrValueRequired",
426: new Object[] { "defaultValue" });
427: registerTagError(s, null);
428: return;
429: }
430: _defaultValue = defaultValue;
431: }
432:
433: /**
434: * Gets the options datasource value (an expression).
435: * @return the options datasource
436: */
437: public Object getOptionsDataSource() {
438: return _optionsDataSource;
439: }
440:
441: /**
442: * Sets the options datasource value (an expression).
443: * @param optionsDataSource the options datasource
444: * @jsptagref.attributedescription Sets the options datasource value (an expression).
445: * @jsptagref.databindable true
446: * @jsptagref.attributesyntaxvalue <i>expression_optionsDataSource</i>
447: * @netui:attribute required="false" rtexprvalue="true" type="java.lang.Object"
448: * description="Sets the options datasource value."
449: */
450: public void setOptionsDataSource(Object optionsDataSource)
451: throws JspException {
452: if (optionsDataSource == null) {
453: String s = Bundle.getString("Tags_AttrValueRequired",
454: new Object[] { "optionsDataSource" });
455: registerTagError(s, null);
456: return;
457: }
458: _optionsDataSource = optionsDataSource;
459: }
460:
461: /**
462: * Return the real value of the <code>optionDataSource</code> attribute. The value returned will
463: * always be an instance of <code>Iterator</code> This value reflects the
464: * result of expression evaluation on the options data source.
465: * @return the object that represents the options data source.
466: * @throws JspException when something bad happens
467: */
468: protected Object evaluateOptionsDataSource() throws JspException {
469: if (_optionsDataSource == null) {
470: // optionsDataSource is null, so provide an informational message. This isn't an error because it's
471: // possible for tags to list their options inside of their bodies rather than via an optionsDataSource
472: logger.info(Bundle.getString("Tags_IteratorError",
473: new Object[] { getTagName(), "optionsDataSource",
474: _optionsDataSource }));
475: return IteratorFactory.EMPTY_ITERATOR;
476: }
477:
478: if (_optionsDataSource instanceof Map)
479: return _optionsDataSource;
480:
481: Iterator it;
482: it = IteratorFactory.createIterator(_optionsDataSource);
483: if (it == null)
484: it = IteratorFactory.EMPTY_ITERATOR;
485:
486: assert (it != null && it instanceof Iterator);
487: return it;
488: }
489:
490: /**
491: * This will create a new option in the HTML.
492: */
493: protected void addOption(AbstractRenderAppender buffer,
494: String type, String optionValue, String optionDisplay,
495: int idx, String altText, char accessKey, boolean disabled)
496: throws JspException {
497: ServletRequest req = pageContext.getRequest();
498: if (_cr == null)
499: _cr = TagRenderingBase.Factory.getConstantRendering(req);
500:
501: assert (buffer != null);
502: assert (optionValue != null);
503: assert (optionDisplay != null);
504: assert (type != null);
505:
506: if (_orientation != null && isVertical()) {
507: _cr.TR_TD(buffer);
508: }
509:
510: _inputState.clear();
511: _inputState.type = type;
512: _inputState.name = getQualifiedDataSourceName();
513: _inputState.value = optionValue;
514: _inputState.style = _style;
515: _inputState.styleClass = _class;
516:
517: if (isMatched(optionValue, null)) {
518: _inputState.checked = true;
519: }
520: _inputState.disabled = isDisabled();
521: _inputState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
522: ALT, altText);
523: if (accessKey != 0x00)
524: _inputState.registerAttribute(
525: AbstractHtmlState.ATTR_GENERAL, ACCESSKEY,
526: Character.toString(accessKey));
527:
528: // if there are attributes defined push them to the options.
529: if (_attrs != null && _attrs.size() > 0) {
530: Iterator iterator = _attrs.keySet().iterator();
531: for (; iterator.hasNext();) {
532: String key = (String) iterator.next();
533: if (key == null)
534: continue;
535:
536: String value = (String) _attrs.get(key);
537: _inputState.registerAttribute(
538: AbstractHtmlState.ATTR_GENERAL, key, value);
539: }
540: }
541:
542: TagRenderingBase br = TagRenderingBase.Factory.getRendering(
543: TagRenderingBase.INPUT_BOOLEAN_TAG, req);
544: br.doStartTag(buffer, _inputState);
545: br.doEndTag(buffer);
546:
547: String ls = _labelStyle;
548: String lsc = _labelStyleClass;
549:
550: _spanState.style = ls;
551: _spanState.styleClass = lsc;
552:
553: br = TagRenderingBase.Factory.getRendering(
554: TagRenderingBase.SPAN_TAG, req);
555: br.doStartTag(buffer, _spanState);
556: buffer.append(optionDisplay);
557: br.doEndTag(buffer);
558:
559: // backward compatibility this is now overridden by the _orientation
560: if (_orientation == null) {
561: _cr.BR(buffer);
562: } else {
563: if (isVertical()) {
564: _cr.TR_TD(buffer);
565: } else {
566: _cr.NBSP(buffer);
567: }
568: }
569: }
570:
571: /**
572: * Release any acquired resources.
573: */
574: protected void localRelease() {
575: super .localRelease();
576: if (_attrs != null)
577: _attrs.clear();
578: _cr = null;
579: _dataSource = null;
580: _defaultValue = null;
581: _optionsDataSource = null;
582: _disabled = false;
583: _orientation = null;
584: _orientVal = null;
585: _realName = null;
586: _style = null;
587: _class = null;
588: _labelStyle = null;
589: _labelStyleClass = null;
590:
591: _repIdx = 0;
592: _repCurItem = null;
593: }
594:
595: }
|