KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > au > id > jericho > lib > html > FormControl


1 // Jericho HTML Parser - Java based library for analysing and manipulating HTML
2
// Version 2.2
3
// Copyright (C) 2006 Martin Jericho
4
// http://sourceforge.net/projects/jerichohtml/
5
//
6
// This library is free software; you can redistribute it and/or
7
// modify it under the terms of the GNU Lesser General Public
8
// License as published by the Free Software Foundation; either
9
// version 2.1 of the License, or (at your option) any later version.
10
// http://www.gnu.org/copyleft/lesser.html
11
//
12
// This library is distributed in the hope that it will be useful,
13
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
// Lesser General Public License for more details.
16
//
17
// You should have received a copy of the GNU Lesser General Public
18
// License along with this library; if not, write to the Free Software
19
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20

21 package au.id.jericho.lib.html;
22
23 import java.util.*;
24
25 /**
26  * Represents an HTML form <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#form-controls">control</a>.
27  * <p>
28  * A <code>FormControl</code> consists of a single {@linkplain #getElement() element}
29  * that matches one of the {@linkplain FormControlType form control types}.
30  * <p>
31  * The term <i><a name="OutputElement">output element</a></i> is used to describe the element that is
32  * {@linkplain OutputSegment#writeTo(Writer) output} if this form control is {@linkplain OutputDocument#replace(FormControl) replaced}
33  * in an {@link OutputDocument}.
34  * <p>
35  * A <i><a name="PredefinedValueControl">predefined value control</a></i> is a form control for which
36  * {@link #getFormControlType()}.{@link FormControlType#hasPredefinedValue() hasPredefinedValue()}
37  * returns <code>true</code>. It has a {@linkplain #getFormControlType() control type} of
38  * {@link FormControlType#CHECKBOX CHECKBOX}, {@link FormControlType#RADIO RADIO}, {@link FormControlType#BUTTON BUTTON},
39  * {@link FormControlType#SUBMIT SUBMIT}, {@link FormControlType#IMAGE IMAGE}, {@link FormControlType#SELECT_SINGLE SELECT_SINGLE}
40  * or {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE}.
41  * <p>
42  * A <i><a name="UserValueControl">user value control</a></i> is a form control for which
43  * {@link #getFormControlType()}.{@link FormControlType#hasPredefinedValue() hasPredefinedValue()}
44  * returns <code>false</code>. It has a {@linkplain #getFormControlType() control type} of
45  * {@link FormControlType#FILE FILE}, {@link FormControlType#HIDDEN HIDDEN}, {@link FormControlType#PASSWORD PASSWORD},
46  * {@link FormControlType#TEXT TEXT} or {@link FormControlType#TEXTAREA TEXTAREA}.
47  * <p>
48  * The functionality of most significance to users of this class relates to the
49  * <i><a name="DisplayCharacteristics">display characteristics</a></i> of the <a HREF="#OutputElement">output element</a>,
50  * manipulated using the {@link #setDisabled(boolean)} and {@link #setOutputStyle(FormControlOutputStyle)} methods.
51  * <p>
52  * As a general rule, the operations dealing with the control's <a HREF="#SubmissionValue">submission values</a>
53  * are better performed on a {@link FormFields} or {@link FormField} object, which provide a more
54  * intuitive interface by grouping form controls of the same {@linkplain #getName() name} together.
55  * The higher abstraction level of these classes means they can automatically ensure that the
56  * <a HREF="#SubmissionValue">submission values</a> of their constituent controls are consistent with each other,
57  * for example by ensuring that only one {@link FormControlType#RADIO RADIO} control with a given name is
58  * {@link #isChecked() checked} at a time.
59  * <p>
60  * A {@link FormFields} object can be directly {@linkplain FormFields#FormFields(Collection) constructed} from
61  * a collection of <code>FormControl</code> objects.
62  * <p>
63  * <code>FormControl</code> instances are obtained using the {@link Element#getFormControl()} method or are created automatically
64  * with the creation of a {@link FormFields} object via the {@link Segment#findFormFields()} method.
65  *
66  * @see FormControlType
67  * @see FormFields
68  * @see FormField
69  */

