Form.java

  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.  *      https://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. import java.io.Serializable;
  19. import java.util.ArrayList;
  20. import java.util.Collections;
  21. import java.util.Iterator;
  22. import java.util.List;
  23. import java.util.Map;

  24. import org.apache.commons.collections.FastHashMap; // DEPRECATED

  25. /**
  26.  * <p>
  27.  * This contains a set of validation rules for a form/JavaBean. The information
  28.  * is contained in a list of {@code Field} objects. Instances of this class
  29.  * are configured with a &lt;form&gt; xml element.
  30.  * </p>
  31.  * <p>
  32.  * The use of FastHashMap is deprecated and will be replaced in a future
  33.  * release.
  34.  * </p>
  35.  */
  36. //TODO mutable non-private fields
  37. public class Form implements Serializable {

  38.     private static final long serialVersionUID = 6445211789563796371L;

  39.     /** The name/key the set of validation rules is stored under. */
  40.     protected String name;

  41.     /**
  42.      * List of {@code Field}s. Used to maintain the order they were added
  43.      * in although individual {@code Field}s can be retrieved using {@link Map}
  44.      * of {@code Field}s.
  45.      */
  46.     protected List<Field> lFields = new ArrayList<>();

  47.     /**
  48.      * Map of {@code Field}s keyed on their property value.
  49.      *
  50.      * @deprecated   Subclasses should use getFieldMap() instead.
  51.      */
  52.     @Deprecated
  53.     protected FastHashMap hFields = new FastHashMap(); // <String, Field>

  54.     /**
  55.      * The name/key of the form which this form extends from.
  56.      *
  57.      * @since 1.2.0
  58.      */
  59.     protected String inherit;

  60.     /**
  61.      * Whether or not the this {@code Form} was processed for replacing
  62.      * variables in strings with their values.
  63.      */
  64.     private boolean processed;

  65.     /**
  66.      * Constructs a new instance.
  67.      */
  68.     public Form() {
  69.         // empty
  70.     }

  71.     /**
  72.      * Add a {@code Field} to the {@code Form}.
  73.      *
  74.      * @param f  The field
  75.      */
  76.     public void addField(final Field f) {
  77.         lFields.add(f);
  78.         getFieldMap().put(f.getKey(), f);
  79.     }

  80.     /**
  81.      * Returns true if this Form contains a Field with the given name.
  82.      *
  83.      * @param fieldName  The field name
  84.      * @return           True if this form contains the field by the given name
  85.      * @since 1.1
  86.      */
  87.     public boolean containsField(final String fieldName) {
  88.         return getFieldMap().containsKey(fieldName);
  89.     }

  90.     /**
  91.      * Gets the name/key of the parent set of validation rules.
  92.      *
  93.      * @return   The extends value
  94.      * @since 1.2.0
  95.      */
  96.     public String getExtends() {
  97.         return inherit;
  98.     }

  99.     /**
  100.      * Returns the Field with the given name or null if this Form has no such
  101.      * field.
  102.      *
  103.      * @param fieldName  The field name
  104.      * @return           The field value
  105.      * @since 1.1
  106.      */
  107.     public Field getField(final String fieldName) {
  108.         return getFieldMap().get(fieldName);
  109.     }

  110.     /**
  111.      * Returns a Map of String field keys to Field objects.
  112.      *
  113.      * @return   The fieldMap value
  114.      * @since 1.2.0
  115.      */
  116.     @SuppressWarnings("unchecked") // FastHashMap is not generic
  117.     protected Map<String, Field> getFieldMap() {
  118.         return hFields;
  119.     }

  120.     /**
  121.      * A {@code List} of {@code Field}s is returned as an unmodifiable
  122.      * {@code List}.
  123.      *
  124.      * @return   The fields value
  125.      */
  126.     public List<Field> getFields() {
  127.         return Collections.unmodifiableList(lFields);
  128.     }

  129.     /**
  130.      * Gets the name/key of the set of validation rules.
  131.      *
  132.      * @return   The name value
  133.      */
  134.     public String getName() {
  135.         return name;
  136.     }

  137.     /**
  138.      * Gets extends flag.
  139.      *
  140.      * @return   The extending value
  141.      * @since 1.2.0
  142.      */
  143.     public boolean isExtending() {
  144.         return inherit != null;
  145.     }

  146.     /**
  147.      * Whether or not the this {@code Form} was processed for replacing
  148.      * variables in strings with their values.
  149.      *
  150.      * @return   The processed value
  151.      * @since 1.2.0
  152.      */
  153.     public boolean isProcessed() {
  154.         return processed;
  155.     }

  156.     /**
  157.      * Merges the given form into this one. For any field in {@code depends}
  158.      * not present in this form, include it. {@code depends} has precedence
  159.      * in the way the fields are ordered.
  160.      *
  161.      * @param depends  the form we want to merge
  162.      * @since 1.2.0
  163.      */
  164.     protected void merge(final Form depends) {
  165.         final List<Field> templFields = new ArrayList<>();
  166.         @SuppressWarnings("unchecked") // FastHashMap is not generic
  167.         final Map<String, Field> temphFields = new FastHashMap();
  168.         for (final Field defaultField : depends.getFields()) {
  169.             if (defaultField != null) {
  170.                 final String fieldKey = defaultField.getKey();
  171.                 if (!containsField(fieldKey)) {
  172.                     templFields.add(defaultField);
  173.                     temphFields.put(fieldKey, defaultField);
  174.                 } else {
  175.                     final Field old = getField(fieldKey);
  176.                     getFieldMap().remove(fieldKey);
  177.                     lFields.remove(old);
  178.                     templFields.add(old);
  179.                     temphFields.put(fieldKey, old);
  180.                 }
  181.             }
  182.         }
  183.         lFields.addAll(0, templFields);
  184.         getFieldMap().putAll(temphFields);
  185.     }

  186.     /**
  187.      * Processes all of the {@code Form}'s {@code Field}s.
  188.      *
  189.      * @param globalConstants  A map of global constants
  190.      * @param constants        Local constants
  191.      * @param forms            Map of forms
  192.      * @since 1.2.0
  193.      */
  194.     protected void process(final Map<String, String> globalConstants, final Map<String, String> constants, final Map<String, Form> forms) {
  195.         if (isProcessed()) {
  196.             return;
  197.         }

  198.         int n = 0; //we want the fields from its parent first
  199.         if (isExtending()) {
  200.             final Form parent = forms.get(inherit);
  201.             if (parent != null) {
  202.                 if (!parent.isProcessed()) {
  203.                     // we want to go all the way up the tree
  204.                     parent.process(constants, globalConstants, forms);
  205.                 }
  206.                 for (final Field f : parent.getFields()) {
  207.                     // we want to be able to override any fields we like
  208.                     if (getFieldMap().get(f.getKey()) == null) {
  209.                         lFields.add(n, f);
  210.                         getFieldMap().put(f.getKey(), f);
  211.                         n++;
  212.                     }
  213.                 }
  214.             }
  215.         }
  216.         hFields.setFast(true);
  217.         // no need to reprocess parent's fields, we iterate from 'n'
  218.         for (final Iterator<Field> i = lFields.listIterator(n); i.hasNext(); ) {
  219.             final Field f = i.next();
  220.             f.process(globalConstants, constants);
  221.         }

  222.         processed = true;
  223.     }

  224.     /**
  225.      * Sets the name/key of the parent set of validation rules.
  226.      *
  227.      * @param inherit  The new extends value
  228.      * @since 1.2.0
  229.      */
  230.     public void setExtends(final String inherit) {
  231.         this.inherit = inherit;
  232.     }

  233.     /**
  234.      * Sets the name/key of the set of validation rules.
  235.      *
  236.      * @param name  The new name value
  237.      */
  238.     public void setName(final String name) {
  239.         this.name = name;
  240.     }

  241.     /**
  242.      * Returns a string representation of the object.
  243.      *
  244.      * @return string representation
  245.      */
  246.     @Override
  247.     public String toString() {
  248.         final StringBuilder results = new StringBuilder();

  249.         results.append("Form: ");
  250.         results.append(name);
  251.         results.append("\n");

  252.         for (final Field lField : lFields) {
  253.             results.append("\tField: \n");
  254.             results.append(lField);
  255.             results.append("\n");
  256.         }

  257.         return results.toString();
  258.     }

  259.     /**
  260.      * Validate all Fields in this Form on the given page and below.
  261.      *
  262.      * @param params               A Map of parameter class names to parameter
  263.      *      values to pass into validation methods.
  264.      * @param actions              A Map of validator names to ValidatorAction
  265.      *      objects.
  266.      * @param page                 Fields on pages higher than this will not be
  267.      *      validated.
  268.      * @return                     A ValidatorResults object containing all
  269.      *      validation messages.
  270.      * @throws ValidatorException
  271.      */
  272.     ValidatorResults validate(final Map<String, Object> params, final Map<String, ValidatorAction> actions, final int page)
  273.         throws ValidatorException {
  274.         return validate(params, actions, page, null);
  275.     }

  276.     /**
  277.      * Validate all Fields in this Form on the given page and below.
  278.      *
  279.      * @param params               A Map of parameter class names to parameter
  280.      *      values to pass into validation methods.
  281.      * @param actions              A Map of validator names to ValidatorAction
  282.      *      objects.
  283.      * @param page                 Fields on pages higher than this will not be
  284.      *      validated.
  285.      * @return                     A ValidatorResults object containing all
  286.      *      validation messages.
  287.      * @throws ValidatorException
  288.      * @since 1.2.0
  289.      */
  290.     ValidatorResults validate(final Map<String, Object> params, final Map<String, ValidatorAction> actions, final int page, final String fieldName)
  291.             throws ValidatorException {
  292.         final ValidatorResults results = new ValidatorResults();
  293.         params.put(Validator.VALIDATOR_RESULTS_PARAM, results);

  294.         // Only validate a single field if specified
  295.         if (fieldName != null) {
  296.             final Field field = getFieldMap().get(fieldName);

  297.             if (field == null) {
  298.                 throw new ValidatorException("Unknown field " + fieldName + " in form " + getName());
  299.             }
  300.             params.put(Validator.FIELD_PARAM, field);

  301.             if (field.getPage() <= page) {
  302.                 results.merge(field.validate(params, actions));
  303.             }
  304.         } else {
  305.             for (final Field field : lFields) {

  306.                 params.put(Validator.FIELD_PARAM, field);

  307.                 if (field.getPage() <= page) {
  308.                     results.merge(field.validate(params, actions));
  309.                 }
  310.             }
  311.         }

  312.         return results;
  313.     }
  314. }