View Javadoc

    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    *      http://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
    19  import java.io.Serializable;
    20  import java.lang.reflect.InvocationTargetException;
    21  import java.util.ArrayList;
    22  import java.util.Collection;
    23  import java.util.Collections;
    24  import java.util.HashMap;
    25  import java.util.Iterator;
    26  import java.util.List;
    27  import java.util.Map;
    28  import java.util.StringTokenizer;
    29
    30  import org.apache.commons.beanutils.PropertyUtils;
    31  import org.apache.commons.collections.FastHashMap; // DEPRECATED
    32  import org.apache.commons.validator.util.ValidatorUtils;
    33
    34  /***
    35   * This contains the list of pluggable validators to run on a field and any
    36   * message information and variables to perform the validations and generate
    37   * error messages.  Instances of this class are configured with a
    38   * &lt;field&gt; xml element.
    39   * <p>
    40   * The use of FastHashMap is deprecated and will be replaced in a future
    41   * release.
    42   * </p>
    43   *
    44   * @version $Revision: 478334 $ $Date: 2006-11-22 21:31:54 +0000 (Wed, 22 Nov 2006) $
    45   * @see org.apache.commons.validator.Form
    46   */
    47  public class Field implements Cloneable, Serializable {
    48
    49      /***
    50       * This is the value that will be used as a key if the <code>Arg</code>
    51       * name field has no value.
    52       */
    53      private static final String DEFAULT_ARG =
    54              "org.apache.commons.validator.Field.DEFAULT";
    55
    56      /***
    57       * This indicates an indexed property is being referenced.
    58       */
    59      public static final String TOKEN_INDEXED = "[]";
    60
    61      /***
    62       * The start of a token.
    63       */
    64      protected static final String TOKEN_START = "${";
    65
    66      /***
    67       * The end of a token.
    68       */
    69      protected static final String TOKEN_END = "}";
    70
    71      /***
    72       * A Vriable token.
    73       */
    74      protected static final String TOKEN_VAR = "var:";
    75
    76      /***
    77       * The Field's property name.
    78       */
    79      protected String property = null;
    80
    81      /***
    82       * The Field's indexed property name.
    83       */
    84      protected String indexedProperty = null;
    85
    86      /***
    87       * The Field's indexed list property name.
    88       */
    89      protected String indexedListProperty = null;
    90
    91      /***
    92       * The Field's unique key.
    93       */
    94      protected String key = null;
    95
    96      /***
    97       * A comma separated list of validator's this field depends on.
    98       */
    99      protected String depends = null;
    100
    101     /***
    102      * The Page Number
    103      */
    104     protected int page = 0;
    105
    106     /***
    107      * The order of the Field in the Form.
    108      */
    109     protected int fieldOrder = 0;
    110
    111     /***
    112      * Internal representation of this.depends String as a List.  This List
    113      * gets updated whenever setDepends() gets called.  This List is
    114      * synchronized so a call to setDepends() (which clears the List) won't
    115      * interfere with a call to isDependency().
    116      */
    117     private List dependencyList = Collections.synchronizedList(new ArrayList());
    118
    119     /***
    120      * @deprecated Subclasses should use getVarMap() instead.
    121      */
    122     protected FastHashMap hVars = new FastHashMap();
    123
    124     /***
    125      * @deprecated Subclasses should use getMsgMap() instead.
    126      */
    127     protected FastHashMap hMsgs = new FastHashMap();
    128
    129     /***
    130      * Holds Maps of arguments.  args[0] returns the Map for the first
    131      * replacement argument.  Start with a 0 length array so that it will
    132      * only grow to the size of the highest argument position.
    133      * @since Validator 1.1
    134      */
    135     protected Map[] args = new Map[0];
    136
    137     /***
    138      * Gets the page value that the Field is associated with for
    139      * validation.
    140      * @return The page number.
    141      */
    142     public int getPage() {
    143         return this.page;
    144     }
    145
    146     /***
    147      * Sets the page value that the Field is associated with for
    148      * validation.
    149      * @param page The page number.
    150      */
    151     public void setPage(int page) {
    152         this.page = page;
    153     }
    154
    155     /***
    156      * Gets the position of the <code>Field</code> in the validation list.
    157      * @return The field position.
    158      */
    159     public int getFieldOrder() {
    160         return this.fieldOrder;
    161     }
    162
    163     /***
    164      * Sets the position of the <code>Field</code> in the validation list.
    165      * @param fieldOrder The field position.
    166      */
    167     public void setFieldOrder(int fieldOrder) {
    168         this.fieldOrder = fieldOrder;
    169     }
    170
    171     /***
    172      * Gets the property name of the field.
    173      * @return The field's property name.
    174      */
    175     public String getProperty() {
    176         return this.property;
    177     }
    178
    179     /***
    180      * Sets the property name of the field.
    181      * @param property The field's property name.
    182      */
    183     public void setProperty(String property) {
    184         this.property = property;
    185     }
    186
    187     /***
    188      * Gets the indexed property name of the field.  This
    189      * is the method name that can take an <code>int</code> as
    190      * a parameter for indexed property value retrieval.
    191      * @return The field's indexed property name.
    192      */
    193     public String getIndexedProperty() {
    194         return this.indexedProperty;
    195     }
    196
    197     /***
    198      * Sets the indexed property name of the field.
    199      * @param indexedProperty The field's indexed property name.
    200      */
    201     public void setIndexedProperty(String indexedProperty) {
    202         this.indexedProperty = indexedProperty;
    203     }
    204
    205     /***
    206      * Gets the indexed property name of the field.  This
    207      * is the method name that will return an array or a
    208      * <code>Collection</code> used to retrieve the
    209      * list and then loop through the list performing the specified
    210      * validations.
    211      * @return The field's indexed List property name.
    212      */
    213     public String getIndexedListProperty() {
    214         return this.indexedListProperty;
    215     }
    216
    217     /***
    218      * Sets the indexed property name of the field.
    219      * @param indexedListProperty The field's indexed List property name.
    220      */
    221     public void setIndexedListProperty(String indexedListProperty) {
    222         this.indexedListProperty = indexedListProperty;
    223     }
    224
    225     /***
    226      * Gets the validation rules for this field as a comma separated list.
    227      * @return A comma separated list of validator names.
    228      */
    229     public String getDepends() {
    230         return this.depends;
    231     }
    232
    233     /***
    234      * Sets the validation rules for this field as a comma separated list.
    235      * @param depends A comma separated list of validator names.
    236      */
    237     public void setDepends(String depends) {
    238         this.depends = depends;
    239
    240         this.dependencyList.clear();
    241
    242         StringTokenizer st = new StringTokenizer(depends, ",");
    243         while (st.hasMoreTokens()) {
    244             String depend = st.nextToken().trim();
    245
    246             if (depend != null && depend.length() > 0) {
    247                 this.dependencyList.add(depend);
    248             }
    249         }
    250     }
    251
    252     /***
    253      * Add a <code>Msg</code> to the <code>Field</code>.
    254      * @param msg A validation message.
    255      */
    256     public void addMsg(Msg msg) {
    257         hMsgs.put(msg.getName(), msg);
    258     }
    259
    260     /***
    261      * Retrieve a message value.
    262      * @param key Validation key.
    263      * @return A validation message for a specified validator.
    264      */
    265     public String getMsg(String key) {
    266         Msg msg = getMessage(key);
    267         return (msg == null) ? null : msg.getKey();
    268     }
    269
    270     /***
    271      * Retrieve a message object.
    272      * @since Validator 1.1.4
    273      * @param key Validation key.
    274      * @return A validation message for a specified validator.
    275      */
    276     public Msg getMessage(String key) {
    277         return (Msg) hMsgs.get(key);
    278     }
    279
    280     /***
    281      * The <code>Field</code>'s messages are returned as an
    282      * unmodifiable <code>Map</code>.
    283      * @since Validator 1.1.4
    284      * @return Map of validation messages for the field.
    285      */
    286     public Map getMessages() {
    287         return Collections.unmodifiableMap(hMsgs);
    288     }
    289
    290     /***
    291      * Add an <code>Arg</code> to the replacement argument list.
    292      * @since Validator 1.1
    293      * @param arg Validation message's argument.
    294      */
    295     public void addArg(Arg arg) {
    296         // TODO this first if check can go away after arg0, etc. are removed from dtd
    297         if (arg == null || arg.getKey() == null || arg.getKey().length() == 0) {
    298             return;
    299         }
    300
    301         determineArgPosition(arg);
    302         ensureArgsCapacity(arg);
    303
    304         Map argMap = this.args[arg.getPosition()];
    305         if (argMap == null) {
    306             argMap = new HashMap();
    307             this.args[arg.getPosition()] = argMap;
    308         }
    309
    310         if (arg.getName() == null) {
    311             argMap.put(DEFAULT_ARG, arg);
    312         } else {
    313             argMap.put(arg.getName(), arg);
    314         }
    315
    316     }
    317
    318     /***
    319      * Calculate the position of the Arg
    320      */
    321     private void determineArgPosition(Arg arg) {
    322
    323         int position = arg.getPosition();
    324
    325         // position has been explicity set
    326         if (position >= 0) {
    327             return;
    328         }
    329
    330         // first arg to be added
    331         if (args == null || args.length == 0) {
    332             arg.setPosition(0);
    333             return;
    334         }
    335
    336         // determine the position of the last argument with
    337         // the same name or the last default argument
    338         String key = arg.getName() == null ? DEFAULT_ARG : arg.getName();
    339         int lastPosition = -1;
    340         int lastDefault  = -1;
    341         for (int i = 0; i < args.length; i++) {
    342             if (args[i] != null && args[i].containsKey(key)) {
    343                 lastPosition = i;
    344             }
    345             if (args[i] != null && args[i].containsKey(DEFAULT_ARG)) {
    346                 lastDefault = i;
    347             }
    348         }
    349
    350         if (lastPosition < 0) {
    351             lastPosition = lastDefault;
    352         }
    353
    354         // allocate the next position
    355         arg.setPosition(++lastPosition);
    356
    357     }
    358
    359     /***
    360      * Ensures that the args array can hold the given arg.  Resizes the array as
    361      * necessary.
    362      * @param arg Determine if the args array is long enough to store this arg's
    363      * position.
    364      */
    365     private void ensureArgsCapacity(Arg arg) {
    366         if (arg.getPosition() >= this.args.length) {
    367             Map[] newArgs = new Map[arg.getPosition() + 1];
    368             System.arraycopy(this.args, 0, newArgs, 0, this.args.length);
    369             this.args = newArgs;
    370         }
    371     }
    372
    373     /***
    374      * Gets the default <code>Arg</code> object at the given position.
    375      * @param position Validation message argument's position.
    376      * @return The default Arg or null if not found.
    377      * @since Validator 1.1
    378      */
    379     public Arg getArg(int position) {
    380         return this.getArg(DEFAULT_ARG, position);
    381     }
    382
    383     /***
    384      * Gets the <code>Arg</code> object at the given position.  If the key
    385      * finds a <code>null</code> value then the default value will be
    386      * retrieved.
    387      * @param key The name the Arg is stored under.  If not found, the default
    388      * Arg for the given position (if any) will be retrieved.
    389      * @param position The Arg number to find.
    390      * @return The Arg with the given name and position or null if not found.
    391      * @since Validator 1.1
    392      */
    393     public Arg getArg(String key, int position) {
    394         if ((position >= this.args.length) || (this.args[position] == null)) {
    395             return null;
    396         }
    397
    398         Arg arg = (Arg) args[position].get(key);
    399
    400         // Didn't find default arg so exit, otherwise we would get into
    401         // infinite recursion
    402         if ((arg == null) && key.equals(DEFAULT_ARG)) {
    403             return null;
    404         }
    405
    406         return (arg == null) ? this.getArg(position) : arg;
    407     }
    408
    409     /***
    410      * Retrieves the Args for the given validator name.
    411      * @param key The validator's args to retrieve.
    412      * @return An Arg[] sorted by the Args' positions (i.e. the Arg at index 0
    413      * has a position of 0).
    414      * @since Validator 1.1.1
    415      */
    416     public Arg[] getArgs(String key){
    417         Arg[] args = new Arg[this.args.length];
    418
    419         for (int i = 0; i < this.args.length; i++) {
    420             args[i] = this.getArg(key, i);
    421         }
    422
    423         return args;
    424     }
    425
    426     /***
    427      * Add a <code>Var</code> to the <code>Field</code>.
    428      * @param v The Validator Argument.
    429      */
    430     public void addVar(Var v) {
    431         this.hVars.put(v.getName(), v);
    432     }
    433
    434     /***
    435      * Add a <code>Var</code>, based on the values passed in, to the
    436      * <code>Field</code>.
    437      * @param name Name of the validation.
    438      * @param value The Argument's value.
    439      * @param jsType The Javascript type.
    440      */
    441     public void addVar(String name, String value, String jsType) {
    442         this.addVar(new Var(name, value, jsType));
    443     }
    444
    445     /***
    446      * Retrieve a variable.
    447      * @param mainKey The Variable's key
    448      * @return the Variable
    449      */
    450     public Var getVar(String mainKey) {
    451         return (Var) hVars.get(mainKey);
    452     }
    453
    454     /***
    455      * Retrieve a variable's value.
    456      * @param mainKey The Variable's key
    457      * @return the Variable's value
    458      */
    459     public String getVarValue(String mainKey) {
    460         String value = null;
    461
    462         Object o = hVars.get(mainKey);
    463         if (o != null && o instanceof Var) {
    464             Var v = (Var) o;
    465             value = v.getValue();
    466         }
    467
    468         return value;
    469     }
    470
    471     /***
    472      * The <code>Field</code>'s variables are returned as an
    473      * unmodifiable <code>Map</code>.
    474      * @return the Map of Variable's for a Field.
    475      */
    476     public Map getVars() {
    477         return Collections.unmodifiableMap(hVars);
    478     }
    479
    480     /***
    481      * Gets a unique key based on the property and indexedProperty fields.
    482      * @return a unique key for the field.
    483      */
    484     public String getKey() {
    485         if (this.key == null) {
    486             this.generateKey();
    487         }
    488
    489         return this.key;
    490     }
    491
    492     /***
    493      * Sets a unique key for the field.  This can be used to change
    494      * the key temporarily to have a unique key for an indexed field.
    495      * @param key a unique key for the field
    496      */
    497     public void setKey(String key) {
    498         this.key = key;
    499     }
    500
    501     /***
    502      * If there is a value specified for the indexedProperty field then
    503      * <code>true</code> will be returned.  Otherwise it will be
    504      * <code>false</code>.
    505      * @return Whether the Field is indexed.
    506      */
    507     public boolean isIndexed() {
    508         return ((indexedListProperty != null && indexedListProperty.length() > 0));
    509     }
    510
    511     /***
    512      * Generate correct <code>key</code> value.
    513      */
    514     public void generateKey() {
    515         if (this.isIndexed()) {
    516             this.key = this.indexedListProperty + TOKEN_INDEXED + "." + this.property;
    517         } else {
    518             this.key = this.property;
    519         }
    520     }
    521
    522     /***
    523      * Replace constants with values in fields and process the depends field
    524      * to create the dependency <code>Map</code>.
    525      */
    526     void process(Map globalConstants, Map constants) {
    527         this.hMsgs.setFast(false);
    528         this.hVars.setFast(true);
    529
    530         this.generateKey();
    531
    532         // Process FormSet Constants
    533         for (Iterator i = constants.keySet().iterator(); i.hasNext();) {
    534             String key = (String) i.next();
    535             String key2 = TOKEN_START + key + TOKEN_END;
    536             String replaceValue = (String) constants.get(key);
    537
    538             property = ValidatorUtils.replace(property, key2, replaceValue);
    539
    540             processVars(key2, replaceValue);
    541
    542             this.processMessageComponents(key2, replaceValue);
    543         }
    544
    545         // Process Global Constants
    546         for (Iterator i = globalConstants.keySet().iterator(); i.hasNext();) {
    547             String key = (String) i.next();
    548             String key2 = TOKEN_START + key + TOKEN_END;
    549             String replaceValue = (String) globalConstants.get(key);
    550
    551             property = ValidatorUtils.replace(property, key2, replaceValue);
    552
    553             processVars(key2, replaceValue);
    554
    555             this.processMessageComponents(key2, replaceValue);
    556         }
    557
    558         // Process Var Constant Replacement
    559         for (Iterator i = hVars.keySet().iterator(); i.hasNext();) {
    560             String key = (String) i.next();
    561             String key2 = TOKEN_START + TOKEN_VAR + key + TOKEN_END;
    562             Var var = this.getVar(key);
    563             String replaceValue = var.getValue();
    564
    565             this.processMessageComponents(key2, replaceValue);
    566         }
    567
    568         hMsgs.setFast(true);
    569     }
    570
    571     /***
    572      * Replace the vars value with the key/value pairs passed in.
    573      */
    574     private void processVars(String key, String replaceValue) {
    575         Iterator i = this.hVars.keySet().iterator();
    576         while (i.hasNext()) {
    577             String varKey = (String) i.next();
    578             Var var = this.getVar(varKey);
    579
    580             var.setValue(ValidatorUtils.replace(var.getValue(), key, replaceValue));
    581         }
    582
    583     }
    584
    585     /***
    586      * Replace the args key value with the key/value pairs passed in.
    587      */
    588     private void processMessageComponents(String key, String replaceValue) {
    589         String varKey = TOKEN_START + TOKEN_VAR;
    590         // Process Messages
    591         if (key != null && !key.startsWith(varKey)) {
    592             for (Iterator i = hMsgs.values().iterator(); i.hasNext();) {
    593                 Msg msg = (Msg) i.next();
    594                 msg.setKey(ValidatorUtils.replace(msg.getKey(), key, replaceValue));
    595             }
    596         }
    597
    598         this.processArg(key, replaceValue);
    599     }
    600
    601     /***
    602      * Replace the arg <code>Collection</code> key value with the key/value
    603      * pairs passed in.
    604      */
    605     private void processArg(String key, String replaceValue) {
    606         for (int i = 0; i < this.args.length; i++) {
    607
    608             Map argMap = this.args[i];
    609             if (argMap == null) {
    610                 continue;
    611             }
    612
    613             Iterator iter = argMap.values().iterator();
    614             while (iter.hasNext()) {
    615                 Arg arg = (Arg) iter.next();
    616
    617                 if (arg != null) {
    618                     arg.setKey(
    619                             ValidatorUtils.replace(arg.getKey(), key, replaceValue));
    620                 }
    621             }
    622         }
    623     }
    624
    625     /***
    626      * Checks if the validator is listed as a dependency.
    627      * @param validatorName Name of the validator to check.
    628      * @return Whether the field is dependant on a validator.
    629      */
    630     public boolean isDependency(String validatorName) {
    631         return this.dependencyList.contains(validatorName);
    632     }
    633
    634     /***
    635      * Gets an unmodifiable <code>List</code> of the dependencies in the same
    636      * order they were defined in parameter passed to the setDepends() method.
    637      * @return A list of the Field's dependancies.
    638      */
    639     public List getDependencyList() {
    640         return Collections.unmodifiableList(this.dependencyList);
    641     }
    642
    643     /***
    644      * Creates and returns a copy of this object.
    645      * @return A copy of the Field.
    646      */
    647     public Object clone() {
    648         Field field = null;
    649         try {
    650             field = (Field) super.clone();
    651         } catch(CloneNotSupportedException e) {
    652             throw new RuntimeException(e.toString());
    653         }
    654
    655         field.args = new Map[this.args.length];
    656         for (int i = 0; i < this.args.length; i++) {
    657             if (this.args[i] == null) {
    658                 continue;
    659             }
    660
    661             Map argMap = new HashMap(this.args[i]);
    662             Iterator iter = argMap.keySet().iterator();
    663             while (iter.hasNext()) {
    664                 String validatorName = (String) iter.next();
    665                 Arg arg = (Arg) argMap.get(validatorName);
    666                 argMap.put(validatorName, arg.clone());
    667             }
    668             field.args[i] = argMap;
    669         }
    670
    671         field.hVars = ValidatorUtils.copyFastHashMap(hVars);
    672         field.hMsgs = ValidatorUtils.copyFastHashMap(hMsgs);
    673
    674         return field;
    675     }
    676
    677     /***
    678      * Returns a string representation of the object.
    679      * @return A string representation of the object.
    680      */
    681     public String toString() {
    682         StringBuffer results = new StringBuffer();
    683
    684         results.append("\t\tkey = " + key + "\n");
    685         results.append("\t\tproperty = " + property + "\n");
    686         results.append("\t\tindexedProperty = " + indexedProperty + "\n");
    687         results.append("\t\tindexedListProperty = " + indexedListProperty + "\n");
    688         results.append("\t\tdepends = " + depends + "\n");
    689         results.append("\t\tpage = " + page + "\n");
    690         results.append("\t\tfieldOrder = " + fieldOrder + "\n");
    691
    692         if (hVars != null) {
    693             results.append("\t\tVars:\n");
    694             for (Iterator i = hVars.keySet().iterator(); i.hasNext();) {
    695                 Object key = i.next();
    696                 results.append("\t\t\t");
    697                 results.append(key);
    698                 results.append("=");
    699                 results.append(hVars.get(key));
    700                 results.append("\n");
    701             }
    702         }
    703
    704         return results.toString();
    705     }
    706
    707     /***
    708      * Returns an indexed property from the object we're validating.
    709      *
    710      * @param bean The bean to extract the indexed values from.
    711      * @throws ValidatorException If there's an error looking up the property
    712      * or, the property found is not indexed.
    713      */
    714     Object[] getIndexedProperty(Object bean) throws ValidatorException {
    715         Object indexedProperty = null;
    716
    717         try {
    718             indexedProperty =
    719                 PropertyUtils.getProperty(bean, this.getIndexedListProperty());
    720
    721         } catch(IllegalAccessException e) {
    722             throw new ValidatorException(e.getMessage());
    723         } catch(InvocationTargetException e) {
    724             throw new ValidatorException(e.getMessage());
    725         } catch(NoSuchMethodException e) {
    726             throw new ValidatorException(e.getMessage());
    727         }
    728
    729         if (indexedProperty instanceof Collection) {
    730             return ((Collection) indexedProperty).toArray();
    731
    732         } else if (indexedProperty.getClass().isArray()) {
    733             return (Object[]) indexedProperty;
    734
    735         } else {
    736             throw new ValidatorException(this.getKey() + " is not indexed");
    737         }
    738
    739     }
    740     /***
    741      * Returns the size of an indexed property from the object we're validating.
    742      *
    743      * @param bean The bean to extract the indexed values from.
    744      * @throws ValidatorException If there's an error looking up the property
    745      * or, the property found is not indexed.
    746      */
    747     private int getIndexedPropertySize(Object bean) throws ValidatorException {
    748         Object indexedProperty = null;
    749
    750         try {
    751             indexedProperty =
    752                 PropertyUtils.getProperty(bean, this.getIndexedListProperty());
    753
    754         } catch(IllegalAccessException e) {
    755             throw new ValidatorException(e.getMessage());
    756         } catch(InvocationTargetException e) {
    757             throw new ValidatorException(e.getMessage());
    758         } catch(NoSuchMethodException e) {
    759             throw new ValidatorException(e.getMessage());
    760         }
    761
    762         if (indexedProperty == null) {
    763             return 0;
    764         } else if (indexedProperty instanceof Collection) {
    765             return ((Collection)indexedProperty).size();
    766         } else if (indexedProperty.getClass().isArray()) {
    767             return ((Object[])indexedProperty).length;
    768         } else {
    769             throw new ValidatorException(this.getKey() + " is not indexed");
    770         }
    771
    772     }
    773
    774     /***
    775      * Executes the given ValidatorAction and all ValidatorActions that it
    776      * depends on.
    777      * @return true if the validation succeeded.
    778      */
    779     private boolean validateForRule(
    780         ValidatorAction va,
    781         ValidatorResults results,
    782         Map actions,
    783         Map params,
    784         int pos)
    785         throws ValidatorException {
    786
    787         ValidatorResult result = results.getValidatorResult(this.getKey());
    788         if (result != null && result.containsAction(va.getName())) {
    789             return result.isValid(va.getName());
    790         }
    791
    792         if (!this.runDependentValidators(va, results, actions, params, pos)) {
    793             return false;
    794         }
    795
    796         return va.executeValidationMethod(this, params, results, pos);
    797     }
    798
    799     /***
    800      * Calls all of the validators that this validator depends on.
    801      * TODO ValidatorAction should know how to run its own dependencies.
    802      * @param va Run dependent validators for this action.
    803      * @param results
    804      * @param actions
    805      * @param pos
    806      * @return true if all of the dependent validations passed.
    807      * @throws ValidatorException If there's an error running a validator
    808      */
    809     private boolean runDependentValidators(
    810         ValidatorAction va,
    811         ValidatorResults results,
    812         Map actions,
    813         Map params,
    814         int pos)
    815         throws ValidatorException {
    816
    817         List dependentValidators = va.getDependencyList();
    818
    819         if (dependentValidators.isEmpty()) {
    820             return true;
    821         }
    822
    823         Iterator iter = dependentValidators.iterator();
    824         while (iter.hasNext()) {
    825             String depend = (String) iter.next();
    826
    827             ValidatorAction action = (ValidatorAction) actions.get(depend);
    828             if (action == null) {
    829                 this.handleMissingAction(depend);
    830             }
    831
    832             if (!this.validateForRule(action, results, actions, params, pos)) {
    833                 return false;
    834             }
    835         }
    836
    837         return true;
    838     }
    839
    840     /***
    841      * Run the configured validations on this field.  Run all validations
    842      * in the depends clause over each item in turn, returning when the first
    843      * one fails.
    844      * @param params A Map of parameter class names to parameter values to pass
    845      * into validation methods.
    846      * @param actions A Map of validator names to ValidatorAction objects.
    847      * @return A ValidatorResults object containing validation messages for
    848      * this field.
    849      * @throws ValidatorException If an error occurs during validation.
    850      */
    851     public ValidatorResults validate(Map params, Map actions)
    852         throws ValidatorException {
    853
    854         if (this.getDepends() == null) {
    855             return new ValidatorResults();
    856         }
    857
    858         ValidatorResults allResults = new ValidatorResults();
    859
    860         Object bean = params.get(Validator.BEAN_PARAM);
    861         int numberOfFieldsToValidate =
    862             this.isIndexed() ? this.getIndexedPropertySize(bean) : 1;
    863
    864         for (int fieldNumber = 0; fieldNumber < numberOfFieldsToValidate; fieldNumber++) {
    865
    866             Iterator dependencies = this.dependencyList.iterator();
    867             ValidatorResults results = new ValidatorResults();
    868             while (dependencies.hasNext()) {
    869                 String depend = (String) dependencies.next();
    870
    871                 ValidatorAction action = (ValidatorAction) actions.get(depend);
    872                 if (action == null) {
    873                     this.handleMissingAction(depend);
    874                 }
    875
    876                 boolean good =
    877                     validateForRule(action, results, actions, params, fieldNumber);
    878
    879                 if (!good) {
    880                     allResults.merge(results);
    881                     return allResults;
    882                 }
    883             }
    884             allResults.merge(results);
    885         }
    886
    887         return allResults;
    888     }
    889
    890     /***
    891      * Called when a validator name is used in a depends clause but there is
    892      * no know ValidatorAction configured for that name.
    893      * @param name The name of the validator in the depends list.
    894      * @throws ValidatorException
    895      */
    896     private void handleMissingAction(String name) throws ValidatorException {
    897         throw new ValidatorException("No ValidatorAction named " + name
    898                 + " found for field " + this.getProperty());
    899     }
    900
    901     /***
    902      * Returns a Map of String Msg names to Msg objects.
    903      * @since Validator 1.2.0
    904      * @return A Map of the Field's messages.
    905      */
    906     protected Map getMsgMap() {
    907         return hMsgs;
    908     }
    909
    910     /***
    911      * Returns a Map of String Var names to Var objects.
    912      * @since Validator 1.2.0
    913      * @return A Map of the Field's variables.
    914      */
    915     protected Map getVarMap() {
    916         return hVars;
    917     }
    918
    919 }
    920