View Javadoc

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 }