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