2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 public class CommonsValidator implements Validator, Serializable {
151
152
153
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 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 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 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 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 this.formName = formName;
202 }
203
204
205
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 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 return vars;
249 }
250
251
252
253
254
255
256
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 type = newValue;
267 }
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 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 client = newValue;
289 }
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 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 server = newValue;
311 }
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 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 message = newValue;
333 }
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 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 vars.put(ARG_VARNAME, newValue);
359 }
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 vars.put(MIN_VARNAME, newValue);
375 }
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 vars.put(MAX_VARNAME, newValue);
390 }
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 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 vars.put(MAX_LENGTH_VARNAME, newValue);
420 }
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 vars.put(MASK_VARNAME, newValue);
435 }
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 vars.put(DATE_PATTERN_STRICT_VARNAME, newValue);
452 }
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 FacesContext context = FacesContext.getCurrentInstance();
462 ExternalContext external = context.getExternalContext();
463 Map applicationMap = external.getApplicationMap();
464 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 Tags tagUtils = new Tags();
490 Arg[] templateArgs = getArgs(MESSAGE_ARG_NAME, ruleName);
491 assert templateArgs != null;
492
493 Object[] target = new Object[templateArgs.length];
494
495 for (int i = 0; i < templateArgs.length; i++) {
496 Object value = vars.get(templateArgs[i].getKey());
497
498
499 if (localVars != null && localVars.containsKey(templateArgs[i].getKey())) {
500 value = localVars.get(templateArgs[i].getKey());
501 } else if (value != null && value instanceof String) {
502
503 value = tagUtils.eval((String) value);
504 }
505 target[i] = value;
506 }
507
508 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 List tmp = new ArrayList();
524 StringTokenizer tokenizer = new StringTokenizer(validationAction.getMethodParams(), ",");
525 while (tokenizer.hasMoreTokens()) {
526 String token = tokenizer.nextToken().trim();
527 if (token.length() > 0) {
528 tmp.add(token);
529 }
530 }
531
532 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
533 if (classLoader == null) {
534 classLoader = this.getClass().getClassLoader();
535 }
536 assert classLoader != null;
537
538
539 Class[] parameterClasses = new Class[tmp.size()];
540 for (int i = 0; i < tmp.size(); i++) {
541 String className = (String) tmp.get(i);
542 if (standardTypes.containsKey(className)) {
543 parameterClasses[i] = (Class) standardTypes.get(className);
544 } else {
545 parameterClasses[i] = classLoader.loadClass(className);
546 }
547 }
548
549 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 Arg[] templateArgs = getArgs(PARAMETER_ARG_NAME, name);
571 assert templateArgs != null;
572
573 String[] target = new String[templateArgs.length];
574
575 for (int i = 0; i < templateArgs.length; i++) {
576 target[i] = templateArgs[i].getKey();
577 }
578
579 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 StringBuffer formName = new StringBuffer(FORM_NAME_PREFIX);
601 formName.append(ruleName);
602
603 Form formDef = getValidatorResources().getForm(Locale.getDefault(), formName.toString());
604 assert formDef != null;
605
606 Field field = formDef.getField(ruleName);
607 assert field != null;
608
609 Arg[] templateArgs = field.getArgs(name);
610
611 int max = -1;
612 for (int i = templateArgs.length - 1; i > -1; i--) {
613 if (templateArgs[i] != null) {
614 max = i;
615 break;
616 }
617 }
618 if (max == -1) {
619 return new Arg[0];
620 } else if (max < templateArgs.length - 1) {
621 Arg[] tmp = new Arg[max + 1];
622 System.arraycopy(templateArgs, 0, tmp, 0, max + 1);
623 templateArgs = tmp;
624 }
625 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 StringBuffer formName = new StringBuffer(FORM_NAME_PREFIX);
645 formName.append(getType());
646
647 Form formDef = getValidatorResources().getForm(Locale.getDefault(), formName.toString());
648 assert formDef != null;
649
650 Field field = formDef.getField(getType());
651 assert field != null;
652
653 String jsType = Var.JSTYPE_STRING;
654 Var var = field.getVar(varName);
655
656 if (var != null && var.getJsType() != null) {
657 jsType = var.getJsType();
658 }
659
660 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 Arg[] args = getArgs(JSCALLBACK_ARG_NAME, ruleName);
686 if (args == null || args.length == 0) {
687 return ruleName;
688 } else {
689 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 Tags tagUtils = new Tags();
703
704 String[] paramNames = getMethodParamNames(validatorAction.getName());
705 assert paramNames != null;
706
707 Object[] target = new Object[paramNames.length];
708 assert paramNames.length == methodParamClasses.length;
709
710 for (int i = 0; i < paramNames.length; i++) {
711 Object obj = vars.get(paramNames[i]);
712 if (obj != null && obj instanceof String) {
713 obj = tagUtils.eval((String) obj);
714 }
715 target[i] = convert(context, obj, methodParamClasses[i]);
716 }
717
718 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 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 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 ValidatorAction action = getValidatorAction(name);
753 assert action != null;
754
755 List dependencies = action.getDependencyList();
756 String[] types = new String[dependencies.size() + 1];
757
758 for (int i = 0; i < dependencies.size(); i++) {
759 types[i] = (String) dependencies.get(i);
760 }
761 types[types.length - 1] = name;
762
763 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 if (Boolean.FALSE.equals(getServer())) {
783 return;
784 }
785 String[] types = getDependencies(getType());
786 for (int j = 0; j < types.length; j++) {
787 ValidatorAction validatorAction = CommonsValidator.getValidatorAction(types[j]);
788
789 try {
790 vars.put(SUBMITTED_VALUE_VARNAME, value);
791 Class validatorClass = loadValidatorClass(validatorAction);
792 Class[] paramClasses = this.loadMethodParamClasses(validatorAction);
793 Object[] paramValues = this.loadMethodParamValues(context, validatorAction, paramClasses);
794 Method validatorMethod = this.loadValidatorMethod(validatorAction, validatorClass, paramClasses);
795 Object validator = null;
796 if (!Modifier.isStatic(validatorMethod.getModifiers())) {
797 validator = validatorClass.newInstance();
798 }
799 Boolean r = (Boolean) validatorMethod.invoke(validator, paramValues);
800 if (r.equals(Boolean.FALSE)) {
801 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 vars.remove(SUBMITTED_VALUE_VARNAME);
825 }
826
827 }
828 }
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 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
844 if (classLoader == null) {
845 classLoader = this.getClass().getClassLoader();
846 }
847 assert classLoader != null;
848
849
850 Class validatorClass = classLoader.loadClass(validatorAction.getClassname());
851
852 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
869 Method validatorMethod = validatorClass.getMethod(validatorAction.getMethod(), methodParamClasses);
870 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 final String DEFAULT_BUNDLE_NAME = "org.apache.shale.validator.messages";
884
885 Locale locale = context.getViewRoot().getLocale();
886 String msg = getMessage();
887 if (msg == null) {
888 String msgkey = validatorAction.getMsg();
889 ClassLoader loader = Thread.currentThread().getContextClassLoader();
890 if (loader == null) { loader = getClass().getClassLoader(); }
891 Application app = context.getApplication();
892 String appBundleName = app.getMessageBundle();
893 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 ;
901 }
902 }
903 }
904 if (msg == null) {
905 ResourceBundle bundle
906 = ResourceBundle.getBundle(DEFAULT_BUNDLE_NAME, locale, loader);
907 if (bundle != null) {
908 try {
909 msg = bundle.getString(msgkey);
910 } catch (MissingResourceException ex) {
911 log.error(messages.getMessage("commonsValidator.msgerror",
912 new Object[] {msgkey}), ex);
913 throw ex;
914 }
915 }
916 }
917 }
918 Object[] params = getMessageArgs(validatorAction.getName(), localVars);
919 msg = new MessageFormat(msg, locale).format(params);
920 return msg;
921 }
922
923
924
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 ConverterHelper converterHelper = new ConverterHelper();
945
946 if (cl.isInstance(obj)) {
947 return obj;
948 }
949
950 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 if (obj instanceof String) {
959
960 return converterHelper.asObject(context, cl, (String) obj);
961 } else {
962
963 String source = converterHelper.asString(context, obj.getClass(), obj);
964
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
980 public static boolean isSupplied(String str) {
981 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 return GenericValidator.isDate(d, datePatternStrict, true);
995 }
996
997
998
999
1000
1001 /***
1002 * <p>Standard types for conversions</p>
1003 */
1004 static {
1005 standardTypes = new HashMap();
1006 standardTypes.put("boolean", boolean.class);
1007 standardTypes.put("byte", byte.class);
1008 standardTypes.put("char", char.class);
1009 standardTypes.put("double", double.class);
1010 standardTypes.put("float", float.class);
1011 standardTypes.put("int", int.class);
1012 standardTypes.put("long", long.class);
1013 standardTypes.put("short", short.class);
1014 standardTypes.put("java.lang.String", String.class);
1015 }
1016
1017 }