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.pageflow.ProcessPopulate;
022: import org.apache.beehive.netui.pageflow.RequestParameterHandler;
023: import org.apache.beehive.netui.tags.ByRef;
024: import org.apache.beehive.netui.tags.IHtmlAccessable;
025: import org.apache.beehive.netui.tags.naming.FormDataNameInterceptor;
026: import org.apache.beehive.netui.tags.naming.IndexedNameInterceptor;
027: import org.apache.beehive.netui.tags.naming.PrefixNameInterceptor;
028: import org.apache.beehive.netui.tags.rendering.*;
029: import org.apache.beehive.netui.util.Bundle;
030: import org.apache.beehive.netui.util.logging.Logger;
031:
032: import javax.servlet.ServletRequest;
033: import javax.servlet.http.HttpServletRequest;
034: import javax.servlet.jsp.JspException;
035: import javax.servlet.jsp.tagext.Tag;
036: import java.util.ArrayList;
037: import java.util.Collections;
038: import java.util.List;
039:
040: /**
041: * Generates a checkbox which binds to a form bean property or databound expression.
042: * CheckBox should be used on its own and not within a CheckBoxGroup. CheckBox ignores its
043: * body content.
044: *
045: * CheckBoxes can bind to boolean, Boolean, and Strings.
046: * @jsptagref.tagdescription <p>Generates a single HTML checkbox. The <netui:checkBox> tag should be used on its own, not within a
047: * {@link CheckBoxGroup}.
048: *
049: * <p>The <netui:checkBox> tag can be data bound to a boolean or Boolean type. For instance,
050: * the following <netui:checkBox> tag...
051: *
052: * <pre> <netui:checkBox dataSource="actionForm.checkBoxValue"/></pre>
053: *
054: * ...must be bound to a boolean or Boolean field in the Form Bean...
055: *
056: * <pre> public static class ProcessDataForm extends FormData
057: * {
058: * private boolean checkBoxValue;
059: *
060: * public void setCheckBoxValue(boolean checkBoxValue)
061: * {
062: * this.checkBoxValue = checkBoxValue;
063: * }
064: *
065: * public boolean isCheckBoxValue()
066: * {
067: * return this.checkBoxValue;
068: * }
069: * }</pre>
070: * @example In this sample, the <netui:checkBox reads it initial value from the
071: * Form Bean field <code>wantSpecialOffers</code>. Upon submission, the user specified value is
072: * loaded into the same Form Bean field. The data is submitted to the
073: * action method <code>processData</code>.
074: * <pre> <netui:form action="processData">
075: * Do you want to be notified of special offers?
076: * <netui:checkBox dataSource="actionForm.wantsSpecialOffers"/><br>
077: * <netui:button value="Submit" type="submit"/>
078: * </netui:form></pre>
079: * @netui:tag name="checkBox" description="Generates a checkbox that binds to a form bean property or databound expression."
080: */
081: public class CheckBox extends HtmlDefaultableDataSourceTag implements
082: IHtmlAccessable {
083: private static final Logger logger = Logger
084: .getInstance(CheckBox.class);
085:
086: private InputBooleanTag.State _state = new InputBooleanTag.State();
087: private InputHiddenTag.State _hiddenState = new InputHiddenTag.State();
088:
089: private static final String CHECKBOX_KEY = "checkbox_key";
090: private static final String OLDVALUE_SUFFIX = "OldValue";
091:
092: private static final List _internalNamingChain;
093:
094: static {
095: List l = new ArrayList(3);
096: l.add(new FormDataNameInterceptor());
097: l.add(new IndexedNameInterceptor());
098: l.add(new PrefixNameInterceptor(CHECKBOX_KEY));
099: _internalNamingChain = Collections.unmodifiableList(l);
100: }
101:
102: static {
103: org.apache.beehive.netui.pageflow.ProcessPopulate
104: .registerPrefixHandler(CHECKBOX_KEY,
105: new CheckBoxPrefixHandler());
106: }
107:
108: /**
109: * The handler for naming and indexing the CheckBox.
110: */
111: public static class CheckBoxPrefixHandler implements
112: RequestParameterHandler {
113: /**
114: * Determines the current state of the CheckBox (true or false) based on the Request.
115: */
116: public void process(HttpServletRequest request, String key,
117: String expr, ProcessPopulate.ExpressionUpdateNode node) {
118: String returnVal = null;
119: if (!key.endsWith(OLDVALUE_SUFFIX)) {
120: //This checkbox is true and should stay that way
121: returnVal = "true";
122: } else {
123: //Check the request to see if checkBox also exists
124: String newKey = key.substring(0, key
125: .indexOf(OLDVALUE_SUFFIX));
126: String checkBox = request.getParameter(newKey);
127: if (checkBox != null) {
128: returnVal = "true";
129: } else {
130: returnVal = "false";
131: }
132: }
133:
134: if (node.expression.endsWith(OLDVALUE_SUFFIX)) {
135: node.expression = node.expression.substring(0,
136: node.expression.indexOf(OLDVALUE_SUFFIX));
137:
138: }
139:
140: node.values = new String[] { returnVal };
141:
142: if (logger.isDebugEnabled()) {
143: logger
144: .debug("*********************************************\n"
145: + "process with key \""
146: + key
147: + "\" and expression \""
148: + node.expression
149: + "\""
150: + "and result: "
151: + returnVal
152: + "\n"
153: + "*********************************************\n");
154: }
155: }
156: }
157:
158: public CheckBox() {
159: super ();
160: }
161:
162: /**
163: * Return the name of the Tag.
164: */
165: public String getTagName() {
166: return "CheckBox";
167: }
168:
169: /**
170: * Base support for the attribute tag. This is overridden to prevent setting the <code>type</code>,
171: * <code>checked</code> and <code>value</code> attributes.
172: * @param name The name of the attribute. This value may not be null or the empty string.
173: * @param value The value of the attribute. This may contain an expression.
174: * @param facet The name of a facet to which the attribute will be applied. This is optional.
175: * @throws JspException A JspException may be thrown if there is an error setting the attribute.
176: */
177: public void setAttribute(String name, String value, String facet)
178: throws JspException {
179: if (name != null) {
180: if (name.equals(TYPE) || name.equals(VALUE)
181: || name.equals(CHECKED)) {
182: String s = Bundle.getString(
183: "Tags_AttributeMayNotBeSet",
184: new Object[] { name });
185: registerTagError(s, null);
186: } else {
187: if (name.equals(DISABLED)) {
188: setDisabled(Boolean.parseBoolean(value));
189: return;
190: }
191: }
192: }
193:
194: super .setAttribute(name, value, facet);
195: }
196:
197: /**
198: * This method will return the state associated with the tag. This is used by this
199: * base class to access the individual state objects created by the tags.
200: * @return a subclass of the <code>AbstractHtmlState</code> class.
201: */
202: protected AbstractHtmlState getState() {
203: return _state;
204: }
205:
206: /**
207: * Return an <code>ArrayList</code> which represents a chain of <code>INameInterceptor</code>
208: * objects. This method by default returns <code>null</code> and should be overridden
209: * by objects that support naming.
210: * @return an <code>ArrayList</code> that will contain <code>INameInterceptor</code> objects.
211: */
212: protected List getNamingChain() {
213: return _internalNamingChain;
214: }
215:
216: /**
217: * @return A boolean value.
218: */
219: private boolean evaluateDefaultValue() {
220: if (_defaultValue instanceof String)
221: return Boolean.valueOf((String) _defaultValue)
222: .booleanValue();
223: if (_defaultValue instanceof Boolean)
224: return ((Boolean) _defaultValue).booleanValue();
225: return false;
226: }
227:
228: /**
229: * Render the checkbox.
230: * @throws JspException if a JSP exception has occurred
231: */
232: public int doStartTag() throws JspException {
233: // evaluate the body so that we can set the attributes
234: return EVAL_BODY_BUFFERED;
235: }
236:
237: /**
238: * Save the body content of the checkbox.
239: * @throws JspException if a JSP exception has occurred
240: */
241: public int doAfterBody() throws JspException {
242: return SKIP_BODY;
243: }
244:
245: /**
246: * Render the checkbox.
247: * @throws JspException if a JSP exception has occurred
248: */
249: public int doEndTag() throws JspException {
250: Tag parent = getParent();
251: if (parent instanceof CheckBoxGroup)
252: registerTagError(Bundle
253: .getString("Tags_CheckBoxGroupChildError"), null);
254:
255: Object val = evaluateDataSource();
256: if (hasErrors())
257: return reportAndExit(EVAL_PAGE);
258:
259: ByRef ref = new ByRef();
260: nameHtmlControl(_state, ref);
261:
262: String hiddenParamName = _state.name + OLDVALUE_SUFFIX;
263: ServletRequest req = pageContext.getRequest();
264:
265: if (val instanceof String) {
266: if (val != null
267: && Boolean.valueOf(val.toString()).booleanValue())
268: _state.checked = true;
269: else
270: _state.checked = false;
271: } else if (val instanceof Boolean) {
272: _state.checked = ((Boolean) val).booleanValue();
273: } else {
274: String oldCheckBoxValue = req.getParameter(hiddenParamName);
275: if (oldCheckBoxValue != null) {
276: _state.checked = new Boolean(oldCheckBoxValue)
277: .booleanValue();
278: } else {
279: _state.checked = evaluateDefaultValue();
280: }
281: }
282: _state.disabled = isDisabled();
283:
284: //Create a hidden field to store the CheckBox oldValue
285: String oldValue = req.getParameter(_state.name);
286: WriteRenderAppender writer = new WriteRenderAppender(
287: pageContext);
288:
289: // if the checkbox is disabled we need to not write out the hidden
290: // field because it can cause the default state to change from
291: // true to false. Disabled check boxes do not postback.
292: if (!_state.disabled) {
293: _hiddenState.name = hiddenParamName;
294: if (oldValue == null) {
295: _hiddenState.value = "false";
296: } else {
297: _hiddenState.value = oldValue;
298: }
299: TagRenderingBase hiddenTag = TagRenderingBase.Factory
300: .getRendering(TagRenderingBase.INPUT_HIDDEN_TAG,
301: req);
302: hiddenTag.doStartTag(writer, _hiddenState);
303: hiddenTag.doEndTag(writer);
304: }
305:
306: _state.type = INPUT_CHECKBOX;
307:
308: TagRenderingBase br = TagRenderingBase.Factory.getRendering(
309: TagRenderingBase.INPUT_BOOLEAN_TAG, req);
310: br.doStartTag(writer, _state);
311:
312: if (!ref.isNull())
313: write((String) ref.getRef());
314:
315: // Continue processing this page
316: localRelease();
317: return EVAL_PAGE;
318: }
319:
320: /**
321: * Release any acquired resources.
322: */
323: protected void localRelease() {
324: super .localRelease();
325:
326: _state.clear();
327: _hiddenState.clear();
328: }
329:
330: /* ==================================================================
331: *
332: * This tag's publically exposed HTML, CSS, and JavaScript attributes
333: *
334: * ==================================================================
335: */
336:
337: /**
338: * Sets the accessKey attribute value. This should key value of the
339: * keyboard navigation key. It is recommended not to use the following
340: * values because there are often used by browsers <code>A, C, E, F, G,
341: * H, V, left arrow, and right arrow</code>.
342: * @param accessKey the accessKey value.
343: * @jsptagref.attributedescription The keyboard navigation key for the element.
344: * The following values are not recommended because they
345: * are often used by browsers: <code>A, C, E, F, G,
346: * H, V, left arrow, and right arrow</code>
347: * @jsptagref.databindable false
348: * @jsptagref.attributesyntaxvalue <i>string_accessKey</i>
349: * @netui:attribute required="false" rtexprvalue="true" type="char"
350: * description="The keyboard navigation key for the element.
351: * The following values are not recommended because they
352: * are often used by browsers: A, C, E, F, G,
353: * H, V, left arrow, and right arrow"
354: */
355: public void setAccessKey(char accessKey) {
356: if (accessKey == 0x00)
357: return;
358: _state.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
359: ACCESSKEY, Character.toString(accessKey));
360: }
361:
362: /**
363: * Sets the alt attribute value.
364: * @param alt the alt value.
365: * @jsptagref.attributedescription The alt attribute of the element.
366: * @jsptagref.databindable Read Only
367: * @jsptagref.attributesyntaxvalue <i>string_alt</i>
368: * @netui:attribute required="false" rtexprvalue="true"
369: * description="The alt attribute of the element."
370: */
371: public void setAlt(String alt) {
372: _state.registerAttribute(AbstractHtmlState.ATTR_GENERAL, ALT,
373: alt);
374: }
375:
376: /**
377: * Sets the tabIndex of the rendered html tag.
378: * @param tabindex the tab index.
379: * @jsptagref.attributedescription The tabIndex of the rendered HTML tag. This attribute determines the position of the
380: * tag in the sequence of page elements that the user may advance through by pressing the TAB key.
381: * @jsptagref.databindable false
382: * @jsptagref.attributesyntaxvalue <i>string_tabIndex</i>
383: * @netui:attribute required="false" rtexprvalue="true" type="int"
384: * description="The tabIndex of the rendered HTML tag. This attribute determines the position of the
385: * tag in the sequence of page elements that the user may advance through by pressing the TAB key."
386: */
387: public void setTabindex(int tabindex) {
388: _state.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
389: TABINDEX, Integer.toString(tabindex));
390: }
391: }
|