View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.validator;
18  
19  import java.io.Serializable;
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.commons.collections.FastHashMap;// DEPRECATED
27  
28  /**
29   * <p>
30   * This contains a set of validation rules for a form/JavaBean. The information
31   * is contained in a list of <code>Field</code> objects. Instances of this class
32   * are configured with a &lt;form&gt; xml element.
33   * </p>
34   * <p>
35   * The use of FastHashMap is deprecated and will be replaced in a future
36   * release.
37   * </p>
38   */
39  //TODO mutable non-private fields
40  public class Form implements Serializable {
41  
42      private static final long serialVersionUID = 6445211789563796371L;
43  
44      /** The name/key the set of validation rules is stored under. */
45      protected String name;
46  
47      /**
48       * List of <code>Field</code>s. Used to maintain the order they were added
49       * in although individual <code>Field</code>s can be retrieved using <code>Map</code>
50       * of <code>Field</code>s.
51       */
52      protected List<Field> lFields = new ArrayList<>();
53  
54      /**
55       * Map of <code>Field</code>s keyed on their property value.
56       *
57       * @deprecated   Subclasses should use getFieldMap() instead.
58       */
59      @Deprecated
60      protected FastHashMap hFields = new FastHashMap(); // <String, Field>
61  
62      /**
63       * The name/key of the form which this form extends from.
64       *
65       * @since 1.2.0
66       */
67      protected String inherit;
68  
69      /**
70       * Whether or not the this <code>Form</code> was processed for replacing
71       * variables in strings with their values.
72       */
73      private boolean processed;
74  
75      /**
76       * Add a <code>Field</code> to the <code>Form</code>.
77       *
78       * @param f  The field
79       */
80      public void addField(final Field f) {
81          this.lFields.add(f);
82          getFieldMap().put(f.getKey(), f);
83      }
84  
85      /**
86       * Returns true if this Form contains a Field with the given name.
87       *
88       * @param fieldName  The field name
89       * @return           True if this form contains the field by the given name
90       * @since 1.1
91       */
92      public boolean containsField(final String fieldName) {
93          return getFieldMap().containsKey(fieldName);
94      }
95  
96      /**
97       * Gets the name/key of the parent set of validation rules.
98       *
99       * @return   The extends value
100      * @since 1.2.0
101      */
102     public String getExtends() {
103         return inherit;
104     }
105 
106     /**
107      * Returns the Field with the given name or null if this Form has no such
108      * field.
109      *
110      * @param fieldName  The field name
111      * @return           The field value
112      * @since 1.1
113      */
114     public Field getField(final String fieldName) {
115         return getFieldMap().get(fieldName);
116     }
117 
118     /**
119      * Returns a Map of String field keys to Field objects.
120      *
121      * @return   The fieldMap value
122      * @since 1.2.0
123      */
124     @SuppressWarnings("unchecked") // FastHashMap is not generic
125     protected Map<String, Field> getFieldMap() {
126         return hFields;
127     }
128 
129     /**
130      * A <code>List</code> of <code>Field</code>s is returned as an unmodifiable
131      * <code>List</code>.
132      *
133      * @return   The fields value
134      */
135     public List<Field> getFields() {
136         return Collections.unmodifiableList(lFields);
137     }
138 
139     /**
140      * Gets the name/key of the set of validation rules.
141      *
142      * @return   The name value
143      */
144     public String getName() {
145         return name;
146     }
147 
148     /**
149      * Gets extends flag.
150      *
151      * @return   The extending value
152      * @since 1.2.0
153      */
154     public boolean isExtending() {
155         return inherit != null;
156     }
157 
158     /**
159      * Whether or not the this <code>Form</code> was processed for replacing
160      * variables in strings with their values.
161      *
162      * @return   The processed value
163      * @since 1.2.0
164      */
165     public boolean isProcessed() {
166         return processed;
167     }
168 
169     /**
170      * Merges the given form into this one. For any field in <code>depends</code>
171      * not present in this form, include it. <code>depends</code> has precedence
172      * in the way the fields are ordered.
173      *
174      * @param depends  the form we want to merge
175      * @since 1.2.0
176      */
177     protected void merge(final Form depends) {
178 
179         final List<Field> templFields = new ArrayList<>();
180         @SuppressWarnings("unchecked") // FastHashMap is not generic
181         final
182         Map<String, Field> temphFields = new FastHashMap();
183         for (final Field defaultField : depends.getFields()) {
184             if (defaultField != null) {
185                 final String fieldKey = defaultField.getKey();
186                 if (!this.containsField(fieldKey)) {
187                     templFields.add(defaultField);
188                     temphFields.put(fieldKey, defaultField);
189                 }
190                 else {
191                     final Field old = getField(fieldKey);
192                     getFieldMap().remove(fieldKey);
193                     lFields.remove(old);
194                     templFields.add(old);
195                     temphFields.put(fieldKey, old);
196                 }
197             }
198         }
199         lFields.addAll(0, templFields);
200         getFieldMap().putAll(temphFields);
201     }
202 
203     /**
204      * Processes all of the <code>Form</code>'s <code>Field</code>s.
205      *
206      * @param globalConstants  A map of global constants
207      * @param constants        Local constants
208      * @param forms            Map of forms
209      * @since 1.2.0
210      */
211     protected void process(final Map<String, String> globalConstants, final Map<String, String> constants, final Map<String, Form> forms) {
212         if (isProcessed()) {
213             return;
214         }
215 
216         int n = 0;//we want the fields from its parent first
217         if (isExtending()) {
218             final Form parent = forms.get(inherit);
219             if (parent != null) {
220                 if (!parent.isProcessed()) {
221                     //we want to go all the way up the tree
222                     parent.process(constants, globalConstants, forms);
223                 }
224                 for (final Field f : parent.getFields()) {
225                     //we want to be able to override any fields we like
226                     if (getFieldMap().get(f.getKey()) == null) {
227                         lFields.add(n, f);
228                         getFieldMap().put(f.getKey(), f);
229                         n++;
230                     }
231                 }
232             }
233         }
234         hFields.setFast(true);
235         //no need to reprocess parent's fields, we iterate from 'n'
236         for (final Iterator<Field> i = lFields.listIterator(n); i.hasNext(); ) {
237             final Field f = i.next();
238             f.process(globalConstants, constants);
239         }
240 
241         processed = true;
242     }
243 
244     /**
245      * Sets the name/key of the parent set of validation rules.
246      *
247      * @param inherit  The new extends value
248      * @since 1.2.0
249      */
250     public void setExtends(final String inherit) {
251         this.inherit = inherit;
252     }
253 
254     /**
255      * Sets the name/key of the set of validation rules.
256      *
257      * @param name  The new name value
258      */
259     public void setName(final String name) {
260         this.name = name;
261     }
262 
263     /**
264      * Returns a string representation of the object.
265      *
266      * @return string representation
267      */
268     @Override
269     public String toString() {
270         final StringBuilder results = new StringBuilder();
271 
272         results.append("Form: ");
273         results.append(name);
274         results.append("\n");
275 
276         for (final Field lField : lFields) {
277             results.append("\tField: \n");
278             results.append(lField);
279             results.append("\n");
280         }
281 
282         return results.toString();
283     }
284 
285     /**
286      * Validate all Fields in this Form on the given page and below.
287      *
288      * @param params               A Map of parameter class names to parameter
289      *      values to pass into validation methods.
290      * @param actions              A Map of validator names to ValidatorAction
291      *      objects.
292      * @param page                 Fields on pages higher than this will not be
293      *      validated.
294      * @return                     A ValidatorResults object containing all
295      *      validation messages.
296      * @throws ValidatorException
297      */
298     ValidatorResults validate(final Map<String, Object> params, final Map<String, ValidatorAction> actions, final int page)
299         throws ValidatorException {
300         return validate(params, actions, page, null);
301     }
302 
303     /**
304      * Validate all Fields in this Form on the given page and below.
305      *
306      * @param params               A Map of parameter class names to parameter
307      *      values to pass into validation methods.
308      * @param actions              A Map of validator names to ValidatorAction
309      *      objects.
310      * @param page                 Fields on pages higher than this will not be
311      *      validated.
312      * @return                     A ValidatorResults object containing all
313      *      validation messages.
314      * @throws ValidatorException
315      * @since 1.2.0
316      */
317     ValidatorResults validate(final Map<String, Object> params, final Map<String, ValidatorAction> actions, final int page, final String fieldName)
318             throws ValidatorException {
319         final ValidatorResults results = new ValidatorResults();
320         params.put(Validator.VALIDATOR_RESULTS_PARAM, results);
321 
322         // Only validate a single field if specified
323         if (fieldName != null) {
324             final Field field = getFieldMap().get(fieldName);
325 
326             if (field == null) {
327                 throw new ValidatorException("Unknown field " + fieldName + " in form " + getName());
328             }
329             params.put(Validator.FIELD_PARAM, field);
330 
331             if (field.getPage() <= page) {
332                 results.merge(field.validate(params, actions));
333             }
334         } else {
335             for (final Field field : this.lFields) {
336 
337                 params.put(Validator.FIELD_PARAM, field);
338 
339                 if (field.getPage() <= page) {
340                     results.merge(field.validate(params, actions));
341                 }
342             }
343         }
344 
345         return results;
346     }
347 }