001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.validator;
018
019import java.io.Serializable;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025
026import org.apache.commons.collections.FastHashMap;// DEPRECATED
027
028/**
029 * <p>
030 *
031 * This contains a set of validation rules for a form/JavaBean. The information
032 * is contained in a list of <code>Field</code> objects. Instances of this class
033 * are configured with a &lt;form&gt; xml element. </p> <p>
034 *
035 * The use of FastHashMap is deprecated and will be replaced in a future
036 * release. </p>
037 *
038 * @version $Revision: 1739361 $
039 */
040//TODO mutable non-private fields
041public class Form implements Serializable {
042
043    private static final long serialVersionUID = 6445211789563796371L;
044
045    /** The name/key the set of validation rules is stored under. */
046    protected String name = null;
047
048    /**
049     * List of <code>Field</code>s. Used to maintain the order they were added
050     * in although individual <code>Field</code>s can be retrieved using <code>Map</code>
051     * of <code>Field</code>s.
052     */
053    protected List<Field> lFields = new ArrayList<Field>();
054
055    /**
056     * Map of <code>Field</code>s keyed on their property value.
057     *
058     * @deprecated   Subclasses should use getFieldMap() instead.
059     */
060    @Deprecated
061    protected FastHashMap hFields = new FastHashMap(); // <String, Field>
062
063    /**
064     * The name/key of the form which this form extends from.
065     *
066     * @since   Validator 1.2.0
067     */
068    protected String inherit = null;
069
070    /**
071     * Whether or not the this <code>Form</code> was processed for replacing
072     * variables in strings with their values.
073     */
074    private boolean processed = false;
075
076    /**
077     * Gets the name/key of the set of validation rules.
078     *
079     * @return   The name value
080     */
081    public String getName() {
082        return name;
083    }
084
085    /**
086     * Sets the name/key of the set of validation rules.
087     *
088     * @param name  The new name value
089     */
090    public void setName(String name) {
091        this.name = name;
092    }
093
094    /**
095     * Add a <code>Field</code> to the <code>Form</code>.
096     *
097     * @param f  The field
098     */
099    public void addField(Field f) {
100        this.lFields.add(f);
101        getFieldMap().put(f.getKey(), f);
102    }
103
104    /**
105     * A <code>List</code> of <code>Field</code>s is returned as an unmodifiable
106     * <code>List</code>.
107     *
108     * @return   The fields value
109     */
110    public List<Field> getFields() {
111        return Collections.unmodifiableList(lFields);
112    }
113
114    /**
115     * Returns the Field with the given name or null if this Form has no such
116     * field.
117     *
118     * @param fieldName  The field name
119     * @return           The field value
120     * @since            Validator 1.1
121     */
122    public Field getField(String fieldName) {
123        return getFieldMap().get(fieldName);
124    }
125
126    /**
127     * Returns true if this Form contains a Field with the given name.
128     *
129     * @param fieldName  The field name
130     * @return           True if this form contains the field by the given name
131     * @since            Validator 1.1
132     */
133    public boolean containsField(String fieldName) {
134        return getFieldMap().containsKey(fieldName);
135    }
136
137    /**
138     * Merges the given form into this one. For any field in <code>depends</code>
139     * not present in this form, include it. <code>depends</code> has precedence
140     * in the way the fields are ordered.
141     *
142     * @param depends  the form we want to merge
143     * @since          Validator 1.2.0
144     */
145    protected void merge(Form depends) {
146
147        List<Field> templFields = new ArrayList<Field>();
148        @SuppressWarnings("unchecked") // FastHashMap is not generic
149        Map<String, Field> temphFields = new FastHashMap();
150        Iterator<Field> dependsIt = depends.getFields().iterator();
151        while (dependsIt.hasNext()) {
152            Field defaultField = dependsIt.next();
153            if (defaultField != null) {
154                String fieldKey = defaultField.getKey();
155                if (!this.containsField(fieldKey)) {
156                    templFields.add(defaultField);
157                    temphFields.put(fieldKey, defaultField);
158                }
159                else {
160                    Field old = getField(fieldKey);
161                    getFieldMap().remove(fieldKey);
162                    lFields.remove(old);
163                    templFields.add(old);
164                    temphFields.put(fieldKey, old);
165                }
166            }
167        }
168        lFields.addAll(0, templFields);
169        getFieldMap().putAll(temphFields);
170    }
171
172    /**
173     * Processes all of the <code>Form</code>'s <code>Field</code>s.
174     *
175     * @param globalConstants  A map of global constants
176     * @param constants        Local constants
177     * @param forms            Map of forms
178     * @since                  Validator 1.2.0
179     */
180    protected void process(Map<String, String> globalConstants, Map<String, String> constants, Map<String, Form> forms) {
181        if (isProcessed()) {
182            return;
183        }
184
185        int n = 0;//we want the fields from its parent first
186        if (isExtending()) {
187            Form parent = forms.get(inherit);
188            if (parent != null) {
189                if (!parent.isProcessed()) {
190                    //we want to go all the way up the tree
191                    parent.process(constants, globalConstants, forms);
192                }
193                for (Iterator<Field> i = parent.getFields().iterator(); i.hasNext(); ) {
194                    Field f = i.next();
195                    //we want to be able to override any fields we like
196                    if (getFieldMap().get(f.getKey()) == null) {
197                        lFields.add(n, f);
198                        getFieldMap().put(f.getKey(), f);
199                        n++;
200                    }
201                }
202            }
203        }
204        hFields.setFast(true);
205        //no need to reprocess parent's fields, we iterate from 'n'
206        for (Iterator<Field> i = lFields.listIterator(n); i.hasNext(); ) {
207            Field f = i.next();
208            f.process(globalConstants, constants);
209        }
210
211        processed = true;
212    }
213
214    /**
215     * Returns a string representation of the object.
216     *
217     * @return string representation
218     */
219    @Override
220    public String toString() {
221        StringBuilder results = new StringBuilder();
222
223        results.append("Form: ");
224        results.append(name);
225        results.append("\n");
226
227        for (Iterator<Field> i = lFields.iterator(); i.hasNext(); ) {
228            results.append("\tField: \n");
229            results.append(i.next());
230            results.append("\n");
231        }
232
233        return results.toString();
234    }
235
236    /**
237     * Validate all Fields in this Form on the given page and below.
238     *
239     * @param params               A Map of parameter class names to parameter
240     *      values to pass into validation methods.
241     * @param actions              A Map of validator names to ValidatorAction
242     *      objects.
243     * @param page                 Fields on pages higher than this will not be
244     *      validated.
245     * @return                     A ValidatorResults object containing all
246     *      validation messages.
247     * @throws ValidatorException
248     */
249    ValidatorResults validate(Map<String, Object> params, Map<String, ValidatorAction> actions, int page)
250        throws ValidatorException {
251        return validate(params, actions, page, null);
252    }
253
254    /**
255     * Validate all Fields in this Form on the given page and below.
256     *
257     * @param params               A Map of parameter class names to parameter
258     *      values to pass into validation methods.
259     * @param actions              A Map of validator names to ValidatorAction
260     *      objects.
261     * @param page                 Fields on pages higher than this will not be
262     *      validated.
263     * @return                     A ValidatorResults object containing all
264     *      validation messages.
265     * @throws ValidatorException
266     * @since 1.2.0
267     */
268    ValidatorResults validate(Map<String, Object> params, Map<String, ValidatorAction> actions, int page, String fieldName)
269        throws ValidatorException {
270        ValidatorResults results = new ValidatorResults();
271        params.put(Validator.VALIDATOR_RESULTS_PARAM, results);
272
273        // Only validate a single field if specified
274        if (fieldName != null) {
275            Field field = getFieldMap().get(fieldName);
276
277            if (field == null) {
278               throw new ValidatorException("Unknown field "+fieldName+" in form "+getName());
279            }
280            params.put(Validator.FIELD_PARAM, field);
281
282            if (field.getPage() <= page) {
283               results.merge(field.validate(params, actions));
284            }
285        } else {
286            Iterator<Field> fields = this.lFields.iterator();
287            while (fields.hasNext()) {
288                Field field = fields.next();
289
290                params.put(Validator.FIELD_PARAM, field);
291
292                if (field.getPage() <= page) {
293                    results.merge(field.validate(params, actions));
294                }
295            }
296        }
297
298        return results;
299    }
300
301    /**
302     * Whether or not the this <code>Form</code> was processed for replacing
303     * variables in strings with their values.
304     *
305     * @return   The processed value
306     * @since    Validator 1.2.0
307     */
308    public boolean isProcessed() {
309        return processed;
310    }
311
312    /**
313     * Gets the name/key of the parent set of validation rules.
314     *
315     * @return   The extends value
316     * @since    Validator 1.2.0
317     */
318    public String getExtends() {
319        return inherit;
320    }
321
322    /**
323     * Sets the name/key of the parent set of validation rules.
324     *
325     * @param inherit  The new extends value
326     * @since          Validator 1.2.0
327     */
328    public void setExtends(String inherit) {
329        this.inherit = inherit;
330    }
331
332    /**
333     * Get extends flag.
334     *
335     * @return   The extending value
336     * @since    Validator 1.2.0
337     */
338    public boolean isExtending() {
339        return inherit != null;
340    }
341
342    /**
343     * Returns a Map of String field keys to Field objects.
344     *
345     * @return   The fieldMap value
346     * @since    Validator 1.2.0
347     */
348    @SuppressWarnings("unchecked") // FastHashMap is not generic
349    protected Map<String, Field> getFieldMap() {
350        return hFields;
351    }
352}