001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.validator;
018
019import java.io.Serializable;
020import java.lang.reflect.InvocationTargetException;
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.HashMap;
025import java.util.Iterator;
026import java.util.List;
027import java.util.Map;
028import java.util.Map.Entry;
029import java.util.StringTokenizer;
030
031import org.apache.commons.beanutils.PropertyUtils;
032import org.apache.commons.collections.FastHashMap; // DEPRECATED
033import org.apache.commons.validator.util.ValidatorUtils;
034
035/**
036 * This contains the list of pluggable validators to run on a field and any
037 * message information and variables to perform the validations and generate
038 * error messages.  Instances of this class are configured with a
039 * <field> xml element.
040 * <p>
041 * The use of FastHashMap is deprecated and will be replaced in a future
042 * release.
043 * </p>
044 *
045 * @version $Revision: 1441678 $ $Date: 2013-02-02 02:31:07 +0100 (Sa, 02 Feb 2013) $
046 * @see org.apache.commons.validator.Form
047 */
048public class Field implements Cloneable, Serializable {
049
050    private static final long serialVersionUID = -8502647722530192185L;
051
052    /**
053     * This is the value that will be used as a key if the <code>Arg</code>
054     * name field has no value.
055     */
056    private static final String DEFAULT_ARG =
057            "org.apache.commons.validator.Field.DEFAULT";
058
059    /**
060     * This indicates an indexed property is being referenced.
061     */
062    public static final String TOKEN_INDEXED = "[]";
063
064    /**
065     * The start of a token.
066     */
067    protected static final String TOKEN_START = "${";
068
069    /**
070     * The end of a token.
071     */
072    protected static final String TOKEN_END = "}";
073
074    /**
075     * A Vriable token.
076     */
077    protected static final String TOKEN_VAR = "var:";
078
079    /**
080     * The Field's property name.
081     */
082    protected String property = null;
083
084    /**
085     * The Field's indexed property name.
086     */
087    protected String indexedProperty = null;
088
089    /**
090     * The Field's indexed list property name.
091     */
092    protected String indexedListProperty = null;
093
094    /**
095     * The Field's unique key.
096     */
097    protected String key = null;
098
099    /**
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