2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

Coverage Report - org.apache.shale.validator.CommonsValidator
 
Classes in this File Line Coverage Branch Coverage Complexity
CommonsValidator
100%
187/187
N/A
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  
  *    &lt;global&gt;
 78  
  *      &lt;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=""/&gt;
 86  
  *    &lt;/global&gt;
 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  
  *   &lt;formset&gt;
 96  
  *     &lt;form name="org.apache.shale.validator.mask"&gt;
 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  
  *           &lt;field property="mask"&gt;
 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  
  *               &lt;arg position="0" name="message" key="arg" resource="false"/&gt;
 113  
  *               &lt;arg position="1" name="message" key="mask" resource="false"/&gt;
 114  
  *               &lt;arg position="2" name="message" key="submittedValue" resource="false"/&gt;
 115  
  *
 116  
  *               &lt;arg position="0" name="parameter" key="submittedValue" resource="false"/&gt;
 117  
  *               &lt;arg position="1" name="parameter" key="mask" resource="false"/&gt;
 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  
  *               &lt;var&gt;
 141  
  *                  &lt;var-name&gt;mask&lt;/var-name&gt;
 142  
  *                  &lt;var-value&gt;&lt;/var-value&gt;
 143  
  *                  &lt;var-jstype&gt;regexp&lt;/var-jstype&gt;
 144  
  *               &lt;/var&gt;
 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  
 }