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