KickJava   Java API By Example, From Geeks To Geeks.

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


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 a <em>field</em> in an HTML <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html">form</a>,
27  * a <em>field</em> being defined as the group of all {@linkplain FormControl form controls}
28  * having the same {@linkplain FormControl#getName() name}.
29  * <p>
30  * The {@link #getFormControls()} method can be used to obtain the collection of this field's constituent
31  * {@link FormControl} objects.
32  * <p>
33  * The {@link FormFields} class, which represents a collection of <code>FormField</code> objects, provides the highest level
34  * interface for dealing with form fields and controls. For the most common tasks it can be used directly without
35  * the need to work with its constituent <code>FormField</code> or {@link FormControl} objects.
36  * <p>
37  * The <code>FormField</code> class serves two main purposes:
38  * <ol>
39  * <li style="margin-bottom: 1.5em">
40  * Provide methods for the modification and retrieval of form control <a HREF="FormControl.html#SubmissionValue">submission values</a>
41  * while ensuring that the states of all the field's constituent form controls remain consistent with each other.
42  * <p>
43  * The methods available for this purpose are:<br />
44  * {@link #getValues() Collection getValues()}<br />
45  * {@link #clearValues() void clearValues()}<br />
46  * {@link #setValues(Collection) void setValues(Collection)}<br />
47  * {@link #setValue(CharSequence) boolean setValue(CharSequence)}<br />
48  * {@link #addValue(CharSequence) boolean addValue(CharSequence)}<br />
49  * <p>
50  * Although the {@link FormControl} class provides methods for directly modifying the submission values
51  * of individual form controls, it is generally recommended to use the interface provided by the {@link FormFields} class
52  * unless there is a specific requirement for the lower level functionality.
53  * The {@link FormFields} class contains convenience methods providing most of the functionality of the above methods,
54  * as well as some higher level functionality such as the ability to set the form
55  * <a HREF="#SubmissionValue">submission values</a> as a complete <a HREF="FormFields.html#FieldDataSet">field data set</a>
56  * using the {@link FormFields#setDataSet(Map)} method.
57  * <li><a name="DataStructureProperties"></a>
58  * Provide a means of determining the data structure of the field, allowing a server receiving a
59  * <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#submit-format">submitted</a>
60  * <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#form-data-set">form data set</a>
61  * to interpret and store the data in an appropriate way.
62  * <p>
63  * The properties available for this purpose are:<br />
64  * {@link #allowsMultipleValues() boolean allowsMultipleValues()}<br />
65  * {@link #getUserValueCount() int getUserValueCount()}<br />
66  * {@link #getPredefinedValues() Collection getPredefinedValues()}<br />
67  * <p>
68  * The {@link FormFields#getColumnLabels()} and {@link FormFields#getColumnValues(Map)} methods utilise these properties
69  * to convert data from a <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#form-data-set">form data set</a>
70  * (represented as a <a HREF="#FieldDataSet">field data set</a>) into a simple array format,
71  * suitable for storage in a tabular format such as a database table or <code>.CSV</code> file.
72  * <p>
73  * The properties need only be utilised directly in the event that a
74  * <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#form-data-set">form data set</a> is to be converted
75  * from its <a HREF="FormFields.html#FieldDataSet">normal format</a> into some other type of data structure.
76  * </ol>
77  * A form field which allows user values normally consists of a single
78  * <a HREF="FormControl.html#UserValueControl">user value control</a>,
79  * such as a {@link FormControlType#TEXT TEXT} control.
80  * <p>
81  * When a form field consists of more than one control, these controls are normally all
82  * <a HREF="FormControl.html#PredefinedValueControl">predefined value controls</a> of the same
83  * {@linkplain FormControlType type}, such as {@link FormControlType#CHECKBOX CHECKBOX} controls.
84  * <p>
85  * Form fields consisting of more than one control do not necessarily return {@linkplain #allowsMultipleValues() multiple values}.
86  * A form field consisting of {@link FormControlType#CHECKBOX CHECKBOX} controls can return multiple values, whereas
87  * a form field consisting of {@link FormControlType#CHECKBOX RADIO} controls returns at most one value.
88  * <p>
89  * The HTML author can disregard convention and mix all types of controls with the same name in the same form,
90  * or include multiple <a HREF="FormControl.html#UserValueControl">user value controls</a> of the same name.
91  * The evidence that such an unusual combination is present is when {@link #getUserValueCount()}<code>&gt;1</code>.
92  * <p>
93  * <code>FormField</code> instances are created automatically with the creation of a {@link FormFields} collection.
94  * <p>
95  * The case sensitivity of form field names is determined by the
96  * {@link Config#CurrentCompatibilityMode}<code>.</code>{@link Config.CompatibilityMode#isFormFieldNameCaseInsensitive() FormFieldNameCaseInsensitive} property.
97  *
98  * @see FormFields
99  * @see FormControl
100  * @see FormControlType
101  */

102 public final class FormField {
103     private final String JavaDoc name;
104     private int userValueCount=0;
105     private boolean allowsMultipleValues=false;
106     private LinkedHashSet predefinedValues=null; // String objects, null if none
107
private final LinkedHashSet formControls=new LinkedHashSet();
108     private transient FormControl firstFormControl=null; // this field is simply a cache for the getFirstFormControl() method
109
int columnIndex; // see FormFields.initColumns()
110

111     /** Constructor called from FormFields class. */
112     FormField(final String JavaDoc name) {
113         this.name=name;
114     }
115
116     /**
117      * Returns the <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#control-name">control name</a> shared by all of this field's constituent {@linkplain FormControl controls}.
118      * <p>
119      * If {@link Config#CurrentCompatibilityMode}<code>.</code>{@link Config.CompatibilityMode#isFormFieldNameCaseInsensitive() isFormFieldNameCaseInsensitive()}
120      * is <code>true</code>, the grouping of the controls by name is case insensitive
121      * and this method always returns the name in lower case.
122      * <p>
123      * Since a form field is simply a group of controls with the same name, the terms <i>control name</i> and
124      * <i>field name</i> are for the most part synonymous, with only a possible difference in case differentiating them.
125      *
126      * @return the <a target="_blank" HREF="http://www.w3.org/TR/html401/interact/forms.html#control-name">control name</a> shared by all of this field's constituent {@linkplain FormControl controls}.
127      * @see FormControl#getName()
128      */

129     public String JavaDoc getName() {
130         return name;
131     }
132
133     /**
134      * Returns a collection of all the constituent {@linkplain FormControl form controls} in this field.
135      * <p>
136      * An iterator over this collection returns the controls in the order of appearance in the source.
137      *
138      * @return a collection of all the constituent {@linkplain FormControl form controls} in this field.
139      * @see #getFormControl()
140      * @see #getFormControl(String predefinedValue)
141      */

142     public Collection getFormControls() {
143         return formControls;
144     }
145
146     /**
147      * Returns the constituent {@link FormControl} with the specified {@linkplain FormControl#getPredefinedValue() predefined value}.
148      * <p>
149      * Specifying a predefined value of <code>null</code> returns the first control without a predefined value.
150      *
151      * @param predefinedValue the predefined value of the control to be returned, or <code>null</code> to return the first control without a predefined value.
152      * @return the constituent {@link FormControl} with the specified {@linkplain FormControl#getPredefinedValue() predefined value}, or <code>null</code> if none exists.
153      * @see #getFormControl()
154      * @see #getFormControls()
155      */

156     public FormControl getFormControl(final String JavaDoc predefinedValue) {
157         if (predefinedValue==null) {
158             for (final Iterator i=formControls.iterator(); i.hasNext();) {
159                 final FormControl formControl=(FormControl)i.next();
160                 if (!formControl.getFormControlType().hasPredefinedValue()) return formControl;
161                 if (formControl.getFormControlType().getElementName()!=Tag.SELECT && formControl.getPredefinedValue()==null) return formControl;
162             }
163         } else {
164             for (final Iterator i=formControls.iterator(); i.hasNext();) {
165                 final FormControl formControl=(FormControl)i.next();
166                 if (formControl.getFormControlType().getElementName()==Tag.SELECT) {
167                     if (formControl.getPredefinedValues().contains(predefinedValue)) return formControl;
168                 } else {
169                     if (predefinedValue.equals(formControl.getPredefinedValue())) return formControl;
170                 }
171             }
172         }
173         return null;
174     }
175
176     /**
177      * Returns the first {@link FormControl} from this field.
178      * @return the first {@link FormControl} from this field, guaranteed not <code>null</code>.
179      * @see #getFormControl(String predefinedValue)
180      * @see #getFormControls()
181      */

182     public FormControl getFormControl() {
183         return (FormControl)formControls.iterator().next();
184     }
185
186     /**
187      * Indicates whether the field allows multiple values.
188      * <p>
189      * Returns <code>false</code> in any one of the following circumstances:
190      * <ul>
191      * <li>The field consists of only one control (unless it is a
192      * {@linkplain FormControlType#SELECT_MULTIPLE multiple select} with more than one option)
193      * <li>The field consists entirely of {@linkplain FormControlType#RADIO radio buttons}
194      * <li>The field consists entirely of {@linkplain FormControlType#isSubmit() submit} buttons
195      * </ul>
196      * If none of these three conditions are met, the method returns <code>true</code>.
197      *
198      * @return <code>true</code> if the field allows multiple values, otherwise <code>false</code>.
199      */

200     public boolean allowsMultipleValues() {
201         return allowsMultipleValues;
202     }
203
204     /**
205      * Returns the number of constituent <a HREF="FormControl.html#UserValueControl">user value controls</a> in this field.
206      * This should in most cases be either <code>0</code> or <code>1</code>.
207      * <p>
208      * A value of <code>0</code> indicates the field values consist only of
209      * {@linkplain #getPredefinedValues() predefined values}, which is the case when the field consists only of
210      * <a HREF="FormControl.html#PredefinedValueControl">predefined value controls</a>.
211      * <p>
212      * A value of <code>1</code> indicates the field values consist of at most one value set by the user.
213      * It is still possible in this case to receive multiple values in the unlikely event that the HTML author mixed
214      * controls of different types with the same name, but any other values would consist only of
215      * {@linkplain #getPredefinedValues() predefined values}.
216      * <p>
217      * A value greater than <code>1</code> indicates that the HTML author has included more than one
218      * <a HREF="FormControl.html#UserValueControl">user value control</a> with the same name.
219      * This would nearly always indicate an unintentional error in the HTML source document,
220      * in which case your application can either log a warning that a poorly designed form has been encountered,
221      * or take special action to try to interpret the multiple user values that might be submitted.
222      *
223      * @return the number of constituent <a HREF="FormControl.html#UserValueControl">user value controls</a> in this field.
224      */

225     public int getUserValueCount() {
226         return userValueCount;
227     }
228
229     /**
230      * Returns a collection of the {@linkplain FormControl#getPredefinedValue() predefined values} of all constituent {@linkplain FormControl controls} in this field.
231      * <p>
232      * All objects in the returned collection are of type <code>String</code>, with no <code>null</code> entries.
233      * <p>
234      * An interator over this collection returns the values in the order of appearance in the source document.
235      *
236      * @return a collection of the {@linkplain FormControl#getPredefinedValue() predefined values} of all constituent {@linkplain FormControl controls} in this field, or <code>null</code> if none.
237      * @see FormControl#getPredefinedValues()
238      */

239     public Collection getPredefinedValues() {
240         return predefinedValues!=null ? predefinedValues : Collections.EMPTY_SET;
241     }
242
243     /**
244      * Returns a collection of the <a HREF="#FieldSubmissionValues">field submission values</a>.
245      * <p>
246      * The term <i><a name="FieldSubmissionValues">field submission values</a></i> is used in this library to refer to the aggregate of all the
247      * <a HREF="FormControl.html#SubmissionValue">submission values</a> of a field's constituent {@linkplain #getFormControls() form controls}.
248      * <p>
249      * All objects in the returned collection are of type <code>CharSequence</code>, with no <code>null</code> entries.
250      *
251      * @return a collection of the <a HREF="#FieldSubmissionValue">field submission values</a>, guaranteed not <code>null</code>.
252      */

253     public Collection getValues() {
254         final HashSet values=new HashSet();
255         for (final Iterator i=formControls.iterator(); i.hasNext();)
256             ((FormControl)i.next()).addValuesTo(values);
257         return values;
258     }
259
260     /**
261      * Clears the <a HREF="FormControl.html#SubmissionValue">submission values</a> of all the constituent {@linkplain #getFormControls() form controls} in this field.
262      * @see FormControl#clearValues()
263      */

264     public void clearValues() {
265         for (final Iterator i=formControls.iterator(); i.hasNext();)
266             ((FormControl)i.next()).clearValues();
267     }
268
269     /**
270      * Sets the <a HREF="#FieldSubmissionValues">field submission values</a> of this field to the specified values.
271      * <p>
272      * This is equivalent to calling {@link #clearValues()} followed by {@link #addValue(CharSequence) addValue(value)} for each
273      * value in the specified collection.
274      * <p>
275      * The specified collection must not contain any <code>null</code> values.
276      *
277      * @param values the new <a HREF="#FieldSubmissionValues">field submission values</a> of this field.
278      * @see #addValue(CharSequence value)
279      */

280     public void setValues(final Collection values) {
281         clearValues();
282         addValues(values);
283     }
284
285     /**
286      * Sets the <a HREF="#FieldSubmissionValues">field submission values</a> of this field to the single specified value.
287      * <p>
288      * This is equivalent to calling {@link #clearValues()} followed by {@link #addValue(CharSequence) addValue(value)}.
289      * <p>
290      * The return value indicates whether any of the constituent form controls "accepted" the value.
291      * A return value of <code>false</code> implies an error condition as the specified value is not compatible with this field.
292      * <p>
293      * Specifying a <code>null</code> value is equivalent to calling {@link #clearValues()} alone, and always returns <code>true</code>.
294      * <p>
295      * See the {@link #addValue(CharSequence value)} method for more information.
296      *
297      * @param value the new <a HREF="#FieldSubmissionValues">field submission value</a> of this field, or <code>null</code> to {@linkplain #clearValues() clear} the field of all submission values.
298      * @return <code>true</code> if one of the constituent {@linkplain #getFormControls() form controls} accepts the value, otherwise <code>false</code>.
299      * @see FormFields#setValue(String fieldName, CharSequence value)
300      */

301     public boolean setValue(final CharSequence JavaDoc value) {
302         clearValues();
303         return value!=null ? addValue(value) : true;
304     }
305
306     /**
307      * Adds the specified value to the <a HREF="#FieldSubmissionValues">field submission values</a> of this field.
308      * <p>
309      * This is achieved internally by attempting to {@linkplain FormControl#addValue(CharSequence) add the value} to every constituent
310      * {@linkplain #getFormControls() form control} until one "accepts" it.
311      * <p>
312      * The return value indicates whether any of the constituent form controls accepted the value.
313      * A return value of <code>false</code> implies an error condition as the specified value is not compatible with this field.
314      * <p>
315      * In the unusual case that this field consists of multiple form controls, but not all of them are
316      * <a HREF="FormControl.html#PredefinedValueControl">predefined value controls</a>, priority is given to the predefined value controls
317      * before attempting to add the value to the <a HREF="FormControl.html#UserValueControl">user value controls</a>.
318      *
319      * @param value the new <a HREF="#FieldSubmissionValues">field submission value</a> to add to this field, must not be <code>null</code>.
320      * @return <code>true</code> if one of the constituent {@linkplain #getFormControls() form controls} accepts the value, otherwise <code>false</code>.
321      */

322     public boolean addValue(final CharSequence JavaDoc value) {
323         if (value==null) throw new IllegalArgumentException JavaDoc("value argument must not be null");
324         if (formControls.size()==1) return getFirstFormControl().addValue(value);
325         List userValueControls=null;
326         for (final Iterator i=formControls.iterator(); i.hasNext();) {
327             final FormControl formControl=(FormControl)i.next();
328             if (!formControl.getFormControlType().hasPredefinedValue()) {
329                 // A user value control has been found, but is not the only control with this name.
330
// This shouldn't normally happen in a well designed form, but we will save the user value control
331
// for later and give all predefined value controls first opportunity to take the value.
332
if (userValueControls==null) userValueControls=new LinkedList();
333                 userValueControls.add(formControl);
334                 continue;
335             }
336             if (formControl.addValue(value)) return true; // return value of true from formControl.addValue(value) means the value was taken by the control
337
}
338         if (userValueControls==null) return false;
339         for (final Iterator i=userValueControls.iterator(); i.hasNext();) {
340             final FormControl formControl=(FormControl)i.next();
341             if (formControl.addValue(value)) return true;
342         }
343         return false;
344     }
345
346     /**
347      * Returns a string representation of this object useful for debugging purposes.
348      * @return a string representation of this object useful for debugging purposes.
349      */

350     public String JavaDoc getDebugInfo() {
351         final StringBuffer JavaDoc sb=new StringBuffer JavaDoc();
352         sb.append("Field: ").append(name).append(", UserValueCount=").append(userValueCount).append(", AllowsMultipleValues=").append(allowsMultipleValues);
353         if (predefinedValues!=null) {
354             for (final Iterator i=predefinedValues.iterator(); i.hasNext();) {
355                 sb.append("\nPredefinedValue: ");
356                 sb.append(i.next());
357             }
358         }
359         for (final Iterator i=formControls.iterator(); i.hasNext();) {
360             sb.append("\nFormControl: ");
361             sb.append(((FormControl)i.next()).getDebugInfo());
362         }
363         sb.append("\n\n");
364         return sb.toString();
365     }
366
367     /**
368      * Returns a string representation of this object useful for debugging purposes.
369      * <p>
370      * This is equivalent to {@link #getDebugInfo()}.
371      *
372      * @return a string representation of this object useful for debugging purposes.
373      */

374     public String JavaDoc toString() {
375         return getDebugInfo();
376     }
377
378     void addValues(final Collection values) {
379         if (values!=null) for (final Iterator i=values.iterator(); i.hasNext();) addValue((CharSequence JavaDoc)i.next());
380     }
381
382     void addValues(final CharSequence JavaDoc[] values) {
383         if (values!=null) for (int i=0; i<values.length; i++) addValue(values[i]);
384     }
385
386     void addFormControl(final FormControl formControl, final String JavaDoc predefinedValue) {
387         // predefinedValue==null if we are adding a user value
388
if (predefinedValue==null) {
389             userValueCount++;
390         } else {
391             if (predefinedValues==null) predefinedValues=new LinkedHashSet();
392             predefinedValues.add(predefinedValue);
393         }
394         formControls.add(formControl);
395         allowsMultipleValues=calculateAllowsMultipleValues(formControl);
396     }
397
398     private boolean calculateAllowsMultipleValues(final FormControl newFormControl) {
399         // false if only one control (unless it is a multiple select with more than one option),
400
// or all of the controls are radio buttons, or all of the controls are submit buttons
401
if (allowsMultipleValues || userValueCount>1) return true;
402         if (userValueCount==1) return predefinedValues!=null;
403         // at this stage we know userValueCount==0 && predefinedValues.size()>=1
404
if (predefinedValues.size()==1) return false;
405         final FormControlType newFormControlType=newFormControl.getFormControlType();
406         if (formControls.size()==1) return newFormControlType==FormControlType.SELECT_MULTIPLE;
407         // at this stage we know there are multiple predefined values in multiple controls.
408
// if all of the controls are radio buttons or all are submit buttons, allowsMultipleValues is false, otherwise true.
409
// checking only the first control and the new control is equivalent to checking them all because if they weren't all
410
// the same allowsMultipleValues would already be true.
411
final FormControlType firstFormControlType=getFirstFormControl().getFormControlType();
412         if (newFormControlType==FormControlType.RADIO && firstFormControlType==FormControlType.RADIO) return false;
413         if (newFormControlType.isSubmit() && firstFormControlType.isSubmit()) return false;
414         return true;
415     }
416
417     FormControl getFirstFormControl() {
418         // formControls must be ordered collection for this method to work.
419
// It has to return the first FormControl entered into the collection
420
// for the algorithm in calculateAllowsMultipleValues() to work.
421
if (firstFormControl==null) firstFormControl=(FormControl)formControls.iterator().next();
422         return firstFormControl;
423     }
424
425     /** only called from FormFields class */
426     void merge(final FormField formField) {
427         if (formField.userValueCount>userValueCount) userValueCount=formField.userValueCount;
428         allowsMultipleValues=allowsMultipleValues || formField.allowsMultipleValues;
429         if (predefinedValues==null) {
430             predefinedValues=formField.predefinedValues;
431         } else if (formField.predefinedValues!=null) {
432             for (final Iterator i=formField.predefinedValues.iterator(); i.hasNext();)
433                 predefinedValues.add(i.next());
434         }
435         for (final Iterator i=formField.getFormControls().iterator(); i.hasNext();)
436             formControls.add(i.next());
437     }
438 }
439
440
Popular Tags