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-01 20:31:07 -0500 (Fri, 01 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