70 public abstract class FormControl extends Segment {
71     FormControlType formControlType;
72     String JavaDoc name;
73     ElementContainer elementContainer;
74     FormControlOutputStyle outputStyle=FormControlOutputStyle.NORMAL;
75
76     private static final String JavaDoc CHECKBOX_NULL_DEFAULT_VALUE="on";
77     private static Comparator COMPARATOR=new PositionComparator();
78
79     static FormControl construct(final Element element) {
80         final String JavaDoc tagName=element.getStartTag().getName();
81         if (tagName==Tag.INPUT) {
82             final String JavaDoc typeAttributeValue=element.getAttributes().getRawValue(Attribute.TYPE);
83             if (typeAttributeValue==null) return new InputFormControl(element,FormControlType.TEXT);
84             FormControlType formControlType=FormControlType.getFromInputElementType(typeAttributeValue.toLowerCase());
85             if (formControlType==null) {
86                 if (element.source.isLoggingEnabled()) element.source.log(element.source.getRowColumnVector(element.begin).appendTo(new StringBuffer JavaDoc(200)).append(": INPUT control with unrecognised type \"").append(typeAttributeValue).append("\" assumed to be type \"text\"").toString());
87                 formControlType=FormControlType.TEXT;
88             }
89             if (formControlType==FormControlType.TEXT) return new InputFormControl(element,formControlType);
90             if (formControlType==FormControlType.CHECKBOX || formControlType==FormControlType.RADIO) return new RadioCheckboxFormControl(element,formControlType);
91             if (formControlType==FormControlType.SUBMIT) return new SubmitFormControl(element,formControlType);
92             if (formControlType==FormControlType.IMAGE) return new ImageSubmitFormControl(element);
93             // formControlType is HIDDEN || PASSWORD || FILE
94
return new InputFormControl(element,formControlType);
95         } else if (tagName==Tag.SELECT) {
96             return new SelectFormControl(element);
97         } else if (tagName==Tag.TEXTAREA) {
98             return new TextAreaFormControl(element);
99         } else if (tagName==Tag.BUTTON) {
100             return element.getAttributes().getRawValue(Attribute.TYPE).equalsIgnoreCase("submit") ? new SubmitFormControl(element,FormControlType.BUTTON) : null;
101         } else {
102             return null;
103         }
104     }
105
106     private FormControl(final Element element, final FormControlType formControlType, final boolean loadPredefinedValue) {
107         super(element.source,element.begin,element.end);
108         elementContainer=new ElementContainer(element,loadPredefinedValue);
109         this.formControlType=formControlType;
110         name=element.getAttributes().getValue(Attribute.NAME);
111         verifyName();
112     }
113
114     /**
115      * Returns the {@linkplain FormControlType type} of this form control.
116      * @return the {@linkplain FormControlType type} of this form control.
117      */

118     public final FormControlType getFormControlType() {
119         return formControlType;
120     }
121
122     /**
123      * Returns the <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#control-name">name</a> of the control.
124      * <p>
125      * The name comes from the value of the <code>name</code> {@linkplain Attribute attribute} of the
126      * {@linkplain #getElement() form control's element}, not the {@linkplain Element#getName() name of the element} itself.
127      * <p>
128      * Since a {@link FormField} is simply a group of controls with the same name, the terms <i>control name</i> and
129      * <i>field name</i> are for the most part synonymous, with only a possible difference in case differentiating them.
130      * <p>
131      * In contrast to the {@link FormField#getName()} method, this method always returns the name using the original case
132      * from the source document, regardless of the current setting of the
133      * {@link Config#CurrentCompatibilityMode}<code>.</code>{@link Config.CompatibilityMode#isFormFieldNameCaseInsensitive() FormFieldNameCaseInsensitive} property.
134      *
135      * @return the <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#control-name">name</a> of the control.
136      */

137     public final String JavaDoc getName() {
138         return name;
139     }
140
141     /**
142      * Returns the {@linkplain Element element} representing this form control in the source document.
143      * <p>
144      * The {@linkplain Element#getAttributes() attributes} of this source element should correspond with the
145      * <a HREF="#OutputAttributes">output attributes</a> if the
146      * <a HREF="#DisplayCharacteristics">display characteristics</a> or <a HREF="FormField.html#SubmissionValue">submission values</a>
147      * have not been modified.
148      *
149      * @return the {@linkplain Element element} representing this form control in the source document.
150      */

151     public final Element getElement() {
152         return elementContainer.element;
153     }
154
155     /**
156      * Returns an iterator over the {@link Tag#OPTION OPTION} {@linkplain Element elements} contained within this control, in order of appearance.
157      * <p>
158      * This method is only relevant to form controls with a {@linkplain #getFormControlType() type} of
159      * {@link FormControlType#SELECT_SINGLE SELECT_SINGLE} or {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE}.
160      *
161      * @return an iterator over the {@link Tag#OPTION OPTION} {@linkplain Element elements} contained within this control, in order of appearance.
162      * @throws UnsupportedOperationException if the {@linkplain #getFormControlType() type} of this control is not {@link FormControlType#SELECT_SINGLE SELECT_SINGLE} or {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE}.
163      */

164     public Iterator getOptionElementIterator() {
165         // overridden in SelectFormControl
166
throw new UnsupportedOperationException JavaDoc("Only SELECT controls contain OPTION elements");
167     }
168
169     /**
170      * Returns the current {@linkplain FormControlOutputStyle output style} of this form control.
171      * <p>
172      * This property affects how this form control is displayed if it has been {@linkplain OutputDocument#replace(FormControl) replaced}
173      * in an {@link OutputDocument}.
174      * See the documentation of the {@link FormControlOutputStyle} class for information on the available output styles.
175      * <p>
176      * The default output style for every form control is {@link FormControlOutputStyle#NORMAL}.
177      *
178      * @return the current {@linkplain FormControlOutputStyle output style} of this form control.
179      * @see #setOutputStyle(FormControlOutputStyle)
180      */

181     public FormControlOutputStyle getOutputStyle() {
182         return outputStyle;
183     }
184
185     /**
186      * Sets the {@linkplain FormControlOutputStyle output style} of this form control.
187      * <p>
188      * See the {@link #getOutputStyle()} method for a full description of this property.
189      *
190      * @param outputStyle the new {@linkplain FormControlOutputStyle output style} of this form control.
191      */

192     public void setOutputStyle(final FormControlOutputStyle outputStyle) {
193         this.outputStyle=outputStyle;
194     }
195
196     /**
197      * Returns a map of the names and values of this form control's <a HREF="#OutputAttributes">output attributes</a>.
198      * <p>
199      * The term <i><a name="OutputAttributes">output attributes</a></i> is used in this library to refer to the
200      * <a target="_blank" HREF="http://www.w3.org/TR/html401/intro/sgmltut.html#h-3.2.2">attributes</a> of a form control's
201      * <a HREF="#OutputElement">output element</a>.
202      * <p>
203      * The map keys are the <code>String</code> attribute names, which should all be in lower case.
204      * The map values are the corresponding <code>CharSequence</code> attribute values, with a <code>null</code> value given
205      * to an attribute that {@linkplain Attribute#hasValue() has no value}.
206      * <p>
207      * Direct manipulation of the returned map affects the attributes of this form control's <a HREF="#OutputElement">output element</a>.
208      * It is the responsibility of the user to ensure that all entries added to the map use the correct key and value types,
209      * and that all keys (attribute names) are in lower case.
210      * <p>
211      * It is recommended that the <a HREF="#SubmissionValueModificationMethods">submission value modification methods</a>
212      * are used to modify attributes that affect the <a HREF="#SubmissionValue">submission value</a> of the control
213      * rather than manipulating the attributes map directly.
214      * <p>
215      * An iteration over the map entries will return the attributes in the same order as they appeared in the source document, or
216      * at the end if the attribute was not present in the source document.
217      * <p>
218      * The returned attributes only correspond with those of the {@linkplain #getElement() source element} if the control's
219      * <a HREF="#DisplayCharacteristics">display characteristics</a> and <a HREF="#SubmissionValue">submission values</a>
220      * have not been modified.
221      *
222      * @return a map of the names and values of this form control's <a HREF="#OutputAttributes">output attributes</a>.
223      */

224     public final Map getAttributesMap() {
225         return elementContainer.getAttributesMap();
226     }
227
228     /**
229      * Indicates whether this form control is <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-disabled">disabled</a>.
230      * <p>
231      * The form control is disabled if the attribute
232      * "<code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-disabled">disabled</a></code>"
233      * is present in its <a HREF="#OutputElement">output element</a>.
234      * <p>
235      * The return value is is logically equivalent to {@link #getAttributesMap()}<code>.containsKey("disabled")</code>,
236      * but using this property may be more efficient in some circumstances.
237      *
238      * @return <code>true</code> if this form control is <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-disabled">disabled</a>, otherwise <code>false</code>.
239      */

240     public final boolean isDisabled() {
241         return elementContainer.getBooleanAttribute(Attribute.DISABLED);
242     }
243
244     /**
245      * Sets whether this form control is <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-disabled">disabled</a>.
246      * <p>
247      * If the argument supplied to this method is <code>true</code> and the <code>disabled</code> attribute is not already present
248      * in the output element, the full
249      * <a target="_blank" HREF="http://www.w3.org/TR/xhtml1/">XHTML</a> compatible attribute <code>disabled="disabled"</code> is added.
250      * If the attribute is already present, it is left unchanged.
251      * <p>
252      * If the argument supplied to this method is <code>false</code>, the attribute is removed from the output element.
253      * <p>
254      * See the {@link #isDisabled()} method for more information.
255      *
256      * @param disabled the new value of this property.
257      */

258     public final void setDisabled(final boolean disabled) {
259         elementContainer.setBooleanAttribute(Attribute.DISABLED,disabled);
260     }
261
262     /**
263      * Indicates whether this form control is <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-checked">checked</a>.
264      * <p>
265      * The term <i>checked</i> is used to describe a checkbox or radio button control that is selected, which is the case if the attribute
266      * "<code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-checked">checked</a></code>"
267      * is present in its <a HREF="#OutputElement">output element</a>.
268      * <p>
269      * This property is only relevant to form controls with a {@linkplain #getFormControlType() type} of
270      * {@link FormControlType#CHECKBOX} or {@link FormControlType#RADIO}, and throws an <code>UnsupportedOperationException</code>
271      * for other control types.
272      * <p>
273      * Use one of the <a HREF="#SubmissionValueModificationMethods">submission value modification methods</a> to change the value
274      * of this property.
275      * <p>
276      * If this control is a checkbox, you can set it to checked by calling
277      * {@link #setValue(CharSequence) setValue}<code>(</code>{@link #getName()}<code>)</code>, and set it to unchecked by calling
278      * {@link #clearValues()}.
279      * <p>
280      * If this control is a radio button, you should use the {@link FormField#setValue(CharSequence)} method or one of the other
281      * higher level <a HREF="#SubmissionValueModificationMethods">submission value modification methods</a>
282      * to set the control to checked, as calling {@link #setValue(CharSequence)} method on this object
283      * in the same way as for a checkbox does not automatically uncheck all other radio buttons with the same name.
284      * Even calling {@link #clearValues()} on this object to ensure that this radio button is unchecked is not recommended, as
285      * it can lead to a situation where all the radio buttons with this name are unchecked.
286      * The <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#radio">HTML 4.01 specification of radio buttons</a>
287      * recommends against this situation because it is not defined how user agents should handle it, and behaviour differs amongst browsers.
288      * <p>
289      * The return value is logically equivalent to {@link #getAttributesMap()}<code>.containsKey("checked")</code>,
290      * but using this property may be more efficient in some circumstances.
291      *
292      * @return <code>true</code> if this form control is <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-checked">checked</a>, otherwise <code>false</code>.
293      * @throws UnsupportedOperationException if the {@linkplain #getFormControlType() type} of this control is not {@link FormControlType#CHECKBOX} or {@link FormControlType#RADIO}.
294      */

295     public boolean isChecked() {
296         throw new UnsupportedOperationException JavaDoc("This property is only relevant for CHECKBOX and RADIO controls");
297     }
298
299     /**
300      * Returns the <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#initial-value">initial value</a> of this control if it has a {@linkplain FormControlType#hasPredefinedValue() predefined value}.
301      * <p>
302      * Only <a HREF="#PredefinedValueControl">predefined value controls</a> can return a non-<code>null</code> result.
303      * All other control types return <code>null</code>.
304      * <p>
305      * {@link FormControlType#CHECKBOX CHECKBOX} and {@link FormControlType#RADIO RADIO} controls have a guaranteed
306      * predefined value determined by the value of its compulsory
307      * <code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-value-INPUT">value</a></code>
308      * attribute. If the attribute is not present in the source document, this library assigns the control a default
309      * predefined value of "<code>on</code>", consistent with popular browsers.
310      * <p>
311      * {@link FormControlType#SUBMIT SUBMIT}, {@link FormControlType#BUTTON BUTTON} and {@link FormControlType#IMAGE IMAGE}
312      * controls have an optional predefined value determined by the value of its
313      * <code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-value-INPUT">value</a></code>
314      * attribute. This value is
315      * <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#successful-controls">successful</a>
316      * only in the control used to submit the form.
317      * <p>
318      * {@link FormControlType#SELECT_SINGLE} and {@link FormControlType#SELECT_MULTIPLE} controls are special cases
319      * because they usually contain multiple
320      * <code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#edef-OPTION">OPTION</a></code>
321      * elements, each with its own predefined value.
322      * In this case the {@link #getPredefinedValues()} method should be used instead, which returns a collection of all the
323      * control's predefined values. Attempting to call this method on a <code>SELECT</code> control results in
324      * a <code>java.lang.UnsupportedOperationException</code>.
325      * <p>
326      * The predefined value of a control is not affected by changes to the
327      * <a HREF="#SubmissionValue">submission value</a> of the control.
328      *
329      * @return the <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#initial-value">initial value</a> of this control if it has a {@linkplain FormControlType#hasPredefinedValue() predefined value}, or <code>null</code> if none.
330      */

331     public String JavaDoc getPredefinedValue() {
332         return elementContainer.predefinedValue;
333     }
334
335     /**
336      * Returns a collection of all {@linkplain #getPredefinedValue() predefined values} in this control.
337      * <p>
338      * All objects in the returned collection are of type <code>String</code>, with no <code>null</code> entries.
339      * <p>
340      * This method is most useful for
341      * <code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#edef-SELECT">SELECT</a></code>
342      * controls since they typically contain multiple predefined values.
343      * In other controls it returns a collection with zero or one item based on the output of the
344      * {@link #getPredefinedValue()} method, so for efficiency it is recommended to use the
345      * {@link #getPredefinedValue()} method instead.
346      * <p>
347      * The multiple predefined values of a
348      * <code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#edef-SELECT">SELECT</a></code>
349      * control are defined by the
350      * <code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#edef-OPTION">OPTION</a></code>
351      * elements within it.
352      * Each <code>OPTION</code> element has an
353      * <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#initial-value">initial value</a>
354      * determined by the value of its
355      * <code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-value-OPTION">value</a></code>
356      * attribute, or if this attribute is not present, by its
357      * {@linkplain CharacterReference#decode(CharSequence) decoded} {@linkplain Element#getContent() content}
358      * text with {@linkplain CharacterReference#decodeCollapseWhiteSpace(CharSequence) collapsed white space}.
359      * <p>
360      * The predefined values of a control are not affected by changes to the
361      * <a HREF="#SubmissionValue">submission values</a> of the control.
362      *
363      * @return a collection of all {@linkplain #getPredefinedValue() predefined values} in this control, guaranteed not <code>null</code>.
364      * @see FormField#getPredefinedValues()
365      */

366     public Collection getPredefinedValues() {
367         return getPredefinedValue()!=null ? Collections.singleton(getPredefinedValue()) : Collections.EMPTY_SET;
368     }
369
370     /**
371      * Returns a collection of the control's <a HREF="#SubmissionValue">submission values</a>.
372      * <p>
373      * All objects in the returned collection are of type <code>CharSequence</code>, with no <code>null</code> entries.
374      * <p>
375      * The term <i><a name="SubmissionValue">submission value</a></i> is used in this library to refer to the value the control
376      * would contribute to the
377      * <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#form-data-set">form data set</a>
378      * of a <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#submit-format">submitted</a>
379      * form, assuming no modification of the control's
380      * <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#current-value">current value</a> by the
381      * <a target="_blank" HREF="http://www.w3.org/TR/html401/conform.html#didx-user_agent">user agent</a> or by end user interaction.
382      * <p>
383      * For <a HREF="#UserValueControl">user value controls</a>, the submission value corresponds to the
384      * control's <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#initial-value">initial value</a>.
385      * <p>
386      * The definition of the submission value for each <a HREF="#PredefinedValueControl">predefined value control</a> type is as follows:
387      * <p>
388      * {@link FormControlType#CHECKBOX CHECKBOX} and {@link FormControlType#RADIO RADIO} controls
389      * have a submission value specified by its {@linkplain #getPredefinedValue() predefined value}
390      * if it is {@link #isChecked() checked}, otherwise it has no submission value.
391      * <p>
392      * {@link FormControlType#SELECT_SINGLE SELECT_SINGLE} and {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE} controls
393      * have submission values specified by the
394      * <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-value-OPTION">values</a> of the control's
395      * <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-selected">selected</a>
396      * <code>OPTION</code> elements.
397      * <p>
398      * Only a {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE} control can have more than one submission value,
399      * all other {@linkplain FormControlType control types} return a collection containing either one value or no values.
400      * A {@link FormControlType#SELECT_SINGLE SELECT_SINGLE} control only returns multiple submission values
401      * if it illegally contains multiple selected options in the source document.
402      * <p>
403      * {@link FormControlType#SUBMIT SUBMIT}, {@link FormControlType#BUTTON BUTTON}, and {@link FormControlType#IMAGE IMAGE}
404      * controls are only ever
405      * <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#successful-controls">successful</a>
406      * when they are activated by the user to
407      * <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#submit-format">submit</a> the form.
408      * Because the submission value is intended to be a static representation of a control's data without
409      * interaction by the user, this library never associates submission values with
410      * {@linkplain FormControlType#isSubmit() submit} buttons, so this method always returns an empty collection for these
411      * control types.
412      * <p>
413      * The <a HREF="#SubmissionValue">submission value(s)</a> of a control can be modified for subsequent output in
414      * an {@link OutputDocument} using the various
415      * <i><a name="SubmissionValueModificationMethods">submission value modification methods</a></i>, namely:<br />
416      * {@link FormField#setValue(CharSequence)}<br />
417      * {@link FormField#addValue(CharSequence)}<br />
418      * {@link FormField#setValues(Collection)}<br />
419      * {@link FormField#clearValues()}<br />
420      * {@link FormFields#setValue(String fieldName, CharSequence value)}<br />
421      * {@link FormFields#addValue(String fieldName, CharSequence value)}<br />
422      * {@link FormFields#setDataSet(Map)}<br />
423      * {@link FormFields#clearValues()}<br />
424      * {@link #setValue(CharSequence) FormControl.setValue(CharSequence)}<br />
425      * {@link #addValue(CharSequence) FormControl.addValue(CharSequence)}<br />
426      * {@link #clearValues() FormControl.clearValues()}<br />
427      * <p>
428      * The values returned by this method reflect any changes made using the submission value modification methods,
429      * in contrast to methods found in the {@link Attributes} and {@link Attribute} classes, which always reflect the source document.
430      *
431      * @return a collection of the control's <i>submission values</i>, guaranteed not <code>null</code>.
432      * @see #getPredefinedValues()
433      */

434     public Collection getValues() {
435         final HashSet values=new HashSet();
436         addValuesTo(values);
437         return values;
438     }
439
440     /**
441      * Clears the control's existing <a HREF="#SubmissionValue">submission values</a>.
442      * <p>
443      * This is equivalent to {@link #setValue(CharSequence) setValue(null)}.
444      * <p>
445      * NOTE: The {@link FormFields} and {@link FormField} classes provide a more appropriate abstraction level for the modification of form control submission values.
446      *
447      * @see FormFields#clearValues()
448      * @see FormField#clearValues()
449      */

450     public final void clearValues() {
451         setValue(null);
452     }
453
454     /**
455      * Sets the control's <a HREF="#SubmissionValue">submission value</a> *.
456      * <p>
457      * * NOTE: The {@link FormFields} and {@link FormField} classes provide a more appropriate abstraction level for the modification of form control submission values.
458      * Consider using the {@link FormFields#setValue(String fieldName, CharSequence value)} method instead.
459      * <p>
460      * The specified value replaces any existing <a HREF="#SubmissionValue">submission values</a> of the control.
461      * <p>
462      * The return value indicates whether the control has "accepted" the value.
463      * For <a HREF="#UserValueControl">user value controls</a>, the return value is always <code>true</code>.
464      * <p>
465      * For <a HREF="#PredefinedValueControl">predefined value controls</a>,
466      * calling this method does not affect the control's
467      * {@linkplain #getPredefinedValues() predefined values}, but instead determines whether the control (or its options) become
468      * <code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-checked">checked</a></code> or
469      * <code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#adef-selected">selected</a></code>
470      * as detailed below:
471      * <p>
472      * {@link FormControlType#CHECKBOX CHECKBOX} and {@link FormControlType#RADIO RADIO} controls become {@link #isChecked() checked}
473      * and the method returns <code>true</code> if the specified value matches the control's predefined value (case sensitive),
474      * otherwise the control becomes unchecked and the method returns <code>false</code>.
475      * Note that any other controls with the same {@linkplain #getName() name} are not unchecked if this control becomes checked,
476      * possibly resulting in an invalid state where multiple <code>RADIO</code> controls are checked at the same time.
477      * The {@link FormField#setValue(CharSequence)} method avoids such problems and its use is recommended over this method.
478      * <p>
479      * {@link FormControlType#SELECT_SINGLE SELECT_SINGLE} and {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE}
480      * controls receive the specified value by selecting the option with the matching value and deselecting all others.
481      * If none of the options match, all are deselected.
482      * The return value of this method indicates whether one of the options matched.
483      * <p>
484      * {@link FormControlType#SUBMIT SUBMIT}, {@link FormControlType#BUTTON BUTTON}, and {@link FormControlType#IMAGE IMAGE}
485      * controls never have a <a HREF="#SubmissionValue">submission value</a>, so calling this method has no effect and
486      * always returns <code>false</code>.
487      *
488      * @param value the new <a HREF="#SubmissionValue">submission value</a> of this control, or <code>null</code> to clear the control of all submission values.
489      * @return <code>true</code> if the control accepts the value, otherwise <code>false</code>.
490      * @see FormFields#setValue(String fieldName, CharSequence value)
491      */

492     public abstract boolean setValue(CharSequence JavaDoc value);
493
494     /**
495      * Adds the specified value to this control's <a HREF="#SubmissionValue">submission values</a> *.
496      * <p>
497      * * NOTE: The {@link FormFields} and {@link FormField} classes provide a more appropriate abstraction level for the modification of form control submission values.
498      * Consider using the {@link FormFields#addValue(String fieldName, CharSequence value)} method instead.
499      * <p>
500      * This is almost equivalent to {@link #setValue(CharSequence)}, with only the following differences:
501      * <p>
502      * {@link FormControlType#CHECKBOX CHECKBOX} controls retain their existing <a HREF="#SubmissionValue">submission value</a>
503      * instead of becoming unchecked if the specified value does not match the control's {@linkplain #getPredefinedValue() predefined value}.
504      * <p>
505      * {@link FormControlType#SELECT_MULTIPLE SELECT_MULTIPLE} controls retain their existing
506      * <a HREF="#SubmissionValue">submission values</a>, meaning that the control's
507      * <code><a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#edef-OPTION">OPTION</a></code>
508      * elements whose {@linkplain #getPredefinedValues() predefined values} do not match the specified value are not deselected.
509      * This is the only type of control that can have multiple submission values within the one control.
510      *
511      * @param value the value to add to this control's <a HREF="#SubmissionValue">submission values</a>, must not be <code>null</code>.
512      * @return <code>true</code> if the control accepts the value, otherwise <code>false</code>.
513      * @see FormFields#addValue(String fieldName, CharSequence value)
514      */

515     public boolean addValue(final CharSequence JavaDoc value) {
516         return setValue(value);
517     }
518
519     abstract void addValuesTo(Collection collection); // should not add null values, values must be of type CharSequence
520
abstract void addToFormFields(FormFields formFields);
521     abstract void replaceInOutputDocument(OutputDocument outputDocument);
522
523     public String JavaDoc getDebugInfo() {
524         final StringBuffer JavaDoc sb=new StringBuffer JavaDoc();
525         sb.append(formControlType).append(" name=\"").append(name).append('"');
526         if (elementContainer.predefinedValue!=null) sb.append(" PredefinedValue=\"").append(elementContainer.predefinedValue).append('"');
527         sb.append(" - ").append(getElement().getDebugInfo());
528         return sb.toString();
529     }
530
531     static final class InputFormControl extends FormControl {
532         // TEXT, HIDDEN, PASSORD or FILE
533
public InputFormControl(final Element element, final FormControlType formControlType) {
534             super(element,formControlType,false);
535         }
536         public boolean setValue(final CharSequence JavaDoc value) {
537             elementContainer.setAttributeValue(Attribute.VALUE,value);
538             return true;
539         }
540         void addValuesTo(final Collection collection) {
541             addValueTo(collection,elementContainer.getAttributeValue(Attribute.VALUE));
542         }
543         void addToFormFields(final FormFields formFields) {
544             formFields.add(this);
545         }
546         void replaceInOutputDocument(final OutputDocument outputDocument) {
547             if (outputStyle==FormControlOutputStyle.REMOVE) {
548                 outputDocument.remove(getElement());
549             } else if (outputStyle==FormControlOutputStyle.DISPLAY_VALUE) {
550                 String JavaDoc output=null;
551                 if (formControlType!=FormControlType.HIDDEN) {
552                     CharSequence JavaDoc value=elementContainer.getAttributeValue(Attribute.VALUE);
553                     if (formControlType==FormControlType.PASSWORD && value!=null) value=getString(FormControlOutputStyle.ConfigDisplayValue.PasswordChar,value.length());
554                     output=getDisplayValueHTML(value,false);
555                 }
556                 outputDocument.replace(getElement(),output);
557             } else {
558                 replaceAttributesInOutputDocumentIfModified(outputDocument);
559             }
560         }
561     }
562
563     static final class TextAreaFormControl extends FormControl {
564         // TEXTAREA
565
public CharSequence JavaDoc value=UNCHANGED;
566         private static final String JavaDoc UNCHANGED=new String JavaDoc();
567         public TextAreaFormControl(final Element element) {
568             super(element,FormControlType.TEXTAREA,false);
569         }
570         public boolean setValue(final CharSequence JavaDoc value) {
571             this.value=value;
572             return true;
573         }
574         void addValuesTo(final Collection collection) {
575             addValueTo(collection,getValue());
576         }
577         void addToFormFields(final FormFields formFields) {
578             formFields.add(this);
579         }
580         void replaceInOutputDocument(final OutputDocument outputDocument) {
581             if (outputStyle==FormControlOutputStyle.REMOVE) {
582                 outputDocument.remove(getElement());
583             } else if (outputStyle==FormControlOutputStyle.DISPLAY_VALUE) {
584                 outputDocument.replace(getElement(),getDisplayValueHTML(getValue(),true));
585             } else {
586                 replaceAttributesInOutputDocumentIfModified(outputDocument);
587                 if (value!=UNCHANGED)
588                     outputDocument.replace(getElement().getContent(),CharacterReference.encode(value));
589             }
590         }
591         private CharSequence JavaDoc getValue() {
592             return (value==UNCHANGED) ? CharacterReference.decode(getElement().getContent()) : value;
593         }
594     }
595
596     static final class RadioCheckboxFormControl extends FormControl {
597         // RADIO or CHECKBOX
598
public RadioCheckboxFormControl(final Element element, final FormControlType formControlType) {
599             super(element,formControlType,true);
600             if (elementContainer.predefinedValue==null) {
601                 elementContainer.predefinedValue=CHECKBOX_NULL_DEFAULT_VALUE;
602                 if (element.source.isLoggingEnabled()) element.source.log(element.source.getRowColumnVector(element.begin).appendTo(new StringBuffer JavaDoc(200)).append(": compulsory \"value\" attribute of ").append(formControlType).append(" control \"").append(name).append("\" is missing, assuming the value \"").append(CHECKBOX_NULL_DEFAULT_VALUE).append('"').toString());
603             }
604         }
605         public boolean setValue(final CharSequence JavaDoc value) {
606             return elementContainer.setSelected(value,Attribute.CHECKED,false);
607         }
608         public boolean addValue(final CharSequence JavaDoc value) {
609             return elementContainer.setSelected(value,Attribute.CHECKED,formControlType==FormControlType.CHECKBOX);
610         }
611         void addValuesTo(final Collection collection) {
612             if (isChecked()) addValueTo(collection,getPredefinedValue());
613         }
614         public boolean isChecked() {
615             return elementContainer.getBooleanAttribute(Attribute.CHECKED);
616         }
617         void addToFormFields(final FormFields formFields) {
618             formFields.add(this);
619         }
620         void replaceInOutputDocument(final OutputDocument outputDocument) {
621             if (outputStyle==FormControlOutputStyle.REMOVE) {
622                 outputDocument.remove(getElement());
623             } else {
624                 if (outputStyle==FormControlOutputStyle.DISPLAY_VALUE) {
625                     final String JavaDoc html=isChecked() ? FormControlOutputStyle.ConfigDisplayValue.CheckedHTML : FormControlOutputStyle.ConfigDisplayValue.UncheckedHTML;
626                     if (html!=null) {
627                         outputDocument.replace(getElement(),html);
628                         return;
629                     }
630                     setDisabled(true);
631                 }
632                 replaceAttributesInOutputDocumentIfModified(outputDocument);
633             }
634         }
635     }
636
637     static class SubmitFormControl extends FormControl {
638         // BUTTON, SUBMIT or (in subclass) IMAGE
639
public SubmitFormControl(final Element element, final FormControlType formControlType) {
640             super(element,formControlType,true);
641         }
642         public boolean setValue(final CharSequence JavaDoc value) {
643             return false;
644         }
645         void addValuesTo(final Collection collection) {}
646         void addToFormFields(final FormFields formFields) {
647             if (getPredefinedValue()!=null) formFields.add(this);
648         }
649         void replaceInOutputDocument(final OutputDocument outputDocument) {
650             if (outputStyle==FormControlOutputStyle.REMOVE) {
651                 outputDocument.remove(getElement());
652             } else {
653                 if (outputStyle==FormControlOutputStyle.DISPLAY_VALUE) setDisabled(true);
654                 replaceAttributesInOutputDocumentIfModified(outputDocument);
655             }
656         }
657     }
658
659     static final class ImageSubmitFormControl extends SubmitFormControl {
660         // IMAGE
661
public ImageSubmitFormControl(final Element element) {
662             super(element,FormControlType.IMAGE);
663         }
664         void addToFormFields(final FormFields formFields) {
665             super.addToFormFields(formFields);
666             formFields.addName(this,name+".x");
667             formFields.addName(this,name+".y");
668         }
669     }
670
671     static final class SelectFormControl extends FormControl {
672         // SELECT_MULTIPLE or SELECT_SINGLE
673
public ElementContainer[] optionElementContainers;
674         public SelectFormControl(final Element element) {
675             super(element,element.getAttributes().get(Attribute.MULTIPLE)!=null ? FormControlType.SELECT_MULTIPLE : FormControlType.SELECT_SINGLE,false);
676             final List optionElements=element.findAllElements(Tag.OPTION);
677             optionElementContainers=new ElementContainer[optionElements.size()];
678             int x=0;
679             for (final Iterator i=optionElements.iterator(); i.hasNext();) {
680                 final ElementContainer optionElementContainer=new ElementContainer((Element)i.next(),true);
681                 if (optionElementContainer.predefinedValue==null)
682                     // use the content of the element if it has no value attribute
683
optionElementContainer.predefinedValue=CharacterReference.decodeCollapseWhiteSpace(optionElementContainer.element.getContent());
684                 optionElementContainers[x++]=optionElementContainer;
685             }
686         }
687         public String JavaDoc getPredefinedValue() {
688             throw new UnsupportedOperationException JavaDoc("Use getPredefinedValues() method instead on SELECT controls");
689         }
690         public Collection getPredefinedValues() {
691             final ArrayList arrayList=new ArrayList(optionElementContainers.length);
692             for (int i=0; i<optionElementContainers.length; i++)
693             arrayList.add(optionElementContainers[i].predefinedValue);
694             return arrayList;
695         }
696         public Iterator getOptionElementIterator() {
697             return new OptionElementIterator();
698         }
699         public boolean setValue(final CharSequence JavaDoc value) {
700             return addValue(value,false);
701         }
702         public boolean addValue(final CharSequence JavaDoc value) {
703             return addValue(value,formControlType==FormControlType.SELECT_MULTIPLE);
704         }
705         private boolean addValue(final CharSequence JavaDoc value, final boolean allowMultipleValues) {
706             boolean valueFound=false;
707             for (int i=0; i<optionElementContainers.length; i++) {
708                 if (optionElementContainers[i].setSelected(value,Attribute.SELECTED,allowMultipleValues)) valueFound=true;
709             }
710             return valueFound;
711         }
712         void addValuesTo(final Collection collection) {
713             for (int i=0; i<optionElementContainers.length; i++) {
714                 if (optionElementContainers[i].getBooleanAttribute(Attribute.SELECTED))
715                     addValueTo(collection,optionElementContainers[i].predefinedValue);
716             }
717         }
718         void addToFormFields(final FormFields formFields) {
719             for (int i=0; i<optionElementContainers.length; i++)
720                 formFields.add(this,optionElementContainers[i].predefinedValue);
721         }
722         void replaceInOutputDocument(final OutputDocument outputDocument) {
723             if (outputStyle==FormControlOutputStyle.REMOVE) {
724                 outputDocument.remove(getElement());
725             } else if (outputStyle==FormControlOutputStyle.DISPLAY_VALUE) {
726                 final StringBuffer JavaDoc sb=new StringBuffer JavaDoc(100);
727                 for (int i=0; i<optionElementContainers.length; i++) {
728                     if (optionElementContainers[i].getBooleanAttribute(Attribute.SELECTED)) {
729                         appendCollapseWhiteSpace(sb,optionElementContainers[i].element.getContent());
730                         sb.append(FormControlOutputStyle.ConfigDisplayValue.MultipleValueSeparator);
731                     }
732                 }
733                 if (sb.length()>0) sb.setLength(sb.length()-FormControlOutputStyle.ConfigDisplayValue.MultipleValueSeparator.length()); // remove last separator
734
outputDocument.replace(getElement(),getDisplayValueHTML(sb,false));
735             } else {
736                 replaceAttributesInOutputDocumentIfModified(outputDocument);
737                 for (int i=0; i<optionElementContainers.length; i++) {
738                     optionElementContainers[i].replaceAttributesInOutputDocumentIfModified(outputDocument);
739                 }
740             }
741         }
742         private final class OptionElementIterator implements Iterator {
743             private int i=0;
744             public boolean hasNext() {
745                 return i<optionElementContainers.length;
746             }
747             public Object JavaDoc next() {
748                 if (!hasNext()) throw new NoSuchElementException();
749                 return optionElementContainers[i++].element;
750             }
751             public void remove() {
752                 throw new UnsupportedOperationException JavaDoc();
753             }
754         }
755     }
756
757     final String JavaDoc getDisplayValueHTML(final CharSequence JavaDoc text, final boolean whiteSpaceFormatting) {
758         final StringBuffer JavaDoc sb=new StringBuffer JavaDoc((text==null ? 0 : text.length()*2)+50);
759         sb.append('<').append(FormControlOutputStyle.ConfigDisplayValue.ElementName);
760         for (final Iterator i=FormControlOutputStyle.ConfigDisplayValue.AttributeNames.iterator(); i.hasNext();) {
761             final String JavaDoc attributeName=i.next().toString();
762             final CharSequence JavaDoc attributeValue=elementContainer.getAttributeValue(attributeName);
763             if (attributeValue==null) continue;
764             Attribute.appendHTML(sb,attributeName,attributeValue);
765         }
766         sb.append('>');
767         if (text==null || text.length()==0)
768             sb.append(FormControlOutputStyle.ConfigDisplayValue.EmptyHTML);
769         else
770             CharacterReference.appendEncode(sb,text,whiteSpaceFormatting);
771         sb.append(EndTagType.START_DELIMITER_PREFIX).append(FormControlOutputStyle.ConfigDisplayValue.ElementName).append('>');
772         return sb.toString();
773     }
774
775     final void replaceAttributesInOutputDocumentIfModified(final OutputDocument outputDocument) {
776         elementContainer.replaceAttributesInOutputDocumentIfModified(outputDocument);
777     }
778
779     static List findAll(final Segment segment) {
780         final ArrayList list=new ArrayList();
781         findAll(segment,list,Tag.INPUT);
782         findAll(segment,list,Tag.TEXTAREA);
783         findAll(segment,list,Tag.SELECT);
784         findAll(segment,list,Tag.BUTTON);
785         Collections.sort(list,COMPARATOR);
786         return list;
787     }
788
789     private static void findAll(final Segment segment, final ArrayList list, final String JavaDoc tagName) {
790         for (final Iterator i=segment.findAllElements(tagName).iterator(); i.hasNext();)
791             list.add(((Element)i.next()).getFormControl());
792     }
793
794     private static CharSequence JavaDoc getString(final char ch, final int length) {
795         if (length==0) return "";
796         final StringBuffer JavaDoc sb=new StringBuffer JavaDoc(length);
797         for (int i=0; i<length; i++) sb.append(ch);
798         return sb.toString();
799     }
800
801     private void verifyName() {
802         if (formControlType.isSubmit()) return;
803         String JavaDoc missingOrBlank;
804         if (name==null) {
805             missingOrBlank="missing";
806         } else {
807             if (name.length()!=0) return;
808             missingOrBlank="blank";
809         }
810         final Source source=getElement().source;
811         if (source.isLoggingEnabled()) source.log(getElement().source.getRowColumnVector(getElement().begin).appendTo(new StringBuffer JavaDoc(200)).append(": compulsory \"name\" attribute of ").append(formControlType).append(" control is ").append(missingOrBlank).toString());
812     }
813
814     private static final void addValueTo(final Collection collection, final CharSequence JavaDoc value) {
815         collection.add(value!=null ? value : "");
816     }
817
818     private static final class PositionComparator implements Comparator {
819         public int compare(final Object JavaDoc o1, final Object JavaDoc o2) {
820             int formControl1Begin=((FormControl)o1).getElement().getBegin();
821             int formControl2Begin=((FormControl)o2).getElement().getBegin();
822             if (formControl1Begin<formControl2Begin) return -1;
823             if (formControl1Begin>formControl2Begin) return 1;
824             return 0;
825         }
826     }
827
828     //////////////////////////////////////////////////////////////////////////////////////
829

830     static final class ElementContainer {
831         // Contains the information common to both a FormControl and to each OPTION element
832
// within a SELECT FormControl
833
public final Element element;
834         public Map attributesMap=null;
835         public String JavaDoc predefinedValue; // never null for option, checkbox or radio elements
836

837         public ElementContainer(final Element element, final boolean loadPredefinedValue) {
838             this.element=element;
839             predefinedValue=loadPredefinedValue ? element.getAttributes().getValue(Attribute.VALUE) : null;
840         }
841
842         public Map getAttributesMap() {
843             if (attributesMap==null) attributesMap=element.getAttributes().getMap(true);
844             return attributesMap;
845         }
846
847         public boolean setSelected(final CharSequence JavaDoc value, final String JavaDoc selectedOrChecked, final boolean allowMultipleValues) {
848             if (value!=null && predefinedValue.equals(value.toString())) {
849                 setBooleanAttribute(selectedOrChecked,true);
850                 return true;
851             }
852             if (!allowMultipleValues) setBooleanAttribute(selectedOrChecked,false);
853             return false;
854         }
855
856         public CharSequence JavaDoc getAttributeValue(final String JavaDoc attributeName) {
857             if (attributesMap!=null)
858                 return (CharSequence JavaDoc)attributesMap.get(attributeName);
859             else
860                 return element.getAttributes().getValue(attributeName);
861         }
862
863         public void setAttributeValue(final String JavaDoc attributeName, final CharSequence JavaDoc value) {
864             // null value indicates attribute should be removed.
865
if (value==null) {
866                 setBooleanAttribute(attributeName,false);
867                 return;
868             }
869             if (attributesMap!=null) {
870                 attributesMap.put(attributeName,value);
871                 return;
872             }
873             final String JavaDoc valueString=value.toString();
874             final CharSequence JavaDoc existingValue=getAttributeValue(attributeName);
875             if (existingValue!=null && existingValue.toString().equals(valueString)) return;
876             getAttributesMap().put(attributeName,valueString);
877         }
878
879         public boolean getBooleanAttribute(final String JavaDoc attributeName) {
880             if (attributesMap!=null)
881                 return attributesMap.containsKey(attributeName);
882             else
883                 return element.getAttributes().get(attributeName)!=null;
884         }
885
886         public void setBooleanAttribute(final String JavaDoc attributeName, final boolean value) {
887             final boolean oldValue=getBooleanAttribute(attributeName);
888             if (value==oldValue) return;
889             if (value)
890                 getAttributesMap().put(attributeName,attributeName); // xhtml compatible attribute
891
else
892                 getAttributesMap().remove(attributeName);
893         }
894
895         public void replaceAttributesInOutputDocumentIfModified(final OutputDocument outputDocument) {
896             if (attributesMap!=null) outputDocument.replace(element.getAttributes(),attributesMap);
897         }
898     }
899 }
900
901
Popular Tags