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