Revision: 455
          http://svn.sourceforge.net/stripes/?rev=455&view=rev
Author:   tfenne
Date:     2006-10-27 04:32:19 -0700 (Fri, 27 Oct 2006)

Log Message:
-----------
Working towards STS-240 and STS-260.  Validation metadata is now stored 
internally in a standalone, non-annotation class, that can be manufactured 
dynamically.  Still need to add interface methods to allow this to be exposed 
and manipulated dynamically.

Modified Paths:
--------------
    
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java
    trunk/stripes/src/net/sourceforge/stripes/controller/StripesConstants.java

Added Paths:
-----------
    trunk/stripes/src/net/sourceforge/stripes/validation/ValidationMetadata.java

Modified: 
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java
   2006-10-26 01:11:45 UTC (rev 454)
+++ 
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java
   2006-10-27 11:32:19 UTC (rev 455)
@@ -20,22 +20,9 @@
 import net.sourceforge.stripes.action.Wizard;
 import net.sourceforge.stripes.config.Configuration;
 import net.sourceforge.stripes.exception.StripesRuntimeException;
-import net.sourceforge.stripes.util.CryptoUtil;
-import net.sourceforge.stripes.util.HtmlUtil;
-import net.sourceforge.stripes.util.Log;
-import net.sourceforge.stripes.util.ReflectUtil;
-import net.sourceforge.stripes.util.CollectionUtil;
-import net.sourceforge.stripes.util.bean.ExpressionException;
-import net.sourceforge.stripes.util.bean.NoSuchPropertyException;
-import net.sourceforge.stripes.util.bean.PropertyExpression;
-import net.sourceforge.stripes.util.bean.PropertyExpressionEvaluation;
-import net.sourceforge.stripes.util.bean.BeanUtil;
-import net.sourceforge.stripes.validation.ScopedLocalizableError;
-import net.sourceforge.stripes.validation.TypeConverter;
-import net.sourceforge.stripes.validation.Validate;
-import net.sourceforge.stripes.validation.ValidateNestedProperties;
-import net.sourceforge.stripes.validation.ValidationError;
-import net.sourceforge.stripes.validation.ValidationErrors;
+import net.sourceforge.stripes.util.*;
+import net.sourceforge.stripes.util.bean.*;
+import net.sourceforge.stripes.validation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.jsp.PageContext;
@@ -43,25 +30,9 @@
 import javax.servlet.jsp.el.Expression;
 import javax.servlet.jsp.el.ExpressionEvaluator;
 import javax.servlet.jsp.el.VariableResolver;
-import java.lang.reflect.Array;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Type;
+import java.lang.reflect.*;
 import java.security.GeneralSecurityException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.regex.Pattern;
+import java.util.*;
 
 /**
  * <p>Implementation of the ActionBeanPropertyBinder interface that uses 
Stripes' built in
@@ -78,28 +49,14 @@
  * @since Stripes 1.4
  */
 public class DefaultActionBeanPropertyBinder implements 
ActionBeanPropertyBinder {
-    private static Log log = 
Log.getInstance(DefaultActionBeanPropertyBinder.class);
-    private static Set<String> SPECIAL_KEYS = new HashSet<String>();
+    private static final Log log = 
Log.getInstance(DefaultActionBeanPropertyBinder.class);
 
-    static {
-        SPECIAL_KEYS.add(StripesConstants.URL_KEY_SOURCE_PAGE);
-        SPECIAL_KEYS.add(StripesConstants.URL_KEY_FIELDS_PRESENT);
-        SPECIAL_KEYS.add(StripesConstants.URL_KEY_FLASH_SCOPE_ID);
-    }
-
     /** Map of validation annotations that is built at startup. */
-    private Map<Class<? extends ActionBean>, Map<String,Validate>> validations;
+    private Map<Class<? extends ActionBean>, Map<String, ValidationMetadata>> 
validations;
 
-    /**
-     * Map of validation annotations to the set of events they should be run 
on. Note that
-     * the events may be prepended with "!", so watch out!
-     */
-    private Map<Validate,Set<String>> validationEventMap;
-
     /** Configuration instance passed in at initialization time. */
     private Configuration configuration;
 
-
     /**
      * Looks up and caches in a useful form the metadata necessary to perform 
validations as
      * properties are bound to the bean.
@@ -107,40 +64,22 @@
     public void init(Configuration configuration) throws Exception {
         this.configuration = configuration;
         Set<Class<? extends ActionBean>> beanClasses = 
ActionClassCache.getInstance().getActionBeanClasses();
-        this.validations = new HashMap<Class<? extends ActionBean>, 
Map<String,Validate>>();
-        this.validationEventMap = new HashMap<Validate,Set<String>>();
+        this.validations = new HashMap<Class<? extends ActionBean>, 
Map<String,ValidationMetadata>>();
 
         for (Class<? extends ActionBean> beanClass : beanClasses) {
-            Map<String, Validate> fieldValidations = new HashMap<String, 
Validate>();
+            Map<String, ValidationMetadata> fieldValidations = new 
HashMap<String, ValidationMetadata>();
             processClassAnnotations(beanClass, fieldValidations);
             this.validations.put(beanClass, fieldValidations);
 
-            // Go through and put the list of events for this validation into
-            // a quicker to access structure
-            for (Validate info : fieldValidations.values()) {
-                Set<String> events = null;
-                if (info.on().length == 0) {
-                    events = Collections.emptySet();
-                }
-                else {
-                    events = new HashSet<String>();
-                    for (String event : info.on()) {
-                        events.add(event);
-                    }
-                }
-
-                this.validationEventMap.put(info, events);
-            }
-
             // Print out a pretty debug message showing what validations got 
configured
             StringBuilder builder = new StringBuilder(128);
-            for (Map.Entry<String,Validate> entry : 
fieldValidations.entrySet()) {
+            for (Map.Entry<String,ValidationMetadata> entry : 
fieldValidations.entrySet()) {
                 if (builder.length() > 0) {
                     builder.append(", ");
                 }
                 builder.append(entry.getKey());
                 builder.append("->");
-                builder.append(ReflectUtil.toString(entry.getValue()));
+                builder.append(entry.getValue());
             }
 
             log.debug("Loaded validations for ActionBean ", 
beanClass.getSimpleName(), ": ",
@@ -154,9 +93,9 @@
      * inheritance hierarchy take precedence over those higher up.
      *
      * @param clazz the ActionBean subclasses (or parent thereof) in question
-     * @param fieldValidations a map of fieldname->Validate in which to store 
validations
+     * @param fieldValidations a map of fieldname->ValidationMetadata in which 
to store validations
      */
-    protected void processClassAnnotations(Class clazz, Map<String,Validate> 
fieldValidations) {
+    protected void processClassAnnotations(Class clazz, 
Map<String,ValidationMetadata> fieldValidations) {
         Class superclass = clazz.getSuperclass();
         if (superclass != null) {
             processClassAnnotations(superclass, fieldValidations);
@@ -169,52 +108,51 @@
                 continue; // only public methods!
             }
 
-            Validate validation = method.getAnnotation(Validate.class);
-            if (validation != null) {
-                String fieldName = getPropertyName(method.getName());
-                fieldValidations.put(fieldName, validation);
-            }
-
-            ValidateNestedProperties nested = 
method.getAnnotation(ValidateNestedProperties.class);
-            if (nested != null) {
-                String fieldName = getPropertyName(method.getName());
-                Validate[] validations = nested.value();
-                for (Validate nestedValidate : validations) {
-                    if ( "".equals(nestedValidate.field()) ) {
-                        log.warn("Nested validation used without field name: 
", validation);
-                    }
-                    else {
-                        fieldValidations.put(fieldName + "." + 
nestedValidate.field(),
-                                             nestedValidate);
-                    }
-                }
-            }
+            processPropertyAnnotations(getPropertyName(method.getName()),
+                                       method.getAnnotation(Validate.class),
+                                       
method.getAnnotation(ValidateNestedProperties.class),
+                                       fieldValidations);
         }
 
         // Process the fields for validation annotations
         Field[] fields = clazz.getDeclaredFields();
         for (Field field : fields) {
-            Validate validation = field.getAnnotation(Validate.class);
-            if (validation != null) {
-                fieldValidations.put(field.getName(), validation);
-            }
+            processPropertyAnnotations(field.getName(),
+                                        field.getAnnotation(Validate.class),
+                                        
field.getAnnotation(ValidateNestedProperties.class),
+                                        fieldValidations);
+        }
+    }
 
-            ValidateNestedProperties nested = 
field.getAnnotation(ValidateNestedProperties.class);
-            if (nested != null) {
-                Validate[] validations = nested.value();
-                for (Validate nestedValidate : validations) {
-                    if ( "".equals(nestedValidate.field()) ) {
-                        log.warn("Nested validation used without field name: 
", validation);
-                    }
-                    else {
-                        fieldValidations.put(field.getName() + "." + 
nestedValidate.field(),
-                                             nestedValidate);
-                    }
+    /**
+     * Helper method that takes the name of a property on the ActionBean along 
with the
+     * property's (potentially null) Validate and ValidateNestedProperties 
annotations
+     * and stores the information in to the internval cache of validation 
information.
+     */
+    protected void processPropertyAnnotations(String name, Validate validate,
+                                              ValidateNestedProperties nested,
+                                              Map<String,ValidationMetadata> 
fieldValidations) {
+        if (validate != null) {
+            fieldValidations.put(name, new ValidationMetadata(name, validate));
+        }
+
+        if (nested != null) {
+            Validate[] validations = nested.value();
+            for (Validate nestedValidate : validations) {
+                if ( "".equals(nestedValidate.field()) ) {
+                    log.warn("Nested validation on field ", name, " used 
without nested field name: ",
+                            ReflectUtil.toString(nestedValidate));
                 }
+                else {
+                    String nestedName = name + "." + nestedValidate.field();
+                    fieldValidations.put(nestedName, new 
ValidationMetadata(nestedName, nestedValidate));
+                }
             }
         }
+
     }
 
+
     /**
      * <p>Loops through the parameters contained in the request and attempts 
to bind each one to the
      * supplied ActionBean.  Invokes validation for each of the properties on 
the bean before
@@ -252,12 +190,13 @@
             try {
                 String pname = name.getName(); // exact name of the param in 
the request
 
-                if (!SPECIAL_KEYS.contains(pname) && 
!pname.equals(context.getEventName())
-                        && !fieldErrors.containsKey(pname)) {
+                if (!StripesConstants.SPECIAL_URL_KEYS.contains(pname)
+                        && !pname.equals(context.getEventName()) && 
!fieldErrors.containsKey(pname)) {
                     log.trace("Running binding for property with name: ", 
name);
 
                     // Determine the target type
-                    Validate validationInfo = 
this.validations.get(bean.getClass()).get(name.getStrippedName());
+                    ValidationMetadata validationInfo =
+                            
this.validations.get(bean.getClass()).get(name.getStrippedName());
                     PropertyExpressionEvaluation eval =
                             new 
PropertyExpressionEvaluation(PropertyExpression.getExpression(pname), bean);
                     Class type = eval.getType();
@@ -553,7 +492,10 @@
      * three characters and makes the next character lower case.
      */
     protected String getPropertyName(String methodName) {
-        return methodName.substring(3,4).toLowerCase() + 
methodName.substring(4);
+        if (methodName.length() > 3)
+            return methodName.substring(3,4).toLowerCase() + 
methodName.substring(4);
+        else
+            return "";
     }
 
 
@@ -577,21 +519,21 @@
             }
         }
 
-        Map<String,Validate> validationInfos = 
this.validations.get(bean.getClass());
+        Map<String,ValidationMetadata> validationInfos = 
this.validations.get(bean.getClass());
         StripesRequestWrapper req = 
StripesRequestWrapper.findStripesWrapper(bean.getContext().getRequest());
 
         if (validationInfos != null) {
             boolean wizard = bean.getClass().getAnnotation(Wizard.class) != 
null;
             Collection<String> fieldsOnPage = getFieldsPresentInfo(bean);
 
-            for (Map.Entry<String,Validate> entry : 
validationInfos.entrySet()) {
+            for (Map.Entry<String,ValidationMetadata> entry : 
validationInfos.entrySet()) {
                 String propertyName = entry.getKey();
-                Validate validationInfo = entry.getValue();
+                ValidationMetadata validationInfo = entry.getValue();
 
                 // If the field is required, and we don't have index params 
that collapse
                 // to that property name, check that it was supplied
-                if (validationInfo.required() && 
!indexedParams.contains(propertyName)
-                        && applies(validationInfo, bean.getContext())) {
+                if (validationInfo.requiredOn(bean.getContext().getEventName())
+                        && !indexedParams.contains(propertyName)) {
 
                     // Make the added check that if the form is a wizard, the 
required field is
                     // in the set of fields that were on the page
@@ -628,12 +570,10 @@
                     for (Map.Entry<ParameterName,String[]> entry : 
row.entrySet()) {
                         ParameterName name = entry.getKey();
                         String[] values = entry.getValue();
-                        Validate validationInfo = 
validationInfos.get(name.getStrippedName());
+                        ValidationMetadata validationInfo = 
validationInfos.get(name.getStrippedName());
 
-                        if (validationInfo != null && validationInfo.required()
-                                && applies(validationInfo, bean.getContext())) 
{
-                            checkSingleRequiredField
-                                    (name.getName(), name.getStrippedName(), 
values, req, errors);
+                        if (validationInfo != null && 
validationInfo.requiredOn(bean.getContext().getEventName())) {
+                            checkSingleRequiredField(name.getName(), 
name.getStrippedName(), values, req, errors);
                         }
                     }
                 }
@@ -703,18 +643,18 @@
      *
      * @param propertyName the name of the property being validated (used for 
constructing errors)
      * @param values the String[] of values from the request being validated
-     * @param validationInfo the Valiate annotation that was decorating the 
property being validated
+     * @param validationInfo the ValidationMetadata for the property being 
validated
      * @param errors a collection of errors to be populated with any 
validation errors discovered
      */
     protected void doPreConversionValidations(ParameterName propertyName,
                                               String[] values,
-                                              Validate validationInfo,
+                                              ValidationMetadata 
validationInfo,
                                               List<ValidationError> errors) {
 
         for (String value : values) {
             // Only run validations when there are non-empty values
             if (value != null && value.length() > 0) {
-                if (validationInfo.minlength() != -1 && value.length() < 
validationInfo.minlength()) {
+                if (validationInfo.minlength() != null && value.length() < 
validationInfo.minlength()) {
                     ValidationError error =
                             new ScopedLocalizableError ("validation.minlength",
                                                         "valueTooShort",
@@ -724,7 +664,7 @@
                     errors.add( error );
                 }
 
-                if (validationInfo.maxlength() != -1 && value.length() > 
validationInfo.maxlength()) {
+                if (validationInfo.maxlength() != null && value.length() > 
validationInfo.maxlength()) {
                     ValidationError error =
                             new ScopedLocalizableError("validation.maxlength",
                                                        "valueTooLong",
@@ -733,8 +673,7 @@
                     errors.add( error );
                 }
 
-                if ( validationInfo.mask().length() > 0 &&
-                        
!Pattern.compile(validationInfo.mask()).matcher(value).matches() ) {
+                if ( validationInfo.mask() != null && 
!validationInfo.mask().matcher(value).matches() ) {
 
                     ValidationError error =
                             new ScopedLocalizableError("validation.mask",
@@ -764,7 +703,8 @@
             // Sort out what we need to validate this field
             ParameterName name = entry.getKey();
             List<Object> values = entry.getValue();
-            Validate validationInfo = 
this.validations.get(bean.getClass()).get(name.getStrippedName());
+            ValidationMetadata validationInfo =
+                    
this.validations.get(bean.getClass()).get(name.getStrippedName());
 
             if (values.size() == 0 || validationInfo == null) {
                 continue;
@@ -776,7 +716,7 @@
                 if (value instanceof Number) {
                     Number number = (Number) value;
 
-                    if (validationInfo.minvalue() != Double.MIN_VALUE &&
+                    if (validationInfo.minvalue() != null &&
                             number.doubleValue() < validationInfo.minvalue() ) 
{
                         ValidationError error = new 
ScopedLocalizableError("validation.minvalue",
                                                                            
"valueBelowMinimum",
@@ -785,7 +725,7 @@
                         errors.add(name.getName(), error);
                     }
 
-                    if (validationInfo.maxvalue() != Double.MAX_VALUE &&
+                    if (validationInfo.maxvalue() != null &&
                             number.doubleValue() > validationInfo.maxvalue() ) 
{
                         ValidationError error = new 
ScopedLocalizableError("validation.maxvalue",
                                                                            
"valueAboveMaximum",
@@ -810,19 +750,19 @@
      * @param bean the ActionBean who's property is being validated
      * @param name the name of the property being validated
      * @param values the non-null post-conversion values for the property
-     * @param validationInfo the validation annotation for the property
+     * @param validationInfo the validation metadata for the property
      * @param errors the validation errors object to add errors to
      */
     protected void doExpressionValidation(ActionBean bean,
                                           ParameterName name,
                                           List<Object> values,
-                                          Validate validationInfo,
+                                          ValidationMetadata validationInfo,
                                           ValidationErrors errors) {
         // If a validation expression was supplied, see if we can process it!
         Expression expr = null;
         DelegatingVariableResolver resolver = null;
 
-        if ( !"".equals(validationInfo.expression()) ) {
+        if (validationInfo.expression() != null) {
             final PageContext context = DispatcherHelper.getPageContext();
 
             if (context == null) {
@@ -835,10 +775,6 @@
                 try {
                     // If this turns out to be slow we could probably cache 
the parsed expression
                     String expression = validationInfo.expression();
-                    if (!expression.startsWith("${")) {
-                        expression = "${" + expression + "}";
-                    }
-
                     ExpressionEvaluator evaluator = 
context.getExpressionEvaluator();
                     expr = evaluator.parseExpression(expression, 
Boolean.class, null);
                     resolver = new DelegatingVariableResolver(bean, 
context.getVariableResolver());
@@ -875,29 +811,6 @@
     }
 
     /**
-     * Determines whether or not a specific validation applies to the current 
event or not.
-     *
-     * @param info the ValidationInfo being looked at
-     * @param context the current ActionBeanContext
-     * @return true if the Validation should be executed, false otherwise
-     */
-    protected boolean applies(Validate info, ActionBeanContext context) {
-        Set<String> events = this.validationEventMap.get(info);
-        String current = context.getEventName();
-
-        if (info.on().length == 0 || current == null) {
-            return true;
-        }
-
-        if (info.on()[0].startsWith("!")) {
-            return !events.contains("!" + current);
-        }
-        else {
-            return events.contains(current);
-        }
-    }
-
-    /**
      * <p>Converts the String[] of values for a given parameter in the 
HttpServletRequest into the
      * desired type of Object.  If a converter is declared using an annotation 
for the property
      * (or getter/setter) then that converter will be used - if it does not 
convert to the right
@@ -916,7 +829,7 @@
      * @param values a String array of values to attempt conversion of
      * @param errors a List into which ValidationError objects will be 
populated for any errors
      *        discovered during conversion.
-     * @param validationInfo the @Validate annotation for the property if one 
exists
+     * @param validationInfo the validation metadata for the property if 
defined
      * @return List<Object> a List of objects containing only objects of the 
desired type. It is
      *         not guaranteed to be the same length as the values array passed 
in.
      */
@@ -924,14 +837,14 @@
                                  ParameterName propertyName,
                                  String[] values,
                                  Class propertyType,
-                                 Validate validationInfo,
+                                 ValidationMetadata validationInfo,
                                  List<ValidationError> errors) throws 
Exception {
 
         List<Object> returns = new ArrayList<Object>();
 
         // Dig up the type converter
         TypeConverter converter = null;
-        if (validationInfo !=  null && validationInfo.converter() != 
TypeConverter.class) {
+        if (validationInfo !=  null && validationInfo.converter() != null) {
             converter = this.configuration.getTypeConverterFactory()
                     .getInstance(validationInfo.converter(), 
bean.getContext().getRequest().getLocale());
         }

Modified: 
trunk/stripes/src/net/sourceforge/stripes/controller/StripesConstants.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/controller/StripesConstants.java  
2006-10-26 01:11:45 UTC (rev 454)
+++ trunk/stripes/src/net/sourceforge/stripes/controller/StripesConstants.java  
2006-10-27 11:32:19 UTC (rev 455)
@@ -14,6 +14,11 @@
  */
 package net.sourceforge.stripes.controller;
 
+import net.sourceforge.stripes.util.Literal;
+
+import java.util.Set;
+import java.util.Collections;
+
 /**
  * Container for constant values that are used across more than one class in 
Stripes.
  *
@@ -39,6 +44,22 @@
     String URL_KEY_EVENT_NAME = "_eventName";
 
     /**
+     * The name of a URL parameter that is used to tell Stripes that a flash 
scope exists
+     * for the current request.
+     */
+    String URL_KEY_FLASH_SCOPE_ID = "__fsk";
+
+    /**
+     * An immutable set of URL keys or request parameters that have special 
meaning to Stripes and
+     * as a result should not be referenced in binding, validation or other 
other places that
+     * work on the full set of request parameters.
+     */
+    Set<String> SPECIAL_URL_KEYS = Collections.unmodifiableSet(
+            Literal.set(StripesConstants.URL_KEY_SOURCE_PAGE,
+                        StripesConstants.URL_KEY_FIELDS_PRESENT,
+                        StripesConstants.URL_KEY_FLASH_SCOPE_ID,
+                        StripesConstants.URL_KEY_EVENT_NAME));
+    /**
      * The name under which the ActionBean for a request is stored as a 
request attribute before
      * forwarding to the JSP.
      */
@@ -64,12 +85,6 @@
     String REQ_ATTR_TAG_STACK = "__stripes_tag_stack";
 
     /**
-     * The name of a URL parameter that is used to tell Stripes that a flash 
scope exists
-     * for the current request.
-     */
-    String URL_KEY_FLASH_SCOPE_ID = "__fsk";
-
-    /**
      * The name of a request parameter that holds a Map of flash scopes keyed 
by the
      * hashcode of the request that generated them.
      */

Added: 
trunk/stripes/src/net/sourceforge/stripes/validation/ValidationMetadata.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/validation/ValidationMetadata.java    
                            (rev 0)
+++ 
trunk/stripes/src/net/sourceforge/stripes/validation/ValidationMetadata.java    
    2006-10-27 11:32:19 UTC (rev 455)
@@ -0,0 +1,216 @@
+/* Copyright 2005-2006 Tim Fennell
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.sourceforge.stripes.validation;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.regex.Pattern;
+
+/**
+ * <p>Encapsulates the validation metadata for a single property of a single 
class. Structure
+ * is purposely very similar to the @Validate annotation. This class is used 
internally
+ * to store and manipulate validation metadata, the source of which is often 
validation
+ * annotations.</p>
+ *
+ * <p>However, since this class is not an annotation it has the added benefits 
of being
+ * able to contain behaviour, being subclassable, and of being able to be 
instantiated at
+ * runtime - i.e. it can contain non-static validation information.</p>
+ *
+ * @author Tim Fennell
+ * @since Stripes 1.5
+ */
+public class ValidationMetadata {
+    String property;
+    boolean required;
+    private Set<String> on;
+    private boolean onIsPositive;
+    private boolean ignore;
+    private Integer minlength, maxlength;
+    private Double minvalue, maxvalue;
+    private Pattern mask;
+    private String expression;
+    Class<? extends TypeConverter> converter;
+
+    /**
+     * Constructs a ValidationMetadata object for the specified property. 
Further constraints
+     * can be specified by calling individual methods, e.g.
+     * [EMAIL PROTECTED]  new 
ValidationMetadata("username").minlength(5).maxlength(10);}
+     *
+     * @param property the name of the property to be validated. If the 
property is a nested
+     *        property then the fully path should be included, e.g. [EMAIL 
PROTECTED] user.address.city}
+     */
+    public ValidationMetadata(String property) {
+        this.property = property;
+    }
+
+    /**
+     * Essentially a copy constructor that constructs a ValidationMetadata 
object from
+     * an @Validate annotation declared on a property.
+     *
+     * @param validate
+     */
+    public ValidationMetadata(String property, Validate validate) {
+        // Copy over all the simple values
+        this.property = property;
+        required(validate.required());
+        ignore(ignore());
+        if (validate.minlength() != -1) minlength(validate.minlength());
+        if (validate.maxlength() != -1) maxlength(validate.maxlength());
+        if (validate.minvalue() != Double.MIN_VALUE) 
minvalue(validate.minvalue());
+        if (validate.maxvalue() != Double.MAX_VALUE) 
maxvalue(validate.maxvalue());
+        if (!"".equals(validate.mask())) mask(validate.mask());
+        if (validate.converter() != TypeConverter.class) 
converter(validate.converter());
+        if (!"".equals(validate.expression())) 
expression(validate.expression());
+        if (validate.on().length > 0) on(validate.on());
+    }
+
+    /** Returns the name of the property this validation metadata represents. 
*/
+    public String getProperty() {
+        return this.property;
+    }
+
+    /** Sets the required-ness of this field. True = required, false = not 
required. */
+    public ValidationMetadata required(boolean required) {
+        this.required = required;
+        return this;
+    }
+
+    /** Returns true if the field in question is required. */
+    public boolean required() { return this.required; }
+
+    /** Returns true if the field is required when processing the specified 
event. */
+    public boolean requiredOn(String event) {
+        return this.required && (
+                (this.on == null) ||
+                (this.onIsPositive && this.on.contains(event)) ||
+                (!this.onIsPositive && !this.on.contains(event))
+            );
+    }
+
+    /** Sets whether or not this field should be ignored during binding and 
validation. */
+    public ValidationMetadata ignore(boolean ignore) {
+        this.ignore = ignore;
+        return this;
+    }
+
+    /** Returns true if this field should be ignored in binding and 
validation. */
+    public boolean ignore() { return this.ignore; }
+
+    /** Sets the minimum acceptable length for property values. */
+    public ValidationMetadata minlength(Integer minlength) {
+        this.minlength = minlength;
+        return this;
+    }
+
+    /** Returns the minimum acceptable length for values, or null if there is 
none. */
+    public Integer minlength() { return this.minlength; }
+
+    /** Sets the maximum acceptable length for property values. */
+    public ValidationMetadata maxlength(Integer maxlength) {
+        this.maxlength = maxlength;
+        return this;
+    }
+
+    /** Returns the maximum acceptable length for values, or null if there is 
none. */
+    public Integer maxlength() { return this.maxlength; }
+
+    /** Sets the minimum acceptable <b>value</b> for numeric property values. 
*/
+    public ValidationMetadata minvalue(Double minvalue) {
+        this.minvalue = minvalue;
+        return this;
+    }
+
+    /** Returns the minimum acceptable value for numeric properties, or null 
if there is none. */
+    public Double minvalue() { return this.minvalue; }
+
+    /** Sets the maximum acceptable <b>value</b> for numeric property values. 
*/
+    public ValidationMetadata maxvalue(Double maxvalue) {
+        this.maxvalue = maxvalue;
+        return this;
+    }
+
+    /** Returns the maximum acceptable value for numeric properties, or null 
if there is none. */
+    public Double maxvalue() { return this.maxvalue; }
+
+    /** Sets the mask which the String form of the property must match. */
+    public ValidationMetadata mask(String mask) {
+        this.mask = Pattern.compile(mask);
+        return this;
+    }
+
+    /** Returns the mask Pattern property values must match, or null if there 
is none. */
+    public Pattern mask() { return this.mask; }
+
+    /** Sets the overridden TypeConveter to use to convert values. */
+    public ValidationMetadata converter(Class<? extends TypeConverter> 
converter) {
+        this.converter = converter;
+        return this;
+    }
+
+    /** Returns the overridden TypeConverter if there is one, or null. */
+    public Class<? extends TypeConverter> converter() { return this.converter; 
}
+
+    /** Sets the expression that should be used to validate values. */
+    public ValidationMetadata expression(String expression) {
+        this.expression = expression;
+        if (!this.expression.startsWith("${")) this.expression = "${" + 
this.expression + "}";
+        return this;
+    }
+
+    /** Returns the overridden TypeConverter if there is one, or null. */
+    public String expression() { return this.expression; }
+
+    /** Sets the set of events for which the field in question is required, if 
it is at all. */
+    public ValidationMetadata on(String... on) {
+        if (on.length == 0) {
+            this.on = null;
+        }
+        else {
+            this.on = new HashSet<String>();
+            this.onIsPositive = !(on[0].charAt(0) == '!');
+            for (String s : on) {
+                if (this.onIsPositive) {
+                    this.on.add(s);
+                }
+                else {
+                    this.on.add(s.substring(1));
+                }
+            }
+        }
+
+        return this;
+    }
+
+    /**
+     * Overidden toString() that only outputs the constraints that are 
specified by
+     * the instance of validation metadata (i.e. omits nulls, defaults etc.)
+     *
+     * @return a human readable string form of the metadata
+     */
+    public String toString() {
+        return "ValidationMetadata{" +
+                (required ? "required=" + required : "") +
+                (ignore   ? ", ignore=" + ignore : "" ) +
+                (minlength != null ? ", minlength=" + minlength : "") +
+                (maxlength != null ? ", maxlength=" + maxlength : "") +
+                (minvalue != null ? ", minvalue=" + minvalue : "") +
+                (maxvalue != null ? ", maxvalue=" + maxvalue : "") +
+                (mask != null ? ", mask=" + mask : "" ) +
+                (expression != null ? ", expression='" + expression + '\'' : 
"") +
+                (converter != null ? ", converter=" + 
converter.getSimpleName() : "") +
+                '}';
+    }
+}
+


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development

Reply via email to