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