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: package org.apache.cocoon.forms.formmodel;
018:
019: import java.util.Locale;
020:
021: import org.apache.cocoon.forms.FormContext;
022: import org.apache.cocoon.forms.FormsConstants;
023: import org.apache.cocoon.forms.event.ValueChangedEvent;
024: import org.apache.cocoon.forms.event.ValueChangedListener;
025: import org.apache.cocoon.forms.event.ValueChangedListenerEnabled;
026: import org.apache.cocoon.forms.event.WidgetEvent;
027: import org.apache.cocoon.forms.event.WidgetEventMulticaster;
028: import org.apache.cocoon.forms.validation.ValidationError;
029: import org.apache.cocoon.forms.validation.ValidationErrorAware;
030: import org.apache.cocoon.xml.AttributesImpl;
031: import org.apache.cocoon.xml.XMLUtils;
032:
033: import org.apache.commons.lang.BooleanUtils;
034: import org.xml.sax.ContentHandler;
035: import org.xml.sax.SAXException;
036:
037: /**
038: * A widget to select a boolean value. Usually rendered as a checkbox.
039: *
040: * <p>You may wonder why we don't use a {@link Field} widget with an associated
041: * Boolean Datatype instead. The reason is that many of the features of the Field
042: * widget are overkill for a Boolean: validation is unnecessary (if the field is
043: * not true it is false), the selectionlist associated with a Datatype also
044: * has no purpose here (there would always be only 2 choices: true or false),
045: * and the manner in which the request parameter of this widget is interpreted
046: * is different (missing or empty request parameter means 'false', rather than null value).
047: *
048: * @version $Id: BooleanField.java 479296 2006-11-26 06:28:51Z antonio $
049: */
050: public class BooleanField extends AbstractWidget implements
051: ValidationErrorAware, ValueChangedListenerEnabled {
052:
053: private static final String BOOLEAN_FIELD_EL = "booleanfield";
054: private static final String VALUE_EL = "value";
055: private static final String VALIDATION_MSG_EL = "validation-message";
056:
057: // FIXME(SW) : should the initial value be false or null ? This would allow
058: // event listeners to be triggered at bind time.
059: private Boolean value = Boolean.FALSE;
060: private final BooleanFieldDefinition definition;
061: /** Additional listeners to those defined as part of the widget definition (if any). */
062: private ValueChangedListener listener;
063: protected ValidationError validationError;
064:
065: public BooleanField(BooleanFieldDefinition definition) {
066: super (definition);
067: this .definition = definition;
068: this .listener = definition.getValueChangedListener();
069: }
070:
071: public WidgetDefinition getDefinition() {
072: return this .definition;
073: }
074:
075: public void initialize() {
076: Boolean value = this .definition.getInitialValue();
077: if (value != null) {
078: setValue(value);
079: }
080: super .initialize();
081: }
082:
083: public void readFromRequest(FormContext formContext) {
084: if (!getCombinedState().isAcceptingInputs()) {
085: return;
086: }
087:
088: validationError = null;
089: Object oldValue = value;
090: String param = formContext.getRequest().getParameter(
091: getRequestParameterName());
092:
093: value = BooleanUtils.toBooleanObject(definition
094: .getTrueParamValue().equals(param));
095:
096: if (!value.equals(oldValue)) {
097: getForm().addWidgetEvent(
098: new ValueChangedEvent(this , oldValue, value));
099: }
100: }
101:
102: /**
103: * Returns the validation error, if any. There will always be a validation error in case the
104: * {@link #validate()} method returned false.
105: */
106: public ValidationError getValidationError() {
107: return validationError;
108: }
109:
110: /**
111: * Set a validation error on this field. This allows fields to be externally marked as invalid by
112: * application logic.
113: *
114: * @param error the validation error
115: */
116: public void setValidationError(ValidationError error) {
117: this .validationError = error;
118: getForm().addWidgetUpdate(this );
119: }
120:
121: /**
122: * @return "booleanfield"
123: */
124: public String getXMLElementName() {
125: return BOOLEAN_FIELD_EL;
126: }
127:
128: protected AttributesImpl getXMLElementAttributes() {
129: AttributesImpl attrs = super .getXMLElementAttributes();
130: // Add the parameter value for true
131: attrs.addCDATAAttribute("true-value", definition
132: .getTrueParamValue());
133: return attrs;
134: }
135:
136: public void generateItemSaxFragment(ContentHandler contentHandler,
137: Locale locale) throws SAXException {
138: // value element
139: contentHandler.startElement(FormsConstants.INSTANCE_NS,
140: VALUE_EL, FormsConstants.INSTANCE_PREFIX_COLON
141: + VALUE_EL, XMLUtils.EMPTY_ATTRIBUTES);
142:
143: String stringValue = BooleanUtils.toBoolean(value) ? definition
144: .getTrueParamValue() : "false";
145:
146: contentHandler.characters(stringValue.toCharArray(), 0,
147: stringValue.length());
148: contentHandler.endElement(FormsConstants.INSTANCE_NS, VALUE_EL,
149: FormsConstants.INSTANCE_PREFIX_COLON + VALUE_EL);
150:
151: // validation message element: only present if the value is not valid
152: if (validationError != null) {
153: contentHandler.startElement(FormsConstants.INSTANCE_NS,
154: VALIDATION_MSG_EL,
155: FormsConstants.INSTANCE_PREFIX_COLON
156: + VALIDATION_MSG_EL,
157: XMLUtils.EMPTY_ATTRIBUTES);
158: validationError.generateSaxFragment(contentHandler);
159: contentHandler.endElement(FormsConstants.INSTANCE_NS,
160: VALIDATION_MSG_EL,
161: FormsConstants.INSTANCE_PREFIX_COLON
162: + VALIDATION_MSG_EL);
163: }
164: }
165:
166: public Object getValue() {
167: return value;
168: }
169:
170: /**
171: * Sets value of the field. If value is null, it is considered to be false
172: * (see class comment).
173: */
174: public void setValue(Object object) {
175: if (object == null) {
176: object = Boolean.FALSE;
177: }
178:
179: if (!(object instanceof Boolean)) {
180: throw new RuntimeException(
181: "Cannot set value of boolean field '"
182: + getRequestParameterName()
183: + "' to a non-Boolean value.");
184: }
185:
186: Object oldValue = value;
187: value = (Boolean) object;
188: if (!value.equals(oldValue)) {
189: Form form = getForm();
190: if (hasValueChangedListeners()
191: || this .getForm().hasFormHandler()) {
192: form.addWidgetEvent(new ValueChangedEvent(this ,
193: oldValue, value));
194: }
195: form.addWidgetUpdate(this );
196: }
197: }
198:
199: /**
200: * Adds a ValueChangedListener to this widget instance. Listeners defined
201: * on the widget instance will be executed in addtion to any listeners
202: * that might have been defined in the widget definition.
203: */
204: public void addValueChangedListener(ValueChangedListener listener) {
205: this .listener = WidgetEventMulticaster.add(this .listener,
206: listener);
207: }
208:
209: public void removeValueChangedListener(ValueChangedListener listener) {
210: this .listener = WidgetEventMulticaster.remove(this .listener,
211: listener);
212: }
213:
214: public boolean hasValueChangedListeners() {
215: return this .listener != null;
216: }
217:
218: public void broadcastEvent(WidgetEvent event) {
219: if (event instanceof ValueChangedEvent) {
220: if (this .listener != null) {
221: this .listener.valueChanged((ValueChangedEvent) event);
222: }
223: } else {
224: // Other kinds of events
225: super.broadcastEvent(event);
226: }
227: }
228: }
|