KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > woody > formmodel > Field


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.woody.formmodel;
17
18 import org.apache.cocoon.woody.Constants;
19 import org.apache.cocoon.woody.FormContext;
20 import org.apache.cocoon.woody.util.I18nMessage;
21 import org.apache.cocoon.woody.validation.ValidationError;
22 import org.apache.cocoon.woody.validation.ValidationErrorAware;
23 import org.apache.cocoon.woody.datatype.SelectionList;
24 import org.apache.cocoon.woody.datatype.Datatype;
25 import org.apache.cocoon.woody.event.DeferredValueChangedEvent;
26 import org.apache.cocoon.woody.event.WidgetEvent;
27 import org.apache.cocoon.woody.event.ValueChangedEvent;
28 import org.apache.cocoon.xml.AttributesImpl;
29 import org.xml.sax.ContentHandler JavaDoc;
30 import org.xml.sax.SAXException JavaDoc;
31
32 import java.util.Locale JavaDoc;
33
34 /**
35  * A general-purpose Widget that can hold one value. A Field widget can be associated
36  * with a {@link org.apache.cocoon.woody.datatype.Datatype Datatype}, and thus
37  * a Field widget can be used to edit different kinds of data, such as strings,
38  * numbers and dates. A Datatype can also have an associated SelectionList, so
39  * that the value for the Field can be selected from a list, rather than being
40  * entered in a textbox. The validation of the field is delegated to its associated
41  * Datatype.
42  *
43  * @author Bruno Dumon
44  * @author <a HREF="http://www.apache.org/~sylvain/">Sylvain Wallez</a>
45  * @version CVS $Id: Field.java 30932 2004-07-29 17:35:38Z vgritsenko $
46  */

47 public class Field extends AbstractWidget implements ValidationErrorAware, DataWidget, SelectableWidget {
48     protected SelectionList selectionList;
49
50     protected String JavaDoc enteredValue;
51     protected Object JavaDoc value;
52
53     // At startup, we don't need to parse (both enteredValue and value are null),
54
// but need to validate (error if field is required)
55
protected boolean needsParse = true;
56     protected boolean needsValidate = true;
57     private boolean isValidating;
58     protected ValidationError validationError;
59
60
61     public Field(FieldDefinition fieldDefinition) {
62         setDefinition(fieldDefinition);
63         setLocation(fieldDefinition.getLocation());
64     }
65
66     public final FieldDefinition getFieldDefinition() {
67         return (FieldDefinition)super.definition;
68     }
69
70     public String JavaDoc getId() {
71         return definition.getId();
72     }
73
74     public Object JavaDoc getValue() {
75         // Parse the value
76
if (this.needsParse) {
77             // Clear value, it will be recomputed
78
this.value = null;
79             if (this.enteredValue != null) {
80                 // Parse the value
81
this.value = getDatatype().convertFromString(this.enteredValue, getForm().getLocale());
82                 if (this.value != null) { // Conversion successfull
83
this.needsParse = false;
84                     this.needsValidate = true;
85                 } else { // Conversion failed
86
this.validationError = new ValidationError(new I18nMessage(
87                         "datatype.conversion-failed",
88                         new String JavaDoc[] {"datatype." + getDatatype().getDescriptiveName()},
89                         new boolean[] { true },
90                         Constants.I18N_CATALOGUE
91                     ));
92                     // No need for further validation (and need to keep the above error)
93
this.needsValidate = false;
94                 }
95             } else {
96                 this.needsParse = false;
97                 this.needsValidate = true;
98             }
99         }
100
101         // if getValue() is called on this field while we're validating, then it's because a validation
102
// rule called getValue(), so then we just return the parsed (but not validated) value to avoid an endless loop
103
if (isValidating) {
104             return value;
105         }
106
107         // Validate the value
108
if (this.needsValidate) {
109             isValidating = true;
110             try {
111                 if (super.validate(null)) {
112                     // New-style validators were successful. Check the old-style ones.
113
if (this.value != null) {
114                         this.validationError = getDatatype().validate(value, new ExpressionContextImpl(this));
115                     } else { // No value : is it required ?
116
if (getFieldDefinition().isRequired()) {
117                             this.validationError = new ValidationError(new I18nMessage("general.field-required", Constants.I18N_CATALOGUE));
118                         }
119                     }
120                 }
121                 this.needsValidate = false;
122             } finally {
123                 isValidating = false;
124             }
125         }
126         return this.validationError == null ? this.value : null;
127     }
128
129     public void setValue(Object JavaDoc newValue) {
130         if (newValue != null && !getDatatype().getTypeClass().isAssignableFrom(newValue.getClass())) {
131             throw new RuntimeException JavaDoc("Incorrect value type for \"" + getFullyQualifiedId() +
132                            "\" (expected " + getDatatype().getTypeClass() +
133                            ", got " + newValue.getClass() + ".");
134         }
135         Object JavaDoc oldValue = this.value;
136         boolean changed = ! (oldValue == null ? "" : oldValue).equals(newValue == null ? "" : newValue);
137         // Do something only if value is different or null
138
// (null allows to reset validation error)
139
if (changed || newValue == null) {
140             this.value = newValue;
141             this.needsParse = false;
142             this.validationError = null;
143             // Force validation, even if set by the application
144
this.needsValidate = true;
145             if (newValue != null) {
146                 this.enteredValue = getDatatype().convertToString(newValue, getForm().getLocale());
147             } else {
148                 this.enteredValue = null;
149             }
150             if (changed) {
151                 getForm().addWidgetEvent(new ValueChangedEvent(this, oldValue, newValue));
152             }
153         }
154     }
155
156     public void readFromRequest(FormContext formContext) {
157         String JavaDoc newEnteredValue = formContext.getRequest().getParameter(getFullyQualifiedId());
158         readFromRequest(newEnteredValue);
159     }
160
161     protected void readFromRequest(String JavaDoc newEnteredValue) {
162         // whitespace & empty field handling
163
if (newEnteredValue != null) {
164             // TODO make whitespace behaviour configurable !!
165
newEnteredValue = newEnteredValue.trim();
166             if (newEnteredValue.length() == 0) {
167                 newEnteredValue = null;
168             }
169         }
170
171         // Only convert if the text value actually changed. Otherwise, keep the old value
172
// and/or the old validation error (allows to keep errors when clicking on actions)
173
if (!(newEnteredValue == null ? "" : newEnteredValue).equals((enteredValue == null ? "" : enteredValue))) {
174             getForm().addWidgetEvent(new DeferredValueChangedEvent(this, value));
175             enteredValue = newEnteredValue;
176             validationError = null;
177             value = null;
178             needsParse = true;
179         }
180
181         // Always revalidate, as validation may depend on the value of other fields
182
this.needsValidate = true;
183     }
184
185     public boolean validate(FormContext formContext) {
186         // If needed, getValue() will do the validation
187
getValue();
188         return this.validationError == null;
189     }
190
191     /**
192      * Returns the validation error, if any. There will always be a validation error in case the
193      * {@link #validate(FormContext)} method returned false.
194      */

195     public ValidationError getValidationError() {
196         return validationError;
197     }
198
199     /**
200      * Set a validation error on this field. This allows fields to be externally marked as invalid by
201      * application logic.
202      *
203      * @param error the validation error
204      */

205     public void setValidationError(ValidationError error) {
206         this.validationError = error;
207     }
208
209     public boolean isRequired() {
210         return getFieldDefinition().isRequired();
211     }
212
213
214     private static final String JavaDoc FIELD_EL = "field";
215     private static final String JavaDoc VALUE_EL = "value";
216     private static final String JavaDoc VALIDATION_MSG_EL = "validation-message";
217
218     public void generateSaxFragment(ContentHandler JavaDoc contentHandler, Locale JavaDoc locale) throws SAXException JavaDoc {
219         AttributesImpl fieldAttrs = new AttributesImpl();
220         fieldAttrs.addCDATAAttribute("id", getFullyQualifiedId());
221         fieldAttrs.addCDATAAttribute("required", String.valueOf(isRequired()));
222         contentHandler.startElement(Constants.WI_NS, FIELD_EL, Constants.WI_PREFIX_COLON + FIELD_EL, fieldAttrs);
223
224         if (enteredValue != null || value != null) {
225             contentHandler.startElement(Constants.WI_NS, VALUE_EL, Constants.WI_PREFIX_COLON + VALUE_EL, Constants.EMPTY_ATTRS);
226             String JavaDoc stringValue;
227             if (value != null) {
228                 stringValue = getDatatype().convertToString(value, locale);
229             } else {
230                 stringValue = enteredValue;
231             }
232             contentHandler.characters(stringValue.toCharArray(), 0, stringValue.length());
233             contentHandler.endElement(Constants.WI_NS, VALUE_EL, Constants.WI_PREFIX_COLON + VALUE_EL);
234         }
235
236         // validation message element: only present if the value is not valid
237
if (validationError != null) {
238             contentHandler.startElement(Constants.WI_NS, VALIDATION_MSG_EL, Constants.WI_PREFIX_COLON + VALIDATION_MSG_EL, Constants.EMPTY_ATTRS);
239             validationError.generateSaxFragment(contentHandler);
240             contentHandler.endElement(Constants.WI_NS, VALIDATION_MSG_EL, Constants.WI_PREFIX_COLON + VALIDATION_MSG_EL);
241         }
242
243         // generate label, help, hint, etc.
244
definition.generateDisplayData(contentHandler);
245
246         // generate selection list, if any
247
if (selectionList != null) {
248             selectionList.generateSaxFragment(contentHandler, locale);
249         } else if (getFieldDefinition().getSelectionList() != null) {
250             getFieldDefinition().getSelectionList().generateSaxFragment(contentHandler, locale);
251         }
252         contentHandler.endElement(Constants.WI_NS, FIELD_EL, Constants.WI_PREFIX_COLON + FIELD_EL);
253     }
254
255     public void generateLabel(ContentHandler JavaDoc contentHandler) throws SAXException JavaDoc {
256         definition.generateLabel(contentHandler);
257     }
258
259     /**
260      * Set this field's selection list.
261      * @param selectionList The new selection list.
262      */

263     public void setSelectionList(SelectionList selectionList) {
264         if (selectionList != null &&
265             selectionList.getDatatype() != null &&
266             selectionList.getDatatype() != getDatatype()) {
267             throw new RuntimeException JavaDoc("Tried to assign a SelectionList that is not associated with this widget's datatype.");
268         }
269         this.selectionList = selectionList;
270     }
271
272     /**
273      * Read this field's selection list from an external source.
274      * All Cocoon-supported protocols can be used.
275      * The format of the XML produced by the source should be the
276      * same as in case of inline specification of the selection list,
277      * thus the root element should be a <code>wd:selection-list</code>
278      * element.
279      * @param uri The URI of the source.
280      */

281     public void setSelectionList(String JavaDoc uri) {
282         setSelectionList(getFieldDefinition().buildSelectionList(uri));
283     }
284
285     /**
286      * Set this field's selection list using values from an in-memory
287      * object. The <code>object</code> parameter should point to a collection
288      * (Java collection or array, or Javascript array) of objects. Each object
289      * belonging to the collection should have a <em>value</em> property and a
290      * <em>label</em> property, whose values are used to specify the <code>value</code>
291      * attribute and the contents of the <code>wd:label</code> child element
292      * of every <code>wd:item</code> in the list.
293      * <p>Access to the values of the above mentioned properties is done
294      * via <a HREF="http://jakarta.apache.org/commons/jxpath/users-guide.html">XPath</a> expressions.
295      * @param model The collection used as a model for the selection list.
296      * @param valuePath An XPath expression referring to the attribute used
297      * to populate the values of the list's items.
298      * @param labelPath An XPath expression referring to the attribute used
299      * to populate the labels of the list's items.
300      */

301     public void setSelectionList(Object JavaDoc model, String JavaDoc valuePath, String JavaDoc labelPath) {
302         setSelectionList(getFieldDefinition().buildSelectionListFromModel(model, valuePath, labelPath));
303     }
304
305     public Datatype getDatatype() {
306         return getFieldDefinition().getDatatype();
307     }
308
309     public void broadcastEvent(WidgetEvent event) {
310         getFieldDefinition().fireValueChangedEvent((ValueChangedEvent)event);
311     }
312 }
313
Popular Tags