1 /* 2 * Copyright 2004-2009 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springmodules.validation.bean; 18 19 import java.util.*; 20 import java.util.Map.Entry; 21 22 import org.springframework.validation.Errors; 23 import org.springframework.validation.Validator; 24 import org.springmodules.validation.bean.rule.DefaultValidationRule; 25 import org.springmodules.validation.bean.rule.PropertyValidationRule; 26 import org.springmodules.validation.bean.rule.ValidationRule; 27 import org.springmodules.validation.util.condition.Condition; 28 29 /** 30 * A {@link org.springframework.validation.Validator} implementation which uses {@link org.springmodules.validation.bean.rule.ValidationRule}'s to define its 31 * validation execution. There are two types of validation rules this validator accepts: 32 * <ul> 33 * <li> 34 * Global Rules - Rules that apply on the validated objects and where all validation errors are registered 35 * globaly with the {@link Errors} object (i.e. {@link Errors#reject(String)}). 36 * </li> 37 * <li> 38 * Property Rules - Rules that apply on specific properties of the validated objects and where all validation 39 * errors are registered with the {@link Errors} object under the context of those properties 40 * (i.e. {@link Errors#rejectValue(String, String)}). 41 * </li> 42 * </ul> 43 * 44 * @author Uri Boness 45 */ 46 public class RuleBasedValidator implements Validator { 47 48 // a list of global ValidationRule's - List<ValidationRule> 49 private List globalRules; 50 51 // maps a list of ValidationRule's to propertyNames - Map<String, List<ValidationRule>> 52 private Map rulesByProperty; 53 54 /** 55 * Contrusts a new RuleBasedValidator for the given type. After contruction, this validator will initially hold 56 * no rules. 57 */ 58 public RuleBasedValidator() { 59 globalRules = new ArrayList(); 60 rulesByProperty = new HashMap(); 61 } 62 63 /** 64 * This validator supports all classes. Any object can be validated by this validator as long as the validation rules 65 * apply to it. 66 * 67 * @see Validator#supports(Class) 68 */ 69 public boolean supports(Class clazz) { 70 return true; 71 } 72 73 /** 74 * Validates the given object and registers all validation errors with the given errors object. The validation 75 * is done by applying all validation rules associated with this validator on the given object. 76 * 77 * @see org.springframework.validation.Validator#validate(Object, org.springframework.validation.Errors) 78 */ 79 public void validate(Object obj, Errors errors) { 80 81 // validating using the registered global rules 82 for (Iterator iter = globalRules.iterator(); iter.hasNext();) { 83 ValidationRule rule = (ValidationRule) iter.next(); 84 if (rule.isApplicable(obj) && !rule.getCondition().check(obj)) { 85 errors.reject(rule.getErrorCode(), rule.getErrorArguments(obj), rule.getDefaultErrorMessage()); 86 } 87 } 88 89 // validating using the registered field rules 90 for (Iterator names = rulesByProperty.keySet().iterator(); names.hasNext();) { 91 String propertyName = (String) names.next(); 92 List rules = (List) rulesByProperty.get(propertyName); 93 for (Iterator iter = rules.iterator(); iter.hasNext();) { 94 ValidationRule rule = (ValidationRule) iter.next(); 95 if (rule.isApplicable(obj) && !rule.getCondition().check(obj)) { 96 errors.rejectValue(propertyName, rule.getErrorCode(), rule.getErrorArguments(obj), rule.getDefaultErrorMessage()); 97 } 98 } 99 } 100 } 101 102 //====== Setters to support JavaBean based configuration environment (e.g. spring's application context =========== 103 104 /** 105 * Sets extra global validation rules for this validator. 106 * 107 * @param globalRules The extra global validation rules to be added to this validator. 108 */ 109 public void setExtraGlobalVadlidationRules(ValidationRule[] globalRules) { 110 for (int i = 0; i < globalRules.length; i++) { 111 addGlobalRule(globalRules[i]); 112 } 113 } 114 115 /** 116 * Sets extra property validation rules for this validator. 117 * 118 * @param rulesByProperty The extra property validation rules for this validator. The map should hold the property 119 * names as keys and {@link ValidationRule} instances as values. 120 */ 121 public void setExtraPropertyValidationRules(Map rulesByProperty) { 122 for (Iterator entries = rulesByProperty.entrySet().iterator(); entries.hasNext();) { 123 Entry entry = (Entry) entries.next(); 124 addPropertyRule((String) entry.getKey(), (ValidationRule) entry.getValue()); 125 } 126 } 127 128 //=================================== Property Rules Regitration Methods ========================================== 129 130 /** 131 * Adds the given property rule to this validator. A property rule is a condition and error information that is 132 * associated with a property name. When this rule is applied on an object, the condition is checked against the 133 * value of the associated property of the (validated) object. All validation errors of this rule will be registered 134 * with the {@link Errors} object under the context of the associated property. Note that the associated property 135 * may be a nested property - in that case, the nested property value will be resolved and the condition will be 136 * applied on the this value. 137 * 138 * @param propertyName The name of the property the added rule is associated with. 139 * @param fieldValueCondition The condition of the rule. 140 * @param errorCode The error code of the rule. 141 */ 142 public void addPropertyRule(String propertyName, Condition fieldValueCondition, String errorCode) { 143 addPropertyRule(propertyName, fieldValueCondition, errorCode, errorCode, new Object[0]); 144 } 145 146 /** 147 * Adds the given property rule to this validator. 148 * 149 * @param propertyName The name of the property the added rule is associated with. 150 * @param fieldValueCondition The condition of the rule. 151 * @param errorCode The error code of the rule. 152 * @param args The arguments of the error code of the rule. 153 * @see #addPropertyRule(String, org.springmodules.validation.util.condition.Condition, String, Object[]) 154 */ 155 public void addPropertyRule(String propertyName, Condition fieldValueCondition, String errorCode, Object[] args) { 156 addPropertyRule(propertyName, fieldValueCondition, errorCode, errorCode, args); 157 } 158 159 /** 160 * Adds the given property rule to this validator. 161 * 162 * @param propertyName The name of the property the added rule is associated with. 163 * @param fieldValueCondition The condition of the rule. 164 * @param errorCode The error code of the rule. 165 * @param message The default error message of the rule. 166 * @param args The arguments of the error code of the rule. 167 * @see #addPropertyRule(String, org.springmodules.validation.util.condition.Condition, String, Object[]) 168 */ 169 public void addPropertyRule( 170 String propertyName, 171 Condition fieldValueCondition, 172 String errorCode, 173 String message, 174 Object[] args) { 175 176 addPropertyRule(propertyName, new DefaultValidationRule(fieldValueCondition, errorCode, message, args)); 177 } 178 179 /** 180 * Adds the given property rule for the given property. 181 * 182 * @param propertyName The name of the property associated with the added rule. 183 * @param propertyRule The rule that should be applied on the value of the given property. 184 * @see #addPropertyRule(String, org.springmodules.validation.util.condition.Condition, String) 185 */ 186 public void addPropertyRule(String propertyName, ValidationRule propertyRule) { 187 addPropertyGlobalRule(propertyName, new PropertyValidationRule(propertyName, propertyRule)); 188 } 189 190 /** 191 * Adds a property rule for the given property. The given rule is actually a global rule, that is, it is being 192 * evaluated on the validated object, not on the property value. The only difference between this added rule and 193 * a global rule is that the validation errors of this rule are associated with the given property and are not 194 * associated globaly with the validated object. 195 * <p/> 196 * This type of rule comes in handy when a complex validation is required on the validation object, but the error 197 * is should only be associated with a specific property. An example would be when two properties of the validated 198 * object should match (i.e. have the same value), but if they don't, the error will only be associated with one 199 * of them (e.g. when a UserRegistration object holds a password and confirmPassword properties - this should 200 * generally match, but if they don't, the error will only be associated with the confirmPassword property). 201 * 202 * @param propertyName The name of the property to associated with the added rule. 203 * @param globalRule The global rule to be added. 204 */ 205 public void addPropertyGlobalRule(String propertyName, ValidationRule globalRule) { 206 List rules = (List) rulesByProperty.get(propertyName); 207 if (rules == null) { 208 rules = new ArrayList(); 209 rulesByProperty.put(propertyName, rules); 210 } 211 rules.add(globalRule); 212 } 213 214 //==================================== Global Rules Regitration Methods ========================================== 215 216 /** 217 * Adds a new global validation rule to this validator. The global validation rule applied on the object level and 218 * all validation error of this rule will be registered with the {@link Errors} object globaly 219 * (see {@link Errors#reject(String)}). 220 * 221 * @param condition The condition of the added rule. 222 * @param errorCode The error code of the added rule. 223 */ 224 public void addGlobalRule(Condition condition, String errorCode) { 225 addGlobalRule(condition, errorCode, errorCode, new Object[0]); 226 } 227 228 /** 229 * Adds a new global validation rule to this validator. 230 * 231 * @param condition The condition of the added rule. 232 * @param errorCode The error code of the added rule. 233 * @param args The arguments for the error of the added rule. 234 * @see #addGlobalRule(org.springmodules.validation.util.condition.Condition, String) 235 */ 236 public void addGlobalRule(Condition condition, String errorCode, Object[] args) { 237 addGlobalRule(condition, errorCode, errorCode, args); 238 } 239 240 /** 241 * Adds a new global validation rule to this validator. 242 * 243 * @param condition The condition of the added rule. 244 * @param errorCode The error code of the added rule. 245 * @param message The error message of the added rule. 246 * @param args The arguments for the error of the added rule. 247 * @see #addGlobalRule(org.springmodules.validation.util.condition.Condition, String) 248 */ 249 public void addGlobalRule(Condition condition, String errorCode, String message, Object[] args) { 250 addGlobalRule(new DefaultValidationRule(condition, errorCode, message, args)); 251 } 252 253 /** 254 * Adds a new global validation rule to this validator. 255 * 256 * @param condition The condition of the added rule. 257 * @param errorCode The error code of the added rule. 258 * @param message The default error message of the added rule. 259 * @see #addGlobalRule(org.springmodules.validation.util.condition.Condition, String) 260 */ 261 public void addGlobalRule(Condition condition, String errorCode, String message) { 262 addGlobalRule(new DefaultValidationRule(condition, errorCode, message, new Object[0])); 263 } 264 265 /** 266 * Adds the given validation rule as a global rule to this validator. 267 * 268 * @param globalRule The global rule to be added to this validator. 269 */ 270 public void addGlobalRule(ValidationRule globalRule) { 271 globalRules.add(globalRule); 272 } 273 274 }