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: 1227719 $ $Date: 2012-01-05 18:45:51 +0100 (Do, 05 Jan 2012) $
039 */
040public class Form implements Serializable {
041
042    private static final long serialVersionUID = 6445211789563796371L;
043
044    /** The name/key the set of validation rules is stored under. */
045    protected String name = null;
046
047    /**
048     * List of <code>Field</code>s. Used to maintain the order they were added
049     * in although individual <code>Field</code>s can be retrieved using <code>Map</code>
050     * of <code>Field</code>s.
051     */
052    protected List lFields = new ArrayList();
053
054    /**
055     * Map of <code>Field</code>s keyed on their property value.
056     *
057     * @deprecated   Subclasses should use getFieldMap() instead.
058     */
059    protected FastHashMap hFields = new FastHashMap();
060
061    /**
062     * The name/key of the form which this form extends from.
063     *
064     * @since   Validator 1.2.0
065     */
066    protected String inherit = null;
067
068    /**
069     * Whether or not the this <code>Form</code> was processed for replacing
070     * variables in strings with their values.
071     */
072    private boolean processed = false;
073
074    /**
075     * Gets the name/key of the set of validation rules.
076     *
077     * @return   The name value
078     */
079    public String getName() {
080        return name;
081    }
082
083    /**
084     * Sets the name/key of the set of validation rules.
085     *
086     * @param name  The new name value
087     */
088    public void setName(String name) {
089        this.name = name;
090    }
091
092    /**
093     * Add a <code>Field</code> to the <code>Form</code>.
094     *
095     * @param f  The field
096     */
097    public void addField(Field f) {
098        this.lFields.add(f);
099        this.hFields.put(f.getKey(), f);
100    }
101
102    /**
103     * A <code>List</code> of <code>Field</code>s is returned as an unmodifiable
104     * <code>List</code>.
105     *
106     * @return   The fields value
107     */
108    public List getFields() {
109        return Collections.unmodifiableList(lFields);
110    }
111
112    /**
113     * Returns the Field with the given name or null if this Form has no such
114     * field.
115     *
116     * @param fieldName  The field name
117     * @return           The field value
118     * @since            Validator 1.1
119     */
120    public Field getField(String fieldName) {
121        return (Field) this.hFields.get(fieldName);
122    }
123
124    /**
125     * Returns true if this Form contains a Field with the given name.
126     *
127     * @param fieldName  The field name
128     * @return           True if this form contains the field by the given name
129     * @since            Validator 1.1
130     */
131    public boolean containsField(String fieldName) {
132        return this.hFields.containsKey(fieldName);
133    }
134
135    /**
136     * Merges the given form into this one. For any field in <code>depends</code>
137     * not present in this form, include it. <code>depends</code> has precedence
138     * in the way the fields are ordered.
139     *
140     * @param depends  the form we want to merge
141     * @since          Validator 1.2.0
142     */
143    protected void merge(Form depends) {
144
145        List templFields = new ArrayList();
146        Map temphFields = new FastHashMap();
147        Iterator dependsIt = depends.getFields().iterator();
148        while (dependsIt.hasNext()) {
149            Field defaultField = (Field) dependsIt.next();
150            if (defaultField != null) {
151                String fieldKey = defaultField.getKey();
152                if (!this.containsField(fieldKey)) {
153                    templFields.add(defaultField);
154                    temphFields.put(fieldKey, defaultField);
155                }
156                else {
157                    Field old = getField(fieldKey);
158                    hFields.remove(fieldKey);
159                    lFields.remove(old);
160                    templFields.add(old);
161                    temphFields.put(fieldKey, old);
162                }
163            }
164        }
165        lFields.addAll(0, templFields);
166        hFields.putAll(temphFields);
167    }
168
169    /**
170     * Processes all of the <code>Form</code>'s <code>Field</code>s.
171     *
172     * @param globalConstants  A map of global constants
173     * @param constants        Local constants
174     * @param forms            Map of forms
175     * @since                  Validator 1.2.0
176     */
177    protected void process(Map globalConstants, Map constants, Map forms) {
178        if (isProcessed()) {
179            return;
180        }
181
182        int n = 0;//we want the fields from its parent first
183        if (isExtending()) {
184            Form parent = (Form) forms.get(inherit);
185            if (parent != null) {
186                if (!parent.isProcessed()) {
187                    //we want to go all the way up the tree
188                    parent.process(constants, globalConstants, forms);
189                }
190                for (Iterator i = parent.getFields().iterator(); i.hasNext(); ) {
191                    Field f = (Field) i.next();
192                    //we want to be able to override any fields we like
193                    if (hFields.get(f.getKey()) == null) {
194                        lFields.add(n, f);
195                        hFields.put(f.getKey(), f);
196                        n++;
197                    }
198                }
199            }
200        }
201        hFields.setFast(true);
202        //no need to reprocess parent's fields, we iterate from 'n'
203        for (Iterator i = lFields.listIterator(n); i.hasNext(); ) {
204            Field f = (Field) i.next();
205            f.process(globalConstants, constants);
206        }
207
208        processed = true;
209    }
210
211    /**
212     * Returns a string representation of the object.
213     *
214     * @return string representation
215     */
216    public String toString() {
217        StringBuffer results = new StringBuffer();
218
219        results.append("Form: ");
220        results.append(name);
221        results.append("\n");
222
223        for (Iterator i = lFields.iterator(); i.hasNext(); ) {
224            results.append("\tField: \n");
225            results.append(i.next());
226            results.append("\n");
227        }
228
229        return results.toString();
230    }
231
232    /**
233     * Validate all Fields in this Form on the given page and below.
234     *
235     * @param params               A Map of parameter class names to parameter
236     *      values to pass into validation methods.
237     * @param actions              A Map of validator names to ValidatorAction
238     *      objects.
239     * @param page                 Fields on pages higher than this will not be
240     *      validated.
241     * @return                     A ValidatorResults object containing all
242     *      validation messages.
243     * @throws ValidatorException
244     */
245    ValidatorResults validate(Map params, Map actions, int page)
246        throws ValidatorException {
247        return validate(params, actions, page, null);
248    }
249
250    /**
251     * Validate all Fields in this Form on the given page and below.
252     *
253     * @param params               A Map of parameter class names to parameter
254     *      values to pass into validation methods.
255     * @param actions              A Map of validator names to ValidatorAction
256     *      objects.
257     * @param page                 Fields on pages higher than this will not be
258     *      validated.
259     * @return                     A ValidatorResults object containing all
260     *      validation messages.
261     * @throws ValidatorException
262     * @since 1.2.0
263     */
264    ValidatorResults validate(Map params, Map actions, int page, String fieldName)
265        throws ValidatorException {
266
267        ValidatorResults results = new ValidatorResults();
268        params.put(Validator.VALIDATOR_RESULTS_PARAM, results);
269
270        // Only validate a single field if specified
271        if (fieldName != null) {
272            Field field = (Field) this.hFields.get(fieldName);
273
274            if (field == null) {
275               throw new ValidatorException("Unknown field "+fieldName+" in form "+getName());
276            }
277            params.put(Validator.FIELD_PARAM, field);
278
279            if (field.getPage() <= page) {
280               results.merge(field.validate(params, actions));
281            }
282        } else {
283            Iterator fields = this.lFields.iterator();
284            while (fields.hasNext()) {
285                Field field = (Field) fields.next();
286
287                params.put(Validator.FIELD_PARAM, field);
288
289                if (field.getPage() <= page) {
290                    results.merge(field.validate(params, actions));
291                }
292            }
293        }
294
295        return results;
296    }
297
298    /**
299     * Whether or not the this <code>Form</code> was processed for replacing
300     * variables in strings with their values.
301     *
302     * @return   The processed value
303     * @since    Validator 1.2.0
304     */
305    public boolean isProcessed() {
306        return processed;
307    }
308
309    /**
310     * Gets the name/key of the parent set of validation rules.
311     *
312     * @return   The extends value
313     * @since    Validator 1.2.0
314     */
315    public String getExtends() {
316        return inherit;
317    }
318
319    /**
320     * Sets the name/key of the parent set of validation rules.
321     *
322     * @param inherit  The new extends value
323     * @since          Validator 1.2.0
324     */
325    public void setExtends(String inherit) {
326        this.inherit = inherit;
327    }
328
329    /**
330     * Get extends flag.
331     *
332     * @return   The extending value
333     * @since    Validator 1.2.0
334     */
335    public boolean isExtending() {
336        return inherit != null;
337    }
338
339    /**
340     * Returns a Map of String field keys to Field objects.
341     *
342     * @return   The fieldMap value
343     * @since    Validator 1.2.0
344     */
345    protected Map getFieldMap() {
346        return hFields;
347    }
348}