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: 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