View Javadoc

1   package org.springmodules.validation.bean.conf.loader.xml.handler;
2   
3   import org.springframework.util.ObjectUtils;
4   import org.springframework.util.StringUtils;
5   import org.springmodules.validation.bean.conf.MutableBeanValidationConfiguration;
6   import org.springmodules.validation.bean.rule.AbstractValidationRule;
7   import org.springmodules.validation.bean.rule.resolver.ErrorArgumentsResolver;
8   import org.springmodules.validation.bean.rule.resolver.FunctionErrorArgumentsResolver;
9   import org.springmodules.validation.util.cel.ConditionExpressionBased;
10  import org.springmodules.validation.util.cel.ConditionExpressionParser;
11  import org.springmodules.validation.util.cel.valang.ValangConditionExpressionParser;
12  import org.springmodules.validation.util.condition.Condition;
13  import org.springmodules.validation.util.fel.FunctionExpressionBased;
14  import org.springmodules.validation.util.fel.FunctionExpressionParser;
15  import org.springmodules.validation.util.fel.parser.ValangFunctionExpressionParser;
16  import org.w3c.dom.Element;
17  
18  /**
19   * A base class for common {@link PropertyValidationElementHandler}
20   * implementations that represent validation rules. This base handler idetifies the supported elements by their
21   * tag names (qualified and local). In addition, it assumes the following common attributes:
22   * <p/>
23   * <ul>
24   * <li>code - Indicates the error code of the validatoin rule</li>
25   * <li>message - The default error message of the validation rule</li>
26   * <li>args - A comma separated list of error arguments</li>
27   * <li>apply-if - An condition expression that determines the applicability of the validation rule</li>
28   * </ul>
29   * <p/>
30   * Note: The apply-if attribute is being parsed by the
31   * {@link org.springmodules.validation.util.cel.ConditionExpressionParser} that is associated with this handler. It
32   * uses {@link org.springmodules.validation.util.cel.valang.ValangConditionExpressionParser} by default.
33   *
34   * @author Uri Boness
35   */
36  public abstract class AbstractClassValidationElementHandler
37      implements ClassValidationElementHandler, ConditionExpressionBased, FunctionExpressionBased {
38  
39      private static final String ERROR_CODE_ATTR = "code";
40  
41      private static final String MESSAGE_ATTR = "message";
42  
43      private static final String ARGS_ATTR = "args";
44  
45      private static final String APPLY_IF_ATTR = "apply-if";
46  
47      private static final String CONTEXTS_ATTR = "contexts";
48  
49      private String elementName;
50  
51      private String namespaceUrl;
52  
53      private ConditionExpressionParser conditionExpressionParser;
54  
55      private FunctionExpressionParser functionExpressionParser;
56  
57      /**
58       * Constructs a new AbstractPropertyValidationElementHandler with given supported element name.
59       *
60       * @param elementName The supported element name.
61       */
62      public AbstractClassValidationElementHandler(String elementName) {
63          this(elementName, null);
64      }
65  
66      /**
67       * Constructs a new AbstractPropertyValidationElementHandler with given supported element name and namespace.
68       *
69       * @param elementName The supported element name.
70       * @param namespace The supported namespace.
71       */
72      public AbstractClassValidationElementHandler(String elementName, String namespace) {
73          this(elementName, namespace, new ValangConditionExpressionParser(), new ValangFunctionExpressionParser());
74      }
75  
76      /**
77       * Constructs a new AbstractPropertyValidationElementHandler with given supported element name and namespace and a
78       * condition handler to parse the <code>apply-if</code> expressions.
79       *
80       * @param elementName The supported element name.
81       * @param namespace The supported namespace.
82       * @param conditionExpressionParser The condition expression parser to be used to parse the various condition expressions.
83       * @param functionExpressionParser The function expression parser to be used to parse the error arguments expressions.
84       */
85      public AbstractClassValidationElementHandler(
86          String elementName,
87          String namespace,
88          ConditionExpressionParser conditionExpressionParser,
89          FunctionExpressionParser functionExpressionParser) {
90  
91          this.elementName = elementName;
92          this.namespaceUrl = namespace;
93          this.conditionExpressionParser = conditionExpressionParser;
94          this.functionExpressionParser = functionExpressionParser;
95      }
96  
97      /**
98       * Determines whether the given element is supported by this handler. The check is done by comparing the element
99       * tag name and namespace with the ones that are configured with this handler.
100      *
101      * @see org.springmodules.validation.bean.conf.loader.xml.handler.PropertyValidationElementHandler#supports(org.w3c.dom.Element, Class, java.beans.PropertyDescriptor)
102      */
103     public boolean supports(Element element, Class clazz) {
104         String localName = element.getLocalName();
105         if (!localName.equals(elementName)) {
106             return false;
107         }
108         String ns = element.getNamespaceURI();
109         return ObjectUtils.nullSafeEquals(ns, namespaceUrl);
110     }
111 
112     /**
113      * Creates the appropriate {@link org.springmodules.validation.bean.rule.ValidationRule} based on the given element
114      * and adds it to the given configuration.
115      *
116      * @see org.springmodules.validation.bean.conf.loader.xml.handler.AbstractClassValidationElementHandler#handle(org.w3c.dom.Element, org.springmodules.validation.bean.conf.MutableBeanValidationConfiguration)
117      */
118     public void handle(Element element, MutableBeanValidationConfiguration configuration) {
119 
120         AbstractValidationRule rule = createValidationRule(element);
121 
122         String errorCode = extractErrorCode(element);
123         if (errorCode != null) {
124             rule.setErrorCode(errorCode);
125         }
126 
127         String message = extractMessage(element);
128         if (message != null) {
129             rule.setDefaultErrorMessage(message);
130         }
131 
132         ErrorArgumentsResolver argumentsResolver = extractArgumentsResolver(element);
133         if (argumentsResolver != null) {
134             rule.setErrorArgumentsResolver(argumentsResolver);
135         }
136 
137         Condition applicabilityCondition = extractApplicabilityCondition(element);
138         if (applicabilityCondition != null) {
139             rule.setApplicabilityCondition(applicabilityCondition);
140         }
141 
142         String[] applicableContexts = extractApplicableContexts(element);
143         if (applicableContexts != null) {
144             rule.setContextTokens(applicableContexts);
145         }
146 
147         configuration.addGlobalRule(rule);
148     }
149 
150     /**
151      * By default the element handlers handle and produce rules that can be associated with both global and non-global
152      * contexts.
153      */
154     public boolean isConditionGloballyScoped() {
155         return false;
156     }
157 
158     /**
159      * Extracts the validation rule error code from the given element. Expects a "code" attribute to indicate the
160      * error code. If no such attribute exisits, returns <code>null</code>.
161      *
162      * @param element The element that represents the validation rule.
163      * @return The validation rule error code.
164      */
165     protected String extractErrorCode(Element element) {
166         String code = element.getAttribute(AbstractClassValidationElementHandler.ERROR_CODE_ATTR);
167         return (StringUtils.hasLength(code)) ? code : null;
168     }
169 
170     /**
171      * Extracts the validation rule error message from the given element. Expects a "message" attribute to indicate the
172      * error message. If no such attribute exisits, returns <code>null</code> instead.
173      *
174      * @param element The element that represents the validation rule.
175      * @return The validation rule error message.
176      */
177     protected String extractMessage(Element element) {
178         String message = element.getAttribute(AbstractClassValidationElementHandler.MESSAGE_ATTR);
179         return (StringUtils.hasLength(message)) ? message : null;
180     }
181 
182     /**
183      * Extracts the validation rule error arguments from the given element. Expects an "args" attribute to indicate the
184      * error arguments. If no such attribute exisits, returns <code>null</code>.
185      *
186      * @param element The element that represents the validation rule.
187      * @return The validation rule error arguments.
188      */
189     protected ErrorArgumentsResolver extractArgumentsResolver(Element element) {
190         String argsString = element.getAttribute(AbstractClassValidationElementHandler.ARGS_ATTR);
191         String[] expressions = (argsString == null) ? new String[0] : StringUtils.tokenizeToStringArray(argsString, ", ");
192         if (expressions.length == 0) {
193             return null;
194         }
195         return new FunctionErrorArgumentsResolver(expressions, functionExpressionParser);
196     }
197 
198     /**
199      * Extracts the validation rule applicability condition from the given element. Expects an "apply-if" attribute to
200      * indicate the condition expression. If no such attribute exisits, returns <code>null</code>.
201      *
202      * @param element The element that represents the validation rule.
203      * @return The validation rule applicability condition.
204      */
205     protected Condition extractApplicabilityCondition(Element element) {
206         String expression = element.getAttribute(AbstractClassValidationElementHandler.APPLY_IF_ATTR);
207         return (StringUtils.hasText(expression)) ? conditionExpressionParser.parse(expression) : null;
208     }
209 
210     /**
211      * Extracts the names of the validation context in which the valiation rule is applicable. Expects a "contexts"
212      * attribute to hold a comma-separated list of context names. If no such attribute exists or if it holds an empty
213      * string, <code>null </code> is returned. As the contract of {@link AbstractValidationRule#setContextTokens(String[])}
214      * defines <code>null</code> means that the rule always applies regardless of the context.
215      *
216      * @param element The element that represents the validation rule.
217      * @return The names of the validation contexts in which the
218      */
219     protected String[] extractApplicableContexts(Element element) {
220         String contextString = element.getAttribute(CONTEXTS_ATTR);
221         return (StringUtils.hasText(contextString)) ? StringUtils.commaDelimitedListToStringArray(contextString) : null;
222     }
223 
224     /**
225      * @see ConditionExpressionBased#setConditionExpressionParser(org.springmodules.validation.util.cel.ConditionExpressionParser)
226      */
227     public void setConditionExpressionParser(ConditionExpressionParser conditionExpressionParser) {
228         this.conditionExpressionParser = conditionExpressionParser;
229     }
230 
231     /**
232      * Returns the condition expression parser associated with this handler.
233      *
234      * @return The condition expression parser associated with this handler.
235      */
236     protected ConditionExpressionParser getConditionExpressionParser() {
237         return conditionExpressionParser;
238     }
239 
240     /**
241      * @see FunctionExpressionBased#setFunctionExpressionParser(org.springmodules.validation.util.fel.FunctionExpressionParser)
242      */
243     public void setFunctionExpressionParser(FunctionExpressionParser functionExpressionParser) {
244         this.functionExpressionParser = functionExpressionParser;
245     }
246 
247     /**
248      * Returns the {@link FunctionExpressionParser} used by this handler to parse the error argument expressions.
249      *
250      * @return The {@link FunctionExpressionParser} used by this handler to parse the error argument expressions.
251      */
252     protected FunctionExpressionParser getFunctionExpressionParser() {
253         return functionExpressionParser;
254     }
255 
256     /**
257      * Indicates whether the validation rule supports null values. Null values support means such values will be
258      * passed to the rule condition during validation. If the rule doesn't support null values, such value will not
259      * be evaluated by the rule condition and the validation will treat them as valid values.
260      *
261      * @return <code>true</code> if the validation rule support null values, <code>false</code> otherwise.
262      */
263     protected boolean isNullSupported() {
264         return false;
265     }
266 
267     /**
268      * Creates the validation rule represented and initialized by and with the given element.
269      *
270      * @param element The element that represents the validation rule.
271      * @return The newly created validation rule.
272      */
273     protected abstract AbstractValidationRule createValidationRule(Element element);
274 
275 }