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