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

For more information, please explore the Attic.

Coverage Report - org.apache.shale.validator.util.ShaleValidatorAction
 
Classes in this File Line Coverage Branch Coverage Complexity
ShaleValidatorAction
100%
68/68
N/A
3.778
 
 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.util;
 19  
 
 20  
 import java.lang.reflect.Method;
 21  
 import java.lang.reflect.Modifier;
 22  
 import java.util.ArrayList;
 23  
 import java.util.HashMap;
 24  
 import java.util.List;
 25  
 import java.util.Locale;
 26  
 import java.util.Map;
 27  
 import org.apache.commons.validator.Arg;
 28  
 import org.apache.commons.validator.Field;
 29  
 import org.apache.commons.validator.Form;
 30  
 import org.apache.commons.validator.ValidatorAction;
 31  
 import org.apache.commons.validator.ValidatorResources;
 32  
 
 33  
 /**
 34  
  * <p>Custom wrapper around a Commons Validator <code>ValidatorAction</code>
 35  
  * that precalculates as many of the introspective lookup operations as
 36  
  * possible.  This ensures that runtime operation of the validator checks
 37  
  * can proceed as quickly as possible.</p>
 38  
  */
 39  
 public final class ShaleValidatorAction {
 40  
     
 41  
 
 42  
     // ------------------------------------------------------------ Constructors
 43  
 
 44  
 
 45  
     /**
 46  
      * <p>Create a new instance of ShaleValidatorAction that wraps the
 47  
      * specified <code>ValidatorAction</code>.</p>
 48  
      *
 49  
      * @param resources <code>ValidatorResources</code> for this application
 50  
      * @param action The <code>ValidatorAction</code> to be wrapped
 51  
      *
 52  
      * @exception IllegalArgumentException if configuration data is missing
 53  
      *  or incorrect
 54  
      */
 55  
     public ShaleValidatorAction(ValidatorResources resources,
 56  1623
                                 ValidatorAction action) {
 57  
 
 58  1623
         this.resources = resources;
 59  1623
         this.action = action;
 60  1623
         precalculate();
 61  
 
 62  1623
     }
 63  
     
 64  
 
 65  
     // -------------------------------------------------------- Static Variables
 66  
 
 67  
 
 68  
     /**
 69  
      * <p>The prefix for form names that correspond to validator types.</p>
 70  
      */
 71  
     private static final String FORM_NAME_PREFIX = "org.apache.shale.validator.";
 72  
 
 73  
 
 74  
     /**
 75  
      * <p>Map of the classes for primitive Java types, keyed by the Java
 76  
      * keywords for the corresponding primitive.</p>
 77  
      */
 78  1
     private static final Map PRIMITIVE_TYPES = new HashMap();
 79  
     static {
 80  1
         PRIMITIVE_TYPES.put("boolean", boolean.class);
 81  1
         PRIMITIVE_TYPES.put("byte", byte.class);
 82  1
         PRIMITIVE_TYPES.put("char", char.class);
 83  1
         PRIMITIVE_TYPES.put("double", double.class);
 84  1
         PRIMITIVE_TYPES.put("float", float.class);
 85  1
         PRIMITIVE_TYPES.put("int", int.class);
 86  1
         PRIMITIVE_TYPES.put("long", long.class);
 87  1
         PRIMITIVE_TYPES.put("short", short.class);
 88  1
     }
 89  
 
 90  
 
 91  
     // ------------------------------------------------------ Instance Variables
 92  
 
 93  
 
 94  
     /**
 95  
      * <p>The <code>ValidatorAction</code> that we are wrapping.</p>
 96  
      */
 97  1623
     private ValidatorAction action = null;
 98  
 
 99  
 
 100  
     /**
 101  
      * <p>The class containing the validation method to be called for this action.</p>
 102  
      */
 103  1623
     private Class clazz = null;
 104  
 
 105  
 
 106  
     /**
 107  
      * <p>The field definition containing the mappings for parameter and
 108  
      * message subtitution for this validator type.</p>
 109  
      */
 110  1623
     private Field field = null;
 111  
 
 112  
 
 113  
     /**
 114  
      * <p>The form definition corresponding to this validator type that is used
 115  
      * for mapping parameter and message arguments.</p>
 116  
      */
 117  1623
     private Form form = null;
 118  
 
 119  
 
 120  
     /**
 121  
      * <p>Return an instance of the specified validation class, if the requested
 122  
      * validation method is not static.</p>
 123  
      */
 124  1623
     private Object instance = null;
 125  
 
 126  
 
 127  
     /**
 128  
      * <p>Array of message arguments defining replacement parameters for error
 129  
      * messages related to this validator.</p>
 130  
      */
 131  1623
     private Arg[] messageArgs = null;
 132  
 
 133  
 
 134  
     /**
 135  
      * <p>The validation <code>Method</code> to be called for this action.</p>
 136  
      */
 137  1623
     private Method method = null;
 138  
 
 139  
 
 140  
     /**
 141  
      * <p>Array of parameter arguments defining values to be sent in to
 142  
      * the validator method for this validator.</p>
 143  
      */
 144  1623
     private Arg[] parameterArgs = null;
 145  
 
 146  
 
 147  
     /**
 148  
      * <p>The <code>ValidatorResources</code> for this application.</p>
 149  
      */
 150  1623
     private ValidatorResources resources = null;
 151  
 
 152  
 
 153  
     /**
 154  
      * <p>The parameter signature for the validation method to be called
 155  
      * for this action.</p>
 156  
      */
 157  1623
     private Class[] signature = null;
 158  
 
 159  
 
 160  
     // -------------------------------------------------------------- Properties
 161  
 
 162  
 
 163  
     /**
 164  
      * <p>Return the <code>ValidatorAction</code> instance we are wrapping.</p>
 165  
      */
 166  
     public ValidatorAction getAction() {
 167  1623
         return this.action;
 168  
     }
 169  
 
 170  
 
 171  
     /**
 172  
      * <p>Return an instance of the requested validator class, if the requested
 173  
      * validation method is not static.  If the method is static, return
 174  
      * <code>null</code> instead.</p>
 175  
      */
 176  
     public Object getInstance() {
 177  
         return this.instance;
 178  
     }
 179  
 
 180  
 
 181  
     /**
 182  
      * <p>Return an array of argument metadata describing subtitution values
 183  
      * for error messages emitted by this validator type.</p>
 184  
      */
 185  
     public Arg[] getMessageArgs() {
 186  
         return this.messageArgs;
 187  
     }
 188  
 
 189  
 
 190  
     /**
 191  
      * <p>Return the lookup key for the error message template to be used
 192  
      * if this validation fails.</p>
 193  
      */
 194  
     public String getMessageKey() {
 195  
         return this.action.getMsg();
 196  
     }
 197  
 
 198  
 
 199  
     /**
 200  
      * <p>Return the validation <code>Method</code> to be called for this
 201  
      * action.</p>
 202  
      */
 203  
     public Method getMethod() {
 204  
         return this.method;
 205  
     }
 206  
 
 207  
 
 208  
     /**
 209  
      * <p>Return an array of argument metadata describing the parameter values
 210  
      * to be sent to the validation method for this validator type.</p>
 211  
      */
 212  
     public Arg[] getParameterArgs() {
 213  
         return this.parameterArgs;
 214  
     }
 215  
 
 216  
 
 217  
     /**
 218  
      * <p>Return the parameter signature for the validation method to be called
 219  
      * for this action.</p>
 220  
      */
 221  
     public Class[] getSignature() {
 222  
         return this.signature;
 223  
     }
 224  
 
 225  
 
 226  
     // --------------------------------------------------------- Private Methods
 227  
 
 228  
 
 229  
     /**
 230  
      * <p>Precalculate the values to be returned by our public properties.</p>
 231  
      *
 232  
      * @exception IllegalArgumentException if configuration data is missing
 233  
      *  or incorrect
 234  
      */
 235  
     private void precalculate() {
 236  
 
 237  1623
         List list = null;
 238  1623
         String value = null;
 239  1623
         String values = null;
 240  
 
 241  
         // Acquire a reference to the class loader we will use
 242  1623
         ClassLoader loader = Thread.currentThread().getContextClassLoader();
 243  1623
         if (loader == null) {
 244  
             loader = this.getClass().getClassLoader();
 245  
         }
 246  
 
 247  
         // Look up the class that contains the validation method
 248  
         // we will be calling
 249  
         try {
 250  1623
             this.clazz = loader.loadClass(action.getClassname());
 251  
         } catch (ClassNotFoundException e) {
 252  
             throw new IllegalArgumentException("ClassNotFoundException:"
 253  
                 + " Cannot load class '" + action.getClassname()
 254  
                 + "' specified by ValidatorAction with name '"
 255  
                 + action.getName() + "'");
 256  1623
         }
 257  
 
 258  
         // Calculate the method parameter signature of the validation
 259  
         // method we will be calling
 260  1623
         list = new ArrayList();
 261  1623
         values = action.getMethodParams().trim();
 262  4197
         while (values.length() > 0) {
 263  
             // Identify the class name of the next class to be loaded
 264  2574
             int comma = values.indexOf(',');
 265  2574
             if (comma >= 0) {
 266  951
                 value = values.substring(0, comma).trim();
 267  951
                 values = values.substring(comma + 1);
 268  951
             } else {
 269  1623
                 value = values.trim();
 270  1623
                 values = "";
 271  
             }
 272  2574
             if (value.length() == 0) {
 273  
                 break;
 274  
             }
 275  
             // Add the corresponding class instance to our list
 276  2574
             Class clazz = (Class) PRIMITIVE_TYPES.get(value);
 277  2574
             if (clazz == null) {
 278  
                 try {
 279  1551
                     clazz = loader.loadClass(value);
 280  
                 } catch (ClassNotFoundException e) {
 281  
                     throw new IllegalArgumentException("ClassNotFoundException:"
 282  
                         + " Cannot load method parameter class '" + value
 283  
                         + "' specified by ValidatorAction with name '"
 284  
                         + action.getName() + "'");
 285  1551
                 }
 286  
             }
 287  2574
             list.add(clazz);
 288  2574
         }
 289  1623
         this.signature = (Class[]) list.toArray(new Class[list.size()]);
 290  
 
 291  
         // Look up the validation method we will be calling
 292  
         try {
 293  1623
             this.method = this.clazz.getMethod(action.getMethod(), this.signature);
 294  
         } catch (NoSuchMethodException e) {
 295  
             throw new IllegalArgumentException("NoSuchMethodException:"
 296  
                 + " Method named '" + action.getMethod() + "' with parameters '"
 297  
                 + action.getMethodParams() + "' for class '" + action.getClassname()
 298  
                 + "' not found. Specified by ValidatorAction with name '"
 299  
                 + action.getName() + "'");
 300  1623
         }
 301  
 
 302  
         // Create an instance of the validator class if we need one
 303  
         // (which is true only if the validation method is not static
 304  1623
         if (!Modifier.isStatic(this.method.getModifiers())) {
 305  
             try {
 306  
                 this.instance = clazz.newInstance();
 307  
             } catch (IllegalAccessException e) {
 308  
                 throw new IllegalArgumentException("IllegalAccessException:"
 309  
                     + " Not allowed to instantiate class '" + action.getClassname()
 310  
                     + "' specified by ValidatorAction with name '" + action.getName()
 311  
                     + "' (validation method with name '" + action.getMethod()
 312  
                     + "' is not static)");
 313  
             } catch (InstantiationException e) {
 314  
                 throw new IllegalArgumentException("InstantiationException:"
 315  
                     + " Could not instantiate class '" + action.getClassname()
 316  
                     + "' specified by ValidatorAction with name '" + action.getName()
 317  
                     + "' (validation method with name '" + action.getMethod()
 318  
                     + "' is not static)");
 319  
             }
 320  
         }
 321  
 
 322  
         // Cache the form definition associated with this validator action type
 323  1623
         this.form = resources.getForm(Locale.getDefault(),
 324  
                                       FORM_NAME_PREFIX + action.getName());
 325  1623
         if (this.form == null) {
 326  
             throw new IllegalArgumentException(FORM_NAME_PREFIX + action.getName());
 327  
         }
 328  
 
 329  
         // Look up the predefined "fields" from our form definition
 330  
         // and cache interesting information
 331  1623
         this.field = this.form.getField(action.getName());
 332  1623
         if (this.field == null) {
 333  
             throw new IllegalArgumentException("Field " + action.getName());
 334  
         }
 335  1623
         this.messageArgs = field.getArgs("message");
 336  1623
         if (this.messageArgs == null) {
 337  
             this.messageArgs = new Arg[0];
 338  
         }
 339  1623
         this.parameterArgs = field.getArgs("parameter");
 340  1623
         if (this.parameterArgs == null) {
 341  
             this.parameterArgs = new Arg[0];
 342  
         }
 343  
 
 344  
         // FIXME - For some reason, Commons Validator is returning a trailing
 345  
         // null Arg in the parameterArgs array sometimes, so strip it off
 346  
         // if present
 347  1623
         if ((this.parameterArgs.length > 0)
 348  
          && (this.parameterArgs[this.parameterArgs.length - 1] == null)) {
 349  1623
             Arg[] results = new Arg[this.parameterArgs.length - 1];
 350  1623
             System.arraycopy(this.parameterArgs, 0, results, 0, results.length);
 351  1623
             this.parameterArgs = results;
 352  
         }
 353  
 
 354  
         // For robustness, validate the length of the parameter arguments
 355  
         // and parameter signature arrays, which should be identical
 356  1623
         if (this.parameterArgs.length != this.signature.length) {
 357  
             throw new IllegalArgumentException(this.action.getName()
 358  
               + ": signature defines " + this.signature.length
 359  
               + " elements but " + this.parameterArgs.length
 360  
               + " parameter arguments are specified");
 361  
         }
 362  
 
 363  1623
     }
 364  
 
 365  
 
 366  
 }