View Javadoc

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