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