Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
CommonsValidator |
|
| 2.5555555555555554;2.556 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
3 | * contributor license agreements. See the NOTICE file distributed with | |
4 | * this work for additional information regarding copyright ownership. | |
5 | * The ASF licenses this file to you under the Apache License, Version 2.0 | |
6 | * (the "License"); you may not use this file except in compliance with | |
7 | * the License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | ||
18 | package org.apache.shale.validator; | |
19 | ||
20 | import java.io.Serializable; | |
21 | import java.lang.reflect.InvocationTargetException; | |
22 | import java.lang.reflect.Method; | |
23 | import java.lang.reflect.Modifier; | |
24 | import java.text.MessageFormat; | |
25 | import java.util.ArrayList; | |
26 | import java.util.HashMap; | |
27 | import java.util.List; | |
28 | import java.util.Locale; | |
29 | import java.util.Map; | |
30 | import java.util.MissingResourceException; | |
31 | import java.util.ResourceBundle; | |
32 | import java.util.StringTokenizer; | |
33 | ||
34 | import javax.faces.application.Application; | |
35 | import javax.faces.application.FacesMessage; | |
36 | import javax.faces.component.UIComponent; | |
37 | import javax.faces.context.ExternalContext; | |
38 | import javax.faces.context.FacesContext; | |
39 | import javax.faces.validator.Validator; | |
40 | import javax.faces.validator.ValidatorException; | |
41 | ||
42 | import org.apache.commons.logging.Log; | |
43 | import org.apache.commons.logging.LogFactory; | |
44 | import org.apache.commons.validator.Arg; | |
45 | import org.apache.commons.validator.Field; | |
46 | import org.apache.commons.validator.Form; | |
47 | import org.apache.commons.validator.GenericValidator; | |
48 | import org.apache.commons.validator.ValidatorAction; | |
49 | import org.apache.commons.validator.ValidatorResources; | |
50 | import org.apache.commons.validator.Var; | |
51 | import org.apache.shale.util.ConverterHelper; | |
52 | import org.apache.shale.util.Messages; | |
53 | import org.apache.shale.util.Tags; | |
54 | ||
55 | /** | |
56 | * <p>This is a JavaServer Faces <code>Validator</code> that uses | |
57 | * Jakarta Commons Validator to perform validation, either on the | |
58 | * client side, the server side, or both.</p> | |
59 | * | |
60 | * <p>The current implementation is dependent on version 1.3 of Commons Validator. | |
61 | * Some new conventions have been adopted for registering a validation rule in | |
62 | * the validator's XML configuration file. In the action framework, validation | |
63 | * was suited for declaring rules associated with a form. This worked well since | |
64 | * generally a single action had the responsibility of handling all the posted | |
65 | * form data at once.</p> | |
66 | * | |
67 | * <p>However, JSF takes a component based approach. Each component has the | |
68 | * responsibility of managing its posted data and rendering its state. In the | |
69 | * component based world, it is easier to associate configuration values at | |
70 | * that level versus what works best for Struts actions.</p> | |
71 | * | |
72 | * <p>In an effort to reuse as much of Commons Validator and provide a method of | |
73 | * registering new rules, a new convention was adopted for declaring validation | |
74 | * rules.</p> | |
75 | * | |
76 | * <pre> | |
77 | * <global> | |
78 | * <validator name="mask" | |
79 | * classname="org.apache.commons.validator.GenericValidator" | |
80 | * method="matchRegexp" | |
81 | * methodParams="java.lang.String,java.lang.String" | |
82 | * msg="errors.invalid" | |
83 | * jsFunctionName="validateMask" | |
84 | * jsFunction="org.apache.commons.validator.javascript.validateMask" | |
85 | * depends=""/> | |
86 | * </global> | |
87 | * </pre> | |
88 | * | |
89 | * <p>The rules declaration is the same but an added form is required to capture extra | |
90 | * configuration information. The form is associated with the validation rule using a | |
91 | * naming convention. The prefix of the form name is "org.apache.shale.validator.XXXX" | |
92 | * where "XXXX" is the validation rule name.</p> | |
93 | * | |
94 | * <pre> | |
95 | * <formset> | |
96 | * <form name="org.apache.shale.validator.mask"> | |
97 | * </pre> | |
98 | * | |
99 | * | |
100 | * <p>The form is followed by a field and the property attribute of the form has the | |
101 | * same value as the rule name.</p> | |
102 | * | |
103 | * <pre> | |
104 | * <field property="mask"> | |
105 | * </pre> | |
106 | * | |
107 | * <p>Within the field definition, arg's are used to define the parameters in order | |
108 | * for message substitution and method argument value resolution. There are two reserved | |
109 | * name values for the arg node used to define messages and parameters.</p> | |
110 | * | |
111 | * <pre> | |
112 | * <arg position="0" name="message" key="arg" resource="false"/> | |
113 | * <arg position="1" name="message" key="mask" resource="false"/> | |
114 | * <arg position="2" name="message" key="submittedValue" resource="false"/> | |
115 | * | |
116 | * <arg position="0" name="parameter" key="submittedValue" resource="false"/> | |
117 | * <arg position="1" name="parameter" key="mask" resource="false"/> | |
118 | * </pre> | |
119 | * | |
120 | * <p>The "message" name arguments defines the possible <code>MessageFormat</code> parameter substitution | |
121 | * where the "position" corresponds to the substitution parameter.</p> | |
122 | * | |
123 | * <pre> | |
124 | * errors.invalid={0} is invalid. | |
125 | * </pre> | |
126 | * | |
127 | * <p>The "parameter" arguments define the variable names that hold values for the target validatior method | |
128 | * identified by the validator rule name. The comma delimited class types in the "methodParms" value list | |
129 | * correspond to the parameters by position.</p> | |
130 | * | |
131 | * <pre> | |
132 | * methodParams="java.lang.String,java.lang.String" | |
133 | * </pre> | |
134 | * | |
135 | * <p>The var node is also used to explicitly define a JavaScript variable type. If not | |
136 | * defined, the default is "string". The var-value is ignored because its captured by | |
137 | * the shale commons validator instance.</p> | |
138 | * | |
139 | * <pre> | |
140 | * <var> | |
141 | * <var-name>mask</var-name> | |
142 | * <var-value></var-value> | |
143 | * <var-jstype>regexp</var-jstype> | |
144 | * </var> | |
145 | * </pre> | |
146 | * | |
147 | * $Id: CommonsValidator.java 481404 2006-12-01 21:29:27Z rahul $ | |
148 | */ | |
149 | ||
150 | 39 | public class CommonsValidator implements Validator, Serializable { |
151 | ||
152 | ||
153 | // -------------------------------------------------------- Static Variables | |
154 | ||
155 | ||
156 | /** | |
157 | * Serial version UID. | |
158 | */ | |
159 | private static final long serialVersionUID = -1783130706547542798L; | |
160 | ||
161 | /** | |
162 | * <p>Map of standard types: boolean, byte, char, etc. | |
163 | * and their corresponding classes.</p> | |
164 | */ | |
165 | private static Map standardTypes; | |
166 | ||
167 | ||
168 | /** | |
169 | * <p>Localized messages for this class.</p> | |
170 | */ | |
171 | 1 | private static final Messages messages = |
172 | new Messages("org.apache.shale.validator.resources.Bundle"); | |
173 | ||
174 | ||
175 | /** | |
176 | * <p>Log instance for this class.</p> | |
177 | */ | |
178 | 1 | private static final Log log = LogFactory.getLog("org.apache.shale.validator"); |
179 | ||
180 | ||
181 | /** | |
182 | * <p>The name of the parent form used for javascript.</p> | |
183 | */ | |
184 | 36 | private String formName = null; |
185 | ||
186 | /** | |
187 | * <p>Returns the parent's form name.</p> | |
188 | * | |
189 | * @return form name the validator is contained in | |
190 | */ | |
191 | public String getFormName() { | |
192 | 30 | return formName; |
193 | } | |
194 | ||
195 | /** | |
196 | * <p>Sets the validator's owning form name.</p> | |
197 | * | |
198 | * @param formName The new form name | |
199 | */ | |
200 | public void setFormName(String formName) { | |
201 | 26 | this.formName = formName; |
202 | 26 | } |
203 | ||
204 | ||
205 | // -------------------------------------------------------- Instance Variables | |
206 | ||
207 | /** | |
208 | * <p>Validator type.</p> | |
209 | */ | |
210 | private String type; | |
211 | ||
212 | /** | |
213 | * <p>Enable client-side validation?</p> | |
214 | */ | |
215 | private Boolean client; | |
216 | ||
217 | ||
218 | /** | |
219 | * <p>Enable server-side validation?</p> | |
220 | */ | |
221 | private Boolean server; | |
222 | ||
223 | ||
224 | /** | |
225 | * <p>The <code>validate</code> method uses this | |
226 | * as the text of an error message it stores on the | |
227 | * FacesContext when validation fails.</p> | |
228 | */ | |
229 | private String message; | |
230 | ||
231 | ||
232 | /** | |
233 | * <p>Parameters for the specific Commons Validator to be used.</p> | |
234 | */ | |
235 | 36 | private Map vars = new HashMap(); |
236 | ||
237 | /** | |
238 | * <p>Returns a <code>Map</code> of variables that can be passed to a | |
239 | * commons validator method or used to create a parameterized error | |
240 | * message. Several of the public properties are contained within | |
241 | * the <code>vars</code> collection. These include: arg, min, max, | |
242 | * minlength, maxlength, mask, datePatternStrict.</p> | |
243 | * | |
244 | * @return A value paired collection of variables used to invoke a | |
245 | * validator method. | |
246 | */ | |
247 | public Map getVars() { | |
248 | 39 | return vars; |
249 | } | |
250 | ||
251 | ||
252 | // -------------------------------------------------------- Transient Variables | |
253 | ||
254 | ||
255 | ||
256 | // ----------------------------------------------------- Property Accessors | |
257 | ||
258 | ||
259 | /** | |
260 | * <p>The setter method for the <code>type</code> property. This property is | |
261 | * passed through to the Commons Validator.</p> | |
262 | * | |
263 | * @param newValue The new value for the <code>type</code> property. | |
264 | */ | |
265 | public void setType(String newValue) { | |
266 | 36 | type = newValue; |
267 | 36 | } |
268 | ||
269 | ||
270 | /** | |
271 | * <p>The getter method for the <code>type</code> property. This property is | |
272 | * passed through to the Commons Validator.</p> | |
273 | * | |
274 | * @return validation rule to apply | |
275 | */ | |
276 | public String getType() { | |
277 | 235 | return type; |
278 | } | |
279 | ||
280 | ||
281 | /** | |
282 | * <p>The setter method for the <code>client</code> property. This property is | |
283 | * passed through to the Commons Validator.</p> | |
284 | * | |
285 | * @param newValue The new value for the <code>client</code> property. | |
286 | */ | |
287 | public void setClient(Boolean newValue) { | |
288 | 42 | client = newValue; |
289 | 42 | } |
290 | ||
291 | ||
292 | /** | |
293 | * <p>The getter method for the <code>client</code> property. This property is | |
294 | * passed through to the Commons Validator.</p> | |
295 | * | |
296 | * @return <code>true</code> if using JavaScript validation | |
297 | */ | |
298 | public Boolean getClient() { | |
299 | 26 | return client; |
300 | } | |
301 | ||
302 | ||
303 | /** | |
304 | * <p>The setter method for the <code>server</code> property. This property is | |
305 | * passed through to the Commons Validator.</p> | |
306 | * | |
307 | * @param newValue The new value for the <code>server</code> property. | |
308 | */ | |
309 | public void setServer(Boolean newValue) { | |
310 | 36 | server = newValue; |
311 | 36 | } |
312 | ||
313 | ||
314 | /** | |
315 | * <p>The getter method for the <code>server</code> property. This property is | |
316 | * passed through to the Commons Validator.</p> | |
317 | * | |
318 | * @return <code>true</code> if using server side validation | |
319 | */ | |
320 | public Boolean getServer() { | |
321 | 45 | return server; |
322 | } | |
323 | ||
324 | ||
325 | /** | |
326 | * <p>The setter method for the <code>message</code> property. This property is | |
327 | * passed through to the Commons Validator.</p> | |
328 | * | |
329 | * @param newValue The new value for the <code>message</code> property. | |
330 | */ | |
331 | public void setMessage(String newValue) { | |
332 | 1 | message = newValue; |
333 | 1 | } |
334 | ||
335 | ||
336 | /** | |
337 | * <p>The getter method for the <code>message</code> property. This property is | |
338 | * passed through to the Commons Validator.</p> | |
339 | * | |
340 | * @return validation message override | |
341 | */ | |
342 | public String getMessage() { | |
343 | 48 | return message; |
344 | } | |
345 | ||
346 | /** | |
347 | * <p>Name of the <code>arg</code> property in the <code>vars</code> Map.</p> | |
348 | */ | |
349 | private static final String ARG_VARNAME = "arg"; | |
350 | ||
351 | /** | |
352 | * <p>The setter method for the <code>arg</code> property. This property is | |
353 | * passed through to the Commons Validator.</p> | |
354 | * | |
355 | * @param newValue The new value for the <code>arg</code> property. | |
356 | */ | |
357 | public void setArg(String newValue) { | |
358 | 37 | vars.put(ARG_VARNAME, newValue); |
359 | 37 | } |
360 | ||
361 | ||
362 | /** | |
363 | * <p>Name of the <code>min</code> property in the <code>vars</code> Map.</p> | |
364 | */ | |
365 | public static final String MIN_VARNAME = "min"; | |
366 | ||
367 | /** | |
368 | * <p>The setter method for the <code>min</code> property. This property is | |
369 | * passed through to the Commons Validator.</p> | |
370 | * | |
371 | * @param newValue The new value for the <code>min</code> property. | |
372 | */ | |
373 | public void setMin(String newValue) { | |
374 | 6 | vars.put(MIN_VARNAME, newValue); |
375 | 6 | } |
376 | ||
377 | /** | |
378 | * <p>Name of the <code>max</code> property in the <code>vars</code> Map.</p> | |
379 | */ | |
380 | public static final String MAX_VARNAME = "max"; | |
381 | ||
382 | /** | |
383 | * <p>The setter method for the <code>max</code> property. This property is | |
384 | * passed through to the Commons Validator.</p> | |
385 | * | |
386 | * @param newValue The new value for the <code>max</code> property. | |
387 | */ | |
388 | public void setMax(String newValue) { | |
389 | 6 | vars.put(MAX_VARNAME, newValue); |
390 | 6 | } |
391 | ||
392 | ||
393 | /** | |
394 | * <p>Name of the <code>minLength</code> property in the <code>vars</code> Map.</p> | |
395 | */ | |
396 | public static final String MIN_LENGTH_VARNAME = "minlength"; | |
397 | ||
398 | /** | |
399 | * <p>The setter method for the <code>minlength</code> property. This property is | |
400 | * passed through to the Commons Validator.</p> | |
401 | * | |
402 | * @param newValue The new value for the <code>minlength</code> property. | |
403 | */ | |
404 | 2 | public void setMinLength(String newValue) { vars.put(MIN_LENGTH_VARNAME, newValue); } |
405 | ||
406 | ||
407 | /** | |
408 | * <p>Name of the <code>maxLength</code> property in the <code>vars</code> Map.</p> | |
409 | */ | |
410 | public static final String MAX_LENGTH_VARNAME = "maxlength"; | |
411 | ||
412 | /** | |
413 | * <p>The setter method for the <code>maxlength</code> property. This property is | |
414 | * passed through to the Commons Validator.</p> | |
415 | * | |
416 | * @param newValue The new value for the <code>maxlength</code> property. | |
417 | */ | |
418 | public void setMaxLength(String newValue) { | |
419 | 2 | vars.put(MAX_LENGTH_VARNAME, newValue); |
420 | 2 | } |
421 | ||
422 | /** | |
423 | * <p>Name of the <code>mask</code> property in the <code>vars</code> Map.</p> | |
424 | */ | |
425 | public static final String MASK_VARNAME = "mask"; | |
426 | ||
427 | /** | |
428 | * <p>The setter method for the <code>mask</code> property. This property is | |
429 | * passed through to the Commons Validator.</p> | |
430 | * | |
431 | * @param newValue The new value for the <code>mask</code> property. | |
432 | */ | |
433 | public void setMask(String newValue) { | |
434 | 2 | vars.put(MASK_VARNAME, newValue); |
435 | 2 | } |
436 | ||
437 | ||
438 | /** | |
439 | * <p>Name of the <code>datePatternStrict</code> in the <code>vars</code> Map.</p> | |
440 | */ | |
441 | public static final String DATE_PATTERN_STRICT_VARNAME = "datePatternStrict"; | |
442 | ||
443 | ||
444 | /** | |
445 | * <p>The setter method for the <code>datePatternStrict</code> property. This property is | |
446 | * passed through to the Commons Validator.</p> | |
447 | * | |
448 | * @param newValue The new value for the <code>datePatternStrict</code> property. | |
449 | */ | |
450 | public void setDatePatternStrict(String newValue) { | |
451 | 2 | vars.put(DATE_PATTERN_STRICT_VARNAME, newValue); |
452 | 2 | } |
453 | ||
454 | ||
455 | /** | |
456 | * <p>Return the validator resources that were configured and cached | |
457 | * at application startup time.</p> | |
458 | */ | |
459 | private static ValidatorResources getValidatorResources() { | |
460 | ||
461 | 364 | FacesContext context = FacesContext.getCurrentInstance(); |
462 | 364 | ExternalContext external = context.getExternalContext(); |
463 | 364 | Map applicationMap = external.getApplicationMap(); |
464 | 364 | return (ValidatorResources) applicationMap.get(Globals.VALIDATOR_RESOURCES); |
465 | ||
466 | } | |
467 | ||
468 | ||
469 | /** | |
470 | * <p>Name of the <code>message</code> property in the <code>vars</code> Map.</p> | |
471 | */ | |
472 | private static final String MESSAGE_ARG_NAME = "message"; | |
473 | ||
474 | /** | |
475 | * <p>Returns an array of values for message parameter replacement | |
476 | * arguments. The list and ordering is determined by a form | |
477 | * registered in the common validators XML. The form name | |
478 | * and the fields property is tied to the validation rule name | |
479 | * by convention. The the <code>arg</code> name attribute is | |
480 | * assumed to be "message" for message argument grouping.</p> | |
481 | * | |
482 | * @param ruleName name of the validation rule | |
483 | * @param localVars snapshot of EL vars captured at renderering time | |
484 | * and used by the script collector | |
485 | * @return array of objects used to fill the message | |
486 | */ | |
487 | protected Object[] getMessageArgs(String ruleName, Map localVars) { | |
488 | ||
489 | 48 | Tags tagUtils = new Tags(); |
490 | 48 | Arg[] templateArgs = getArgs(MESSAGE_ARG_NAME, ruleName); |
491 | 48 | assert templateArgs != null; |
492 | ||
493 | 48 | Object[] target = new Object[templateArgs.length]; |
494 | ||
495 | 179 | for (int i = 0; i < templateArgs.length; i++) { |
496 | 131 | Object value = vars.get(templateArgs[i].getKey()); |
497 | ||
498 | // look for a local var override | |
499 | 131 | if (localVars != null && localVars.containsKey(templateArgs[i].getKey())) { |
500 | value = localVars.get(templateArgs[i].getKey()); | |
501 | } else if (value != null && value instanceof String) { | |
502 | // if a String, check for a value binding expression | |
503 | 101 | value = tagUtils.eval((String) value); |
504 | } | |
505 | 131 | target[i] = value; |
506 | } | |
507 | ||
508 | 48 | return target; |
509 | } | |
510 | ||
511 | /** | |
512 | * <p>Returns an array of class types corresponding to the the | |
513 | * target validation rules method signature. The params are | |
514 | * configured by the <code>validator</code>'s <code>methodParams</code> | |
515 | * attribute.</p> | |
516 | * | |
517 | * @param validationAction the validators configuration bean populated from the XML file. | |
518 | * @return an array of class types for the formal parameter list. | |
519 | * @throws ClassNotFoundException validation rule class not found | |
520 | */ | |
521 | protected Class[] loadMethodParamClasses(ValidatorAction validationAction) throws ClassNotFoundException { | |
522 | ||
523 | 22 | List tmp = new ArrayList(); |
524 | 22 | StringTokenizer tokenizer = new StringTokenizer(validationAction.getMethodParams(), ","); |
525 | 55 | while (tokenizer.hasMoreTokens()) { |
526 | 33 | String token = tokenizer.nextToken().trim(); |
527 | 33 | if (token.length() > 0) { |
528 | 33 | tmp.add(token); |
529 | } | |
530 | 33 | } |
531 | ||
532 | 22 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
533 | 22 | if (classLoader == null) { |
534 | classLoader = this.getClass().getClassLoader(); | |
535 | } | |
536 | 22 | assert classLoader != null; |
537 | ||
538 | ||
539 | 22 | Class[] parameterClasses = new Class[tmp.size()]; |
540 | 55 | for (int i = 0; i < tmp.size(); i++) { |
541 | 33 | String className = (String) tmp.get(i); |
542 | 33 | if (standardTypes.containsKey(className)) { |
543 | 33 | parameterClasses[i] = (Class) standardTypes.get(className); |
544 | 33 | } else { |
545 | parameterClasses[i] = classLoader.loadClass(className); | |
546 | } | |
547 | } | |
548 | ||
549 | 22 | return parameterClasses; |
550 | } | |
551 | ||
552 | ||
553 | /** | |
554 | * <p>The args name used to define the parameter values for | |
555 | * a validation rule.</p> | |
556 | */ | |
557 | private static final String PARAMETER_ARG_NAME = "parameter"; | |
558 | ||
559 | /** | |
560 | * <p>Returns an array of parameter names in the target validator's | |
561 | * method. The parameter names are defined in the validators configuration | |
562 | * file under a form and field that correspond to the target rule. | |
563 | * The name attribute of the nested <code>arg</code> is assumed to be | |
564 | * "parameter". | |
565 | * | |
566 | * @param name the name of the target validation rule | |
567 | * @return array of formal parameter names | |
568 | */ | |
569 | public String[] getMethodParamNames(String name) { | |
570 | 22 | Arg[] templateArgs = getArgs(PARAMETER_ARG_NAME, name); |
571 | 22 | assert templateArgs != null; |
572 | ||
573 | 22 | String[] target = new String[templateArgs.length]; |
574 | ||
575 | 55 | for (int i = 0; i < templateArgs.length; i++) { |
576 | 33 | target[i] = templateArgs[i].getKey(); |
577 | } | |
578 | ||
579 | 22 | return target; |
580 | } | |
581 | ||
582 | private static final String FORM_NAME_PREFIX = "org.apache.shale.validator."; | |
583 | ||
584 | /** | |
585 | * <p>Returns validator <code>Arg</code> beans from the configuration file. | |
586 | * The <code>form</code> and <code>field</code> nodes that contain the target | |
587 | * arguments have a naming convention to a validation rule. This convention | |
588 | * was adopted for JSF validators because the <code>var</code>'s of the defining | |
589 | * validator is defined by the JSF validator object and not in the config file. | |
590 | * The JSF implementation doesn't have a concept of defining global form field | |
591 | * characteristics outside of the associated JSF component. But, we needed | |
592 | * a place to explicitly declare parameter names and message arguments.<p> | |
593 | * | |
594 | * @param name the name of the <code>arg</code> name attribute. | |
595 | * @param ruleName the name of the validator rule | |
596 | * @return an array of validator <code>Arg</code> beans. | |
597 | */ | |
598 | protected static Arg[] getArgs(String name, String ruleName) { | |
599 | ||
600 | 100 | StringBuffer formName = new StringBuffer(FORM_NAME_PREFIX); |
601 | 100 | formName.append(ruleName); |
602 | ||
603 | 100 | Form formDef = getValidatorResources().getForm(Locale.getDefault(), formName.toString()); |
604 | 100 | assert formDef != null; |
605 | ||
606 | 100 | Field field = formDef.getField(ruleName); |
607 | 100 | assert field != null; |
608 | ||
609 | 100 | Arg[] templateArgs = field.getArgs(name); |
610 | ||
611 | 100 | int max = -1; |
612 | 184 | for (int i = templateArgs.length - 1; i > -1; i--) { |
613 | 168 | if (templateArgs[i] != null) { |
614 | 84 | max = i; |
615 | 84 | break; |
616 | } | |
617 | } | |
618 | 100 | if (max == -1) { |
619 | 16 | return new Arg[0]; |
620 | 84 | } else if (max < templateArgs.length - 1) { |
621 | 36 | Arg[] tmp = new Arg[max + 1]; |
622 | 36 | System.arraycopy(templateArgs, 0, tmp, 0, max + 1); |
623 | 36 | templateArgs = tmp; |
624 | } | |
625 | 84 | return templateArgs; |
626 | } | |
627 | ||
628 | ||
629 | /** | |
630 | * <p>Returns the JavaScript type for a <code>var</code> collection | |
631 | * item. The default is <code>Var.JSTYPE_STRING</code>. The type | |
632 | * can be overridden by adding a <code>var</code> node to the | |
633 | * <code>field</code> node in the validator configuration file. | |
634 | * The <code>var-value</code> is ignored but a value is required | |
635 | * for well-formness. The <code>var-jstype</code> | |
636 | * and <code>var-name</code> are the only values used. | |
637 | * The <code>form</code> and the <code>field</code> the <code>var</code> | |
638 | * is nested under has an association with the <code>type</code>. | |
639 | * </p> | |
640 | * @param varName The name of the target variable | |
641 | * @return The JavaScript variable type ("string", "int", "regexp") | |
642 | */ | |
643 | public String getVarType(String varName) { | |
644 | 54 | StringBuffer formName = new StringBuffer(FORM_NAME_PREFIX); |
645 | 54 | formName.append(getType()); |
646 | ||
647 | 54 | Form formDef = getValidatorResources().getForm(Locale.getDefault(), formName.toString()); |
648 | 54 | assert formDef != null; |
649 | ||
650 | 54 | Field field = formDef.getField(getType()); |
651 | 54 | assert field != null; |
652 | ||
653 | 54 | String jsType = Var.JSTYPE_STRING; // default type |
654 | 54 | Var var = field.getVar(varName); |
655 | ||
656 | 54 | if (var != null && var.getJsType() != null) { |
657 | 2 | jsType = var.getJsType(); |
658 | } | |
659 | ||
660 | 54 | return jsType; |
661 | } | |
662 | ||
663 | ||
664 | /** | |
665 | * <p>Assumed argument name that captures the javascript | |
666 | * validation rule callback.</p> | |
667 | */ | |
668 | private static final String JSCALLBACK_ARG_NAME = "jscallback"; | |
669 | ||
670 | ||
671 | /** | |
672 | * <p>Returns a mnemonic used to build the commons validator | |
673 | * javascript call back. This method is invoked from the | |
674 | * {@link org.apache.shale.component.ValidatorScript} component. | |
675 | * The routine looks for an <code>arg</code> with a name of | |
676 | * <code>jscallback</code> under a form name and field | |
677 | * property corresponding to the rule. If there is not | |
678 | * a matching arg found, the <code>ruleName</code> is | |
679 | * returned as the default. | |
680 | * </p> | |
681 | * @param ruleName name of the target rule to invoke | |
682 | * @return code used to create the javacript call back function | |
683 | */ | |
684 | public static String getJsCallbackMnemonic(String ruleName) { | |
685 | 30 | Arg[] args = getArgs(JSCALLBACK_ARG_NAME, ruleName); |
686 | 30 | if (args == null || args.length == 0) { |
687 | 16 | return ruleName; |
688 | } else { | |
689 | 14 | return args[0].getKey(); |
690 | } | |
691 | } | |
692 | ||
693 | /** | |
694 | * <p>Loads an array of method parameter values corresponding to the | |
695 | * formal parameter of the target validator's method.<p> | |
696 | * @param context faces context | |
697 | * @param validatorAction <code>ValidatorAction</code> configuration bean. | |
698 | * @param methodParamClasses <code>Class[]</code> of the parameters of the target method. | |
699 | * @return An array of object valuse for each method parameter. | |
700 | */ | |
701 | protected Object[] loadMethodParamValues(FacesContext context, ValidatorAction validatorAction, Class[] methodParamClasses) { | |
702 | 22 | Tags tagUtils = new Tags(); |
703 | ||
704 | 22 | String[] paramNames = getMethodParamNames(validatorAction.getName()); |
705 | 22 | assert paramNames != null; |
706 | ||
707 | 22 | Object[] target = new Object[paramNames.length]; |
708 | 22 | assert paramNames.length == methodParamClasses.length; |
709 | ||
710 | 55 | for (int i = 0; i < paramNames.length; i++) { |
711 | 33 | Object obj = vars.get(paramNames[i]); |
712 | 33 | if (obj != null && obj instanceof String) { |
713 | 33 | obj = tagUtils.eval((String) obj); |
714 | } | |
715 | 33 | target[i] = convert(context, obj, methodParamClasses[i]); |
716 | } | |
717 | ||
718 | 22 | return target; |
719 | } | |
720 | ||
721 | ||
722 | /** | |
723 | * <p>Returns the Commons validator action that's appropriate | |
724 | * for the validator with the given <code>name</code>.</p> | |
725 | * | |
726 | * @param name The name of the validator | |
727 | * @return Validator rules config bean | |
728 | */ | |
729 | public static ValidatorAction getValidatorAction(String name) { | |
730 | 154 | return getValidatorResources().getValidatorAction(name); |
731 | } | |
732 | /** | |
733 | * <p>Returns the commons validator action associated with | |
734 | * the <code>type</code> attribute.</p> | |
735 | * | |
736 | * @return Validator rules config bean | |
737 | */ | |
738 | public ValidatorAction getValidatorAction() { | |
739 | 56 | return getValidatorResources().getValidatorAction(getType()); |
740 | } | |
741 | ||
742 | /** | |
743 | * <p>For a given commons validator rule, returns an array of | |
744 | * rule names that are dependent of the <code>name</code>. | |
745 | * Rule dependencies will be first in the returned array. | |
746 | * </p> | |
747 | * @param name target validator rule | |
748 | * @return array of all dependent rules for the target name | |
749 | */ | |
750 | protected String[] getDependencies(String name) { | |
751 | ||
752 | 19 | ValidatorAction action = getValidatorAction(name); |
753 | 19 | assert action != null; |
754 | ||
755 | 19 | List dependencies = action.getDependencyList(); |
756 | 19 | String[] types = new String[dependencies.size() + 1]; |
757 | ||
758 | 22 | for (int i = 0; i < dependencies.size(); i++) { |
759 | 3 | types[i] = (String) dependencies.get(i); |
760 | } | |
761 | 19 | types[types.length - 1] = name; |
762 | ||
763 | 19 | return types; |
764 | } | |
765 | ||
766 | /** | |
767 | * <p>Name used to store the component's submitted value in the | |
768 | * <code>vars</code> Map.</p> | |
769 | */ | |
770 | private static final String SUBMITTED_VALUE_VARNAME = "submittedValue"; | |
771 | ||
772 | /** | |
773 | * <p>This <code>validate</code> method is called by JSF to verify | |
774 | * the component to which the validator is attached.</p> | |
775 | * | |
776 | * @param context The faces context | |
777 | * @param component The component to validate | |
778 | * @param value the component's submitted value after the converter applied. | |
779 | */ | |
780 | public void validate(FacesContext context, UIComponent component, Object value) { | |
781 | ||
782 | 19 | if (Boolean.FALSE.equals(getServer())) { |
783 | return; | |
784 | } | |
785 | 19 | String[] types = getDependencies(getType()); |
786 | 23 | for (int j = 0; j < types.length; j++) { |
787 | 22 | ValidatorAction validatorAction = CommonsValidator.getValidatorAction(types[j]); |
788 | ||
789 | try { | |
790 | 22 | vars.put(SUBMITTED_VALUE_VARNAME, value); |
791 | 22 | Class validatorClass = loadValidatorClass(validatorAction); |
792 | 22 | Class[] paramClasses = this.loadMethodParamClasses(validatorAction); |
793 | 22 | Object[] paramValues = this.loadMethodParamValues(context, validatorAction, paramClasses); |
794 | 22 | Method validatorMethod = this.loadValidatorMethod(validatorAction, validatorClass, paramClasses); |
795 | 22 | Object validator = null; |
796 | 22 | if (!Modifier.isStatic(validatorMethod.getModifiers())) { |
797 | validator = validatorClass.newInstance(); | |
798 | } | |
799 | 22 | Boolean r = (Boolean) validatorMethod.invoke(validator, paramValues); |
800 | 22 | if (r.equals(Boolean.FALSE)) { |
801 | 18 | throw new ValidatorException(new FacesMessage( |
802 | FacesMessage.SEVERITY_ERROR, | |
803 | getErrorMessage(context, validatorAction, null), null)); | |
804 | } | |
805 | } catch (IllegalArgumentException e) { | |
806 | throw new RuntimeException(messages.getMessage("commonsValidator.intException", | |
807 | new Object[] {getType(), component.getId()}), e); | |
808 | } catch (ClassNotFoundException e) { | |
809 | throw new RuntimeException(messages.getMessage("commonsValidator.intException", | |
810 | new Object[] {getType(), component.getId()}), e); | |
811 | } catch (InstantiationException e) { | |
812 | throw new RuntimeException(messages.getMessage("commonsValidator.intException", | |
813 | new Object[] {getType(), component.getId()}), e); | |
814 | } catch (IllegalAccessException e) { | |
815 | throw new RuntimeException(messages.getMessage("commonsValidator.intException", | |
816 | new Object[] {getType(), component.getId()}), e); | |
817 | } catch (NoSuchMethodException e) { | |
818 | throw new RuntimeException(messages.getMessage("commonsValidator.intException", | |
819 | new Object[] {getType(), component.getId()}), e); | |
820 | } catch (InvocationTargetException e) { | |
821 | throw new RuntimeException(messages.getMessage("commonsValidator.intException", | |
822 | new Object[] {getType(), component.getId()}), e); | |
823 | } finally { | |
824 | 22 | vars.remove(SUBMITTED_VALUE_VARNAME); |
825 | 22 | } |
826 | ||
827 | } | |
828 | 1 | } |
829 | ||
830 | ||
831 | /** | |
832 | * <p>Loads the commons validator class containing the target rule.</p> | |
833 | * @param validatorAction the validator rules config bean | |
834 | * @return the class having the target validation method | |
835 | * | |
836 | * @throws ClassNotFoundException if the specified class cannot be found | |
837 | * @throws InstantiationException if a new instance cannot be instantiated | |
838 | * @throws IllegalAccessException if there is no public constructor | |
839 | */ | |
840 | protected Class loadValidatorClass(ValidatorAction validatorAction) | |
841 | throws ClassNotFoundException, InstantiationException, IllegalAccessException { | |
842 | ||
843 | 22 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
844 | 22 | if (classLoader == null) { |
845 | classLoader = this.getClass().getClassLoader(); | |
846 | } | |
847 | 22 | assert classLoader != null; |
848 | ||
849 | //find the target class having the validator rule | |
850 | 22 | Class validatorClass = classLoader.loadClass(validatorAction.getClassname()); |
851 | ||
852 | 22 | return validatorClass; |
853 | } | |
854 | ||
855 | /** | |
856 | * <p>Loads the <code>Method</code> of the <code>validatorClass</code> having | |
857 | * using definitions from the <code>validatorAction</code> bean. | |
858 | * </p> | |
859 | * @param validatorAction The config info bean of the target rule. | |
860 | * @param validatorClass The class having the validation method. | |
861 | * @param methodParamClasses The method formal parameter class signature. | |
862 | * @return target commons validator method to invoke. | |
863 | * @throws NoSuchMethodException if the specified method cannot be found | |
864 | */ | |
865 | protected Method loadValidatorMethod(ValidatorAction validatorAction, | |
866 | Class validatorClass, | |
867 | Class[] methodParamClasses) throws NoSuchMethodException { | |
868 | //find the method on the target validator rule class | |
869 | 22 | Method validatorMethod = validatorClass.getMethod(validatorAction.getMethod(), methodParamClasses); |
870 | 22 | return validatorMethod; |
871 | } | |
872 | ||
873 | ||
874 | /** | |
875 | * <p>Retrieves an error message, using the validator's message combined | |
876 | * with the errant value.</p> | |
877 | * @param context the all knowing faces conext object. | |
878 | * @param validatorAction config bean defining the commons validator rule. | |
879 | * @param localVars snapshot of EL variables at renderering time; used by client side | |
880 | * @return the message after parameter substitution. | |
881 | */ | |
882 | public String getErrorMessage(FacesContext context, ValidatorAction validatorAction, Map localVars) { | |
883 | 48 | final String DEFAULT_BUNDLE_NAME = "org.apache.shale.validator.messages"; |
884 | ||
885 | 48 | Locale locale = context.getViewRoot().getLocale(); |
886 | 48 | String msg = getMessage(); |
887 | 48 | if (msg == null) { |
888 | 47 | String msgkey = validatorAction.getMsg(); |
889 | 47 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
890 | 47 | if (loader == null) { loader = getClass().getClassLoader(); } |
891 | 47 | Application app = context.getApplication(); |
892 | 47 | String appBundleName = app.getMessageBundle(); |
893 | 47 | if (appBundleName != null) { |
894 | ResourceBundle bundle | |
895 | = ResourceBundle.getBundle(appBundleName, locale, loader); | |
896 | if (bundle != null) { | |
897 | try { | |
898 | msg = bundle.getString(msgkey); | |
899 | } catch (MissingResourceException ex) { | |
900 | ; // Ignore this | |
901 | } | |
902 | } | |
903 | } | |
904 | 47 | if (msg == null) { |
905 | 47 | ResourceBundle bundle |
906 | = ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale, loader); | |
907 | 47 | if (bundle != null) { |
908 | try { | |
909 | 47 | msg = bundle.getString(msgkey); |
910 | } catch (MissingResourceException ex) { | |
911 | log.error(messages.getMessage("commonsValidator.msgerror", | |
912 | new Object[] {msgkey}), ex); | |
913 | throw ex; | |
914 | 47 | } |
915 | } | |
916 | } | |
917 | } | |
918 | 48 | Object[] params = getMessageArgs(validatorAction.getName(), localVars); |
919 | 48 | msg = new MessageFormat(msg, locale).format(params); |
920 | 48 | return msg; |
921 | } | |
922 | ||
923 | ||
924 | // ----------------------------------------------------- Static Initialization | |
925 | ||
926 | /** | |
927 | * <p>A utility method that converts an object to an instance | |
928 | * of a given class, such as converting <code>"true"</code> | |
929 | * for example, into <code>Boolean.TRUE</code>.</p> | |
930 | * <p>If the component passed to this method is an instance of | |
931 | * <code>EditableValueHolder</code> and the object's class is | |
932 | * <code>String</code>, this method returns the component's | |
933 | * submitted value, without converting it to a string. The | |
934 | * <code>component</code> parameter can be <code>null</code>. | |
935 | * </p> | |
936 | * | |
937 | * @param context faces context | |
938 | * @param obj The object to convert | |
939 | * @param cl The type of object to convert to | |
940 | * @return target object type | |
941 | */ | |
942 | private static Object convert(FacesContext context, Object obj, Class cl) { | |
943 | ||
944 | 33 | ConverterHelper converterHelper = new ConverterHelper(); |
945 | // correct target type | |
946 | 33 | if (cl.isInstance(obj)) { |
947 | 22 | return obj; |
948 | } | |
949 | // target type is String | |
950 | 11 | if (cl == String.class) { |
951 | if (obj instanceof String) { | |
952 | return obj; | |
953 | } else { | |
954 | return converterHelper.asString(context, obj.getClass(), obj); | |
955 | } | |
956 | } | |
957 | ||
958 | 11 | if (obj instanceof String) { |
959 | // String to object | |
960 | 11 | return converterHelper.asObject(context, cl, (String) obj); |
961 | } else { | |
962 | //Object to String | |
963 | String source = converterHelper.asString(context, obj.getClass(), obj); | |
964 | // String to Object | |
965 | return converterHelper.asObject(context, cl, source); | |
966 | } | |
967 | ||
968 | } | |
969 | ||
970 | ||
971 | /** | |
972 | * <p>A utility method that returns <code>true</code> if | |
973 | * the supplied string has a length greater than zero. | |
974 | * </p> | |
975 | * | |
976 | * @param str The string | |
977 | * @return <code>true</code> if not an empty String | |
978 | */ | |
979 | // these two methods are referenced in validator-utils.xml | |
980 | public static boolean isSupplied(String str) { | |
981 | 1 | return str.trim().length() > 0; |
982 | } | |
983 | ||
984 | ||
985 | /** | |
986 | * <p>A utility method that returns <code>true</code> if | |
987 | * the supplied string represents a date.</p> | |
988 | * | |
989 | * @param d The string representation of the date. | |
990 | * @param datePatternStrict Commons validator property | |
991 | * @return <code>true</code> if <code>d</code> is a date | |
992 | */ | |
993 | public static boolean isDate(String d, String datePatternStrict) { | |
994 | 1 | return GenericValidator.isDate(d, datePatternStrict, true); |
995 | } | |
996 | ||
997 | ||
998 | // ----------------------------------------------------- Static Initialization | |
999 | ||
1000 | ||
1001 | /** | |
1002 | * <p>Standard types for conversions</p> | |
1003 | */ | |
1004 | static { | |
1005 | 1 | standardTypes = new HashMap(); |
1006 | 1 | standardTypes.put("boolean", boolean.class); |
1007 | 1 | standardTypes.put("byte", byte.class); |
1008 | 1 | standardTypes.put("char", char.class); |
1009 | 1 | standardTypes.put("double", double.class); |
1010 | 1 | standardTypes.put("float", float.class); |
1011 | 1 | standardTypes.put("int", int.class); |
1012 | 1 | standardTypes.put("long", long.class); |
1013 | 1 | standardTypes.put("short", short.class); |
1014 | 1 | standardTypes.put("java.lang.String", String.class); |
1015 | 1 | } |
1016 | ||
1017 | } |