    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$
    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}