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