Commits

Anonymous committed b6b15da

XW-71 Field Errors need to be able to hold more than one error per field - field errors now holds Lists
XW-58 Create VisitorFieldValidator - VisitorFieldValidator allows sub-objects to be validated from the action using its own validations (as defined in the *-validation.xml files)
I also refactored LocaleAware and ValidationAware and created support classes for these, which the BaseActionSupport delegate to.

git-svn-id: http://svn.opensymphony.com/svn/xwork/trunk/src@9e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (27)

java/com/opensymphony/xwork/BaseActionSupport.java

  */
 package com.opensymphony.xwork;
 
-import com.opensymphony.xwork.util.LocalizedTextUtil;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import java.io.Serializable;
 
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 import java.util.ResourceBundle;
  * Time: 3:45:29 PM
  * To change this template use Options | File Templates.
  */
-public class BaseActionSupport implements Action, ValidationAware, LocaleAware, Serializable {
+public class BaseActionSupport implements Action, Serializable, ValidationAware, LocaleAware {
     //~ Static fields/initializers /////////////////////////////////////////////
 
     protected transient static final Log LOG = LogFactory.getLog(ActionSupport.class);
 
     //~ Instance fields ////////////////////////////////////////////////////////
 
-    private Collection actionErrors;
-    private Map fieldErrors;
+    private final LocaleAware localeAware = new LocaleAwareSupport(getClass());
+    private final ValidationAware validationAware = new ValidationAwareSupport();
 
     //~ Methods ////////////////////////////////////////////////////////////////
 
     public void setActionErrors(Collection errorMessages) {
-        this.actionErrors = errorMessages;
+        validationAware.setActionErrors(errorMessages);
     }
 
     public Collection getActionErrors() {
-        if (actionErrors == null) {
-            actionErrors = new ArrayList();
-        }
-
-        return actionErrors;
+        return validationAware.getActionErrors();
     }
 
     public void setFieldErrors(Map errorMap) {
-        this.fieldErrors = errorMap;
+        validationAware.setFieldErrors(errorMap);
     }
 
     public Map getFieldErrors() {
-        if (fieldErrors == null) {
-            fieldErrors = new HashMap();
-        }
-
-        return fieldErrors;
+        return validationAware.getFieldErrors();
     }
 
-    /**
-    * Get the locale for this action.
-    *
-    * Applications may customize how locale is chosen by
-    * subclassing ActionSupport and override this methodName.
-    *
-    * @return     the locale to use
-    */
     public Locale getLocale() {
-        return ActionContext.getContext().getLocale();
+        return localeAware.getLocale();
     }
 
-    /**
-    * Get a text from the resource bundles associated with this action.
-    * The resource bundles are searched, starting with the one associated
-    * with this particular action, and testing all its superclasses' bundles.
-    * It will stop once a bundle is found that contains the given text. This gives
-    * a cascading style that allow global texts to be defined for an application base
-    * class.
-    *
-    * @param   aTextName  name of text to be found
-    * @return     value of named text
-    */
     public String getText(String aTextName) {
-        Class thisClass = getClass();
+        return localeAware.getText(aTextName);
+    }
 
-        return LocalizedTextUtil.findText(thisClass, aTextName);
+    public String getText(String aTextName, String defaultValue) {
+        return localeAware.getText(aTextName, defaultValue);
     }
 
-    /**
-    * Get the named bundle.
-    *
-    * You can override the getLocale() methodName to change the behaviour of how
-    * to choose locale for the bundles that are returned. Typically you would
-    * use the LocaleAware interface to get the users configured locale, or use
-    * your own methodName to allow the user to select the locale and store it in
-    * the session (by using the SessionAware interface).
-    *
-    * @param   aBundleName  bundle name
-    * @return     a resource bundle
-    */
     public ResourceBundle getTexts(String aBundleName) {
-        return LocalizedTextUtil.findResourceBundle(aBundleName, getLocale());
+        return localeAware.getTexts(aBundleName);
     }
 
-    /**
-    * Get the resource bundle associated with this action.
-    * This will be based on the actual subclass that is used.
-    *
-    * @return     resouce bundle
-    */
     public ResourceBundle getTexts() {
-        return getTexts(getClass().getName());
+        return localeAware.getTexts();
     }
 
     public void addActionError(String anErrorMessage) {
-        getActionErrors().add(anErrorMessage);
+        validationAware.addActionError(anErrorMessage);
     }
 
     public void addFieldError(String fieldName, String errorMessage) {
-        getFieldErrors().put(fieldName, errorMessage);
+        validationAware.addFieldError(fieldName, errorMessage);
     }
 
     public String execute() throws Exception {
     }
 
     public boolean hasActionErrors() {
-        return (actionErrors != null) && !actionErrors.isEmpty();
+        return validationAware.hasActionErrors();
     }
 
-    /**
-    * Note that this does not have the same meaning as in WW 1.x
-    * @return (hasActionErrors() || hasFieldErrors())
-    */
     public boolean hasErrors() {
-        return (hasActionErrors() || hasFieldErrors());
+        return validationAware.hasErrors();
     }
 
     public boolean hasFieldErrors() {
-        return (fieldErrors != null) && !fieldErrors.isEmpty();
+        return validationAware.hasFieldErrors();
     }
 }

java/com/opensymphony/xwork/DefaultActionProxy.java

 
         // this will set up a new ActionContext on the ThreadLocal
         invocation = ActionProxyFactory.getFactory().createActionInvocation(this, extraContext);
+        ActionContext.getContext().setName(actionName);
     }
 }

java/com/opensymphony/xwork/LocaleAware.java

     String getText(String aTextName);
 
     /**
+     * Get a text from the resource bundles associated with this action.
+     * The resource bundles are searched, starting with the one associated
+     * with this particular action, and testing all its superclasses' bundles.
+     * It will stop once a bundle is found that contains the given text. This gives
+     * a cascading style that allow global texts to be defined for an application base
+     * class. If no text is found for this text name, the default value is returned.
+     *
+     * @param   aTextName  name of text to be found
+     * @param   defaultValue the default value which will be returned if no text is found
+     * @return     value of named text
+     */
+    String getText(String aTextName, String defaultValue);
+
+    /**
     * Get the named bundle.
     *
     * You can override the getLocale() methodName to change the behaviour of how

java/com/opensymphony/xwork/LocaleAwareSupport.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork;
+
+import com.opensymphony.xwork.util.LocalizedTextUtil;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+
+/**
+ * LocaleAwareSupport
+ * @author Jason Carreira
+ * Created Aug 3, 2003 12:21:12 AM
+ */
+public class LocaleAwareSupport implements LocaleAware {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    private Class clazz;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public LocaleAwareSupport(Class clazz) {
+        this.clazz = clazz;
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    /**
+    * Get the locale for this action.
+    *
+    * Applications may customize how locale is chosen by
+    * subclassing ActionSupport and override this methodName.
+    *
+    * @return     the locale to use
+    */
+    public Locale getLocale() {
+        return ActionContext.getContext().getLocale();
+    }
+
+    /**
+    * Get a text from the resource bundles associated with this action.
+    * The resource bundles are searched, starting with the one associated
+    * with this particular action, and testing all its superclasses' bundles.
+    * It will stop once a bundle is found that contains the given text. This gives
+    * a cascading style that allow global texts to be defined for an application base
+    * class.
+    *
+    * @param   aTextName  name of text to be found
+    * @return     value of named text
+    */
+    public String getText(String aTextName) {
+        return LocalizedTextUtil.findText(clazz, aTextName);
+    }
+
+    /**
+     * Get a text from the resource bundles associated with this action.
+     * The resource bundles are searched, starting with the one associated
+     * with this particular action, and testing all its superclasses' bundles.
+     * It will stop once a bundle is found that contains the given text. This gives
+     * a cascading style that allow global texts to be defined for an application base
+     * class. If no text is found for this text name, the default value is returned.
+     *
+     * @param   aTextName  name of text to be found
+     * @param   defaultValue the default value which will be returned if no text is found
+     * @return     value of named text
+     */
+    public String getText(String aTextName, String defaultValue) {
+        return LocalizedTextUtil.findText(clazz, aTextName, getLocale(), defaultValue);
+    }
+
+    /**
+    * Get the named bundle.
+    *
+    * You can override the getLocale() methodName to change the behaviour of how
+    * to choose locale for the bundles that are returned. Typically you would
+    * use the LocaleAware interface to get the users configured locale, or use
+    * your own methodName to allow the user to select the locale and store it in
+    * the session (by using the SessionAware interface).
+    *
+    * @param   aBundleName  bundle name
+    * @return     a resource bundle
+    */
+    public ResourceBundle getTexts(String aBundleName) {
+        return LocalizedTextUtil.findResourceBundle(aBundleName, getLocale());
+    }
+
+    /**
+    * Get the resource bundle associated with this action.
+    * This will be based on the actual subclass that is used.
+    *
+    * @return     resouce bundle
+    */
+    public ResourceBundle getTexts() {
+        return getTexts(clazz.getName());
+    }
+}

java/com/opensymphony/xwork/ValidationAwareSupport.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * ValidationAwareSupport
+ * @author Jason Carreira
+ * Created Aug 2, 2003 11:56:27 PM
+ */
+public class ValidationAwareSupport implements ValidationAware {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    private Collection actionErrors;
+    private Map fieldErrors;
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public void setActionErrors(Collection errorMessages) {
+        this.actionErrors = errorMessages;
+    }
+
+    public Collection getActionErrors() {
+        if (actionErrors == null) {
+            actionErrors = new ArrayList();
+        }
+
+        return actionErrors;
+    }
+
+    public void setFieldErrors(Map errorMap) {
+        this.fieldErrors = errorMap;
+    }
+
+    public Map getFieldErrors() {
+        if (fieldErrors == null) {
+            fieldErrors = new HashMap();
+        }
+
+        return fieldErrors;
+    }
+
+    public void addActionError(String anErrorMessage) {
+        getActionErrors().add(anErrorMessage);
+    }
+
+    public void addFieldError(String fieldName, String errorMessage) {
+        final Map errors = getFieldErrors();
+        List thisFieldErrors = (List) errors.get(fieldName);
+
+        if (thisFieldErrors == null) {
+            thisFieldErrors = new ArrayList();
+            errors.put(fieldName, thisFieldErrors);
+        }
+
+        thisFieldErrors.add(errorMessage);
+    }
+
+    public boolean hasActionErrors() {
+        return (actionErrors != null) && !actionErrors.isEmpty();
+    }
+
+    /**
+    * Note that this does not have the same meaning as in WW 1.x
+    * @return (hasActionErrors() || hasFieldErrors())
+    */
+    public boolean hasErrors() {
+        return (hasActionErrors() || hasFieldErrors());
+    }
+
+    public boolean hasFieldErrors() {
+        return (fieldErrors != null) && !fieldErrors.isEmpty();
+    }
+}

java/com/opensymphony/xwork/validator/ActionValidatorManager.java

 
 import com.opensymphony.util.FileManager;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 import java.io.InputStream;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
     protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
     private static final Map validatorCache = Collections.synchronizedMap(new HashMap());
     private static final Map validatorFileCache = Collections.synchronizedMap(new HashMap());
+    private static final Log LOG = LogFactory.getLog(ActionValidatorManager.class);
 
     //~ Methods ////////////////////////////////////////////////////////////////
 
         return (List) validatorCache.get(validatorKey);
     }
 
+    public static void validate(Object object, String context) throws ValidationException {
+        ValidatorContext validatorContext = new DelegatingValidatorContext(object);
+        validate(object, context, validatorContext);
+    }
+
+    public static void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException {
+        List validators = getValidators(object.getClass(), context);
+
+        for (Iterator iterator = validators.iterator(); iterator.hasNext();) {
+            Validator validator = (Validator) iterator.next();
+            validator.setValidatorContext(validatorContext);
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Running validator: " + validator + " for object " + object);
+            }
+
+            validator.validate(object);
+        }
+    }
+
     protected static String buildValidatorKey(Class clazz, String context) {
         StringBuffer sb = new StringBuffer(clazz.getName());
         sb.append("/");

java/com/opensymphony/xwork/validator/DelegatingValidatorContext.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork.validator;
+
+import com.opensymphony.xwork.LocaleAware;
+import com.opensymphony.xwork.LocaleAwareSupport;
+import com.opensymphony.xwork.ValidationAware;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+
+/**
+ * DelegatingValidatorContext
+ * @author Jason Carreira
+ * Created Aug 3, 2003 12:33:30 AM
+ */
+public class DelegatingValidatorContext implements ValidatorContext {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    private LocaleAware localeAware;
+    private ValidationAware validationAware;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public DelegatingValidatorContext(Object object) {
+        if (object instanceof ValidationAware) {
+            validationAware = (ValidationAware) object;
+        } else {
+            validationAware = new LoggingValidationAware(object);
+        }
+
+        if (object instanceof LocaleAware) {
+            localeAware = (LocaleAware) object;
+        } else {
+            localeAware = new LocaleAwareSupport(object.getClass());
+        }
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public void setActionErrors(Collection errorMessages) {
+        validationAware.setActionErrors(errorMessages);
+    }
+
+    public Collection getActionErrors() {
+        return validationAware.getActionErrors();
+    }
+
+    public void setFieldErrors(Map errorMap) {
+        validationAware.setFieldErrors(errorMap);
+    }
+
+    public Map getFieldErrors() {
+        return validationAware.getFieldErrors();
+    }
+
+    public Locale getLocale() {
+        return localeAware.getLocale();
+    }
+
+    public String getText(String aTextName) {
+        return localeAware.getText(aTextName);
+    }
+
+    public String getText(String aTextName, String defaultValue) {
+        return localeAware.getText(aTextName, defaultValue);
+    }
+
+    public ResourceBundle getTexts(String aBundleName) {
+        return localeAware.getTexts(aBundleName);
+    }
+
+    public ResourceBundle getTexts() {
+        return localeAware.getTexts();
+    }
+
+    public void addActionError(String anErrorMessage) {
+        validationAware.addActionError(anErrorMessage);
+    }
+
+    public void addFieldError(String fieldName, String errorMessage) {
+        validationAware.addFieldError(fieldName, errorMessage);
+    }
+
+    public boolean hasActionErrors() {
+        return validationAware.hasActionErrors();
+    }
+
+    public boolean hasErrors() {
+        return validationAware.hasErrors();
+    }
+
+    public boolean hasFieldErrors() {
+        return validationAware.hasFieldErrors();
+    }
+
+    //~ Inner Classes //////////////////////////////////////////////////////////
+
+    private class LoggingValidationAware implements ValidationAware {
+        private Log log;
+
+        public LoggingValidationAware(Object obj) {
+            log = LogFactory.getLog(obj.getClass());
+        }
+
+        public void setActionErrors(Collection errorMessages) {
+            for (Iterator iterator = errorMessages.iterator();
+                    iterator.hasNext();) {
+                String s = (String) iterator.next();
+                addActionError(s);
+            }
+        }
+
+        public Collection getActionErrors() {
+            return null;
+        }
+
+        public void setFieldErrors(Map errorMap) {
+            for (Iterator iterator = errorMap.entrySet().iterator();
+                    iterator.hasNext();) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                addFieldError((String) entry.getKey(), (String) entry.getValue());
+            }
+        }
+
+        public Map getFieldErrors() {
+            return null;
+        }
+
+        public void addActionError(String anErrorMessage) {
+            log.error("Validation error: " + anErrorMessage);
+        }
+
+        public void addFieldError(String fieldName, String errorMessage) {
+            log.error("Validation error for " + fieldName + ":" + errorMessage);
+        }
+
+        public boolean hasActionErrors() {
+            return false;
+        }
+
+        public boolean hasErrors() {
+            return false;
+        }
+
+        public boolean hasFieldErrors() {
+            return false;
+        }
+    }
+}

java/com/opensymphony/xwork/validator/ValidationInterceptor.java

     }
 
     protected void before(ActionInvocation invocation) throws Exception {
-        List validators = ActionValidatorManager.getValidators(invocation.getAction().getClass(), invocation.getProxy().getActionName());
         Action action = invocation.getAction();
+        String context = invocation.getProxy().getActionName();
 
         if (log.isDebugEnabled()) {
-            log.debug("Validating " + invocation.getProxy().getNamespace() + invocation.getProxy().getActionName() + " with " + validators.size() + " validators.");
+            log.debug("Validating " + invocation.getProxy().getNamespace() + invocation.getProxy().getActionName() + ".");
         }
 
-        for (Iterator iterator = validators.iterator(); iterator.hasNext();) {
-            Validator validator = (Validator) iterator.next();
-
-            if (log.isDebugEnabled()) {
-                log.debug("Running validator: " + validator);
-            }
-
-            validator.validate(action);
-        }
+        ActionValidatorManager.validate(action, context);
     }
 }

java/com/opensymphony/xwork/validator/Validator.java

 
     String getMessageKey();
 
+    /**
+     * This method will be called before validate with a non-null ValidatorContext.
+     * @param validatorContext
+     */
+    void setValidatorContext(ValidatorContext validatorContext);
+
+    ValidatorContext getValidatorContext();
+
+    /**
+     * The validation implementation must guarantee that setValidatorContext will
+     * be called with a non-null ValidatorContext before validate is called.
+     * @param object
+     * @throws ValidationException
+     */
     void validate(Object object) throws ValidationException;
 }

java/com/opensymphony/xwork/validator/ValidatorContext.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork.validator;
+
+import com.opensymphony.xwork.LocaleAware;
+import com.opensymphony.xwork.ValidationAware;
+
+
+/**
+ * ValidatorContext
+ * @author Jason Carreira
+ * Created Aug 3, 2003 12:30:32 AM
+ */
+public interface ValidatorContext extends ValidationAware, LocaleAware {
+}

java/com/opensymphony/xwork/validator/ValidatorFileParser.java

 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
 import org.xml.sax.EntityResolver;
                 validator.setMessageKey(key);
             }
 
-            String defaultMessage = messageElement.getFirstChild().getNodeValue();
+            final Node defaultMessageNode = messageElement.getFirstChild();
+            String defaultMessage = (defaultMessageNode == null) ? "" : defaultMessageNode.getNodeValue();
             validator.setDefaultMessage(defaultMessage);
             validators.add(validator);
         }

java/com/opensymphony/xwork/validator/validators/ValidatorSupport.java

 package com.opensymphony.xwork.validator.validators;
 
 import com.opensymphony.xwork.ActionContext;
-import com.opensymphony.xwork.LocaleAware;
-import com.opensymphony.xwork.ValidationAware;
 import com.opensymphony.xwork.util.OgnlValueStack;
 import com.opensymphony.xwork.util.TextParseUtil;
 import com.opensymphony.xwork.validator.ValidationException;
 import com.opensymphony.xwork.validator.Validator;
+import com.opensymphony.xwork.validator.ValidatorContext;
 
 import ognl.Ognl;
 import ognl.OgnlException;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import java.util.MissingResourceException;
-
 
 /**
  * ValidatorSupport
     protected final Log log = LogFactory.getLog(this.getClass());
     private String defaultMessage = "";
     private String messageKey = null;
+    private ValidatorContext validatorContext;
 
     //~ Methods ////////////////////////////////////////////////////////////////
 
     public String getMessage(Object object) {
         String message;
 
-        if ((messageKey != null) && (object instanceof LocaleAware)) {
-            try {
-                LocaleAware localeAware = (LocaleAware) object;
-                message = localeAware.getText(messageKey);
-            } catch (MissingResourceException e) {
-                message = defaultMessage;
-            }
+        if (messageKey != null) {
+            message = validatorContext.getText(messageKey, defaultMessage);
         } else {
             message = defaultMessage;
         }
         return messageKey;
     }
 
+    public void setValidatorContext(ValidatorContext validatorContext) {
+        this.validatorContext = validatorContext;
+    }
+
+    public ValidatorContext getValidatorContext() {
+        return validatorContext;
+    }
+
     protected Object getFieldValue(String name, Object object) throws ValidationException {
         try {
             return Ognl.getValue(name, object);
     }
 
     protected void addActionError(Object object) {
-        if (object instanceof ValidationAware) {
-            ValidationAware validationAware = (ValidationAware) object;
-            validationAware.addActionError(getMessage(object));
-        } else {
-            log.error("Validation error: " + getMessage(object));
-        }
+        validatorContext.addActionError(getMessage(object));
     }
 
     protected void addFieldError(String propertyName, Object object) {
-        if (object instanceof ValidationAware) {
-            ValidationAware validationAction = (ValidationAware) object;
-            validationAction.addFieldError(propertyName, getMessage(object));
-        } else {
-            log.error("Validation error for " + propertyName + ":" + getMessage(object));
-        }
+        validatorContext.addFieldError(propertyName, getMessage(object));
     }
 }

java/com/opensymphony/xwork/validator/validators/VisitorFieldValidator.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork.validator.validators;
+
+import com.opensymphony.xwork.ActionContext;
+import com.opensymphony.xwork.validator.ActionValidatorManager;
+import com.opensymphony.xwork.validator.DelegatingValidatorContext;
+import com.opensymphony.xwork.validator.ValidationException;
+import com.opensymphony.xwork.validator.ValidatorContext;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+
+/**
+ * VisitorFieldValidator
+ * @author Jason Carreira
+ * Created Aug 2, 2003 10:27:48 PM
+ */
+public class VisitorFieldValidator extends FieldValidatorSupport {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    private String context;
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public void setContext(String context) {
+        this.context = context;
+    }
+
+    public String getContext() {
+        return context;
+    }
+
+    public void validate(Object object) throws ValidationException {
+        String fieldName = getFieldName();
+        Object value = this.getFieldValue(fieldName, object);
+        String visitorContext = (context == null) ? ActionContext.getContext().getName() : context;
+
+        if (value == null) {
+            return;
+        }
+
+        if (value instanceof Collection) {
+            Collection coll = (Collection) value;
+
+            for (Iterator iterator = coll.iterator(); iterator.hasNext();) {
+                Object o = iterator.next();
+                ValidatorContext validatorContext = new AppendingValidatorContext(getValidatorContext(), getMessage(o));
+                ActionValidatorManager.validate(o, visitorContext, validatorContext);
+            }
+        } else if (value instanceof Object[]) {
+            Object[] array = (Object[]) value;
+
+            for (int i = 0; i < array.length; i++) {
+                Object o = array[i];
+                ValidatorContext validatorContext = new AppendingValidatorContext(getValidatorContext(), getMessage(o));
+                ActionValidatorManager.validate(o, visitorContext, validatorContext);
+            }
+        } else {
+            ValidatorContext validatorContext = new AppendingValidatorContext(getValidatorContext(), getMessage(value));
+            ActionValidatorManager.validate(value, visitorContext, validatorContext);
+        }
+    }
+
+    //~ Inner Classes //////////////////////////////////////////////////////////
+
+    private class AppendingValidatorContext extends DelegatingValidatorContext {
+        String message;
+
+        public AppendingValidatorContext(Object object, String message) {
+            super(object);
+            this.message = message;
+        }
+
+        public void addActionError(String anErrorMessage) {
+            super.addActionError(message + anErrorMessage);
+        }
+
+        public void addFieldError(String fieldName, String errorMessage) {
+            super.addFieldError(fieldName, message + errorMessage);
+        }
+    }
+}

test/com/opensymphony/xwork/TestBean-anotherContext-validation.xml

+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
+<validators>
+    <field name="count">
+        <field-validator type="int">
+            <param name="min">1</param>
+            <param name="max">100</param>
+            <message>Count must be between ${min} and ${max}, current value is ${count}.</message>
+        </field-validator>
+    </field>
+</validators>

test/com/opensymphony/xwork/TestBean-validation.xml

+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
+<validators>
+    <field name="name">
+        <field-validator type="requiredstring">
+            <message>You must enter a name.</message>
+        </field-validator>
+    </field>
+</validators>

test/com/opensymphony/xwork/TestBean-visitorValidation-validation.xml

+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
+<validators>
+    <field name="birth">
+        <field-validator type="date">
+            <param name="min">01/01/1970</param>
+            <message>You must have been born after 1970.</message>
+        </field-validator>
+    </field>
+</validators>

test/com/opensymphony/xwork/TestBean.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork;
+
+import java.util.Date;
+
+
+/**
+ * TestBean
+ * @author Jason Carreira
+ * Created Aug 4, 2003 12:39:53 AM
+ */
+public class TestBean {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    private Date birth;
+    private String name;
+    private int count;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public TestBean() {
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public void setBirth(Date birth) {
+        this.birth = birth;
+    }
+
+    public Date getBirth() {
+        return birth;
+    }
+
+    public void setCount(int count) {
+        this.count = count;
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+}

test/com/opensymphony/xwork/VisitorValidatorTestAction-validation.xml

+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
+<validators>
+    <field name="context">
+        <field-validator type="requiredstring">
+            <message>You must enter a context.</message>
+        </field-validator>
+    </field>
+</validators>

test/com/opensymphony/xwork/VisitorValidatorTestAction-visitorValidation-validation.xml

+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
+<validators>
+    <field name="bean">
+        <field-validator type="visitor">
+            <message>bean: </message>
+        </field-validator>
+    </field>
+</validators>

test/com/opensymphony/xwork/VisitorValidatorTestAction-visitorValidationAlias-validation.xml

+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
+<validators>
+    <field name="bean">
+        <field-validator type="visitor">
+            <param name="context">anotherContext</param>
+            <message>bean: </message>
+        </field-validator>
+    </field>
+</validators>

test/com/opensymphony/xwork/VisitorValidatorTestAction.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * VisitorValidatorTestAction
+ * @author Jason Carreira
+ * Created Aug 4, 2003 1:00:04 AM
+ */
+public class VisitorValidatorTestAction extends BaseActionSupport {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    private List testBeanList = new ArrayList();
+    private String context;
+    private TestBean bean = new TestBean();
+    private TestBean[] testBeanArray;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public VisitorValidatorTestAction() {
+        testBeanArray = new TestBean[5];
+
+        for (int i = 0; i < 5; i++) {
+            testBeanArray[i] = new TestBean();
+            testBeanList.add(new TestBean());
+        }
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public void setBean(TestBean bean) {
+        this.bean = bean;
+    }
+
+    public TestBean getBean() {
+        return bean;
+    }
+
+    public void setContext(String context) {
+        this.context = context;
+    }
+
+    public String getContext() {
+        return context;
+    }
+
+    public void setTestBeanArray(TestBean[] testBeanArray) {
+        this.testBeanArray = testBeanArray;
+    }
+
+    public TestBean[] getTestBeanArray() {
+        return testBeanArray;
+    }
+
+    public void setTestBeanList(List testBeanList) {
+        this.testBeanList = testBeanList;
+    }
+
+    public List getTestBeanList() {
+        return testBeanList;
+    }
+}

test/com/opensymphony/xwork/config/providers/MockConfigurationProvider.java

 package com.opensymphony.xwork.config.providers;
 
 import com.opensymphony.xwork.ActionChainResult;
-import com.opensymphony.xwork.ModelDrivenAction;
 import com.opensymphony.xwork.SimpleAction;
 import com.opensymphony.xwork.config.Configuration;
 import com.opensymphony.xwork.config.ConfigurationProvider;

test/com/opensymphony/xwork/validator/DateRangeValidatorTest.java

 
 import junit.framework.TestCase;
 
+import java.util.List;
 import java.util.Map;
 
 
             assertTrue(((ValidationAware) proxy.getAction()).hasFieldErrors());
 
             Map errors = ((ValidationAware) proxy.getAction()).getFieldErrors();
-            String errorMessage = (String) errors.get("date");
+            List errorMessages = (List) errors.get("date");
+            assertEquals(1, errorMessages.size());
+
+            String errorMessage = (String) errorMessages.get(0);
             assertNotNull(errorMessage);
         } catch (Exception e) {
             e.printStackTrace();

test/com/opensymphony/xwork/validator/IntRangeValidatorTest.java

 import junit.framework.TestCase;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 
             assertTrue(((ValidationAware) proxy.getAction()).hasFieldErrors());
 
             Map errors = ((ValidationAware) proxy.getAction()).getFieldErrors();
-            String errorMessage = (String) errors.get("bar");
+            List errorMessages = (List) errors.get("bar");
+            assertEquals(1, errorMessages.size());
+
+            String errorMessage = (String) errorMessages.get(0);
             assertNotNull(errorMessage);
         } catch (Exception e) {
             e.printStackTrace();

test/com/opensymphony/xwork/validator/SimpleActionValidationTest.java

 import junit.framework.TestCase;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
             Map errors = validationAware.getFieldErrors();
             assertTrue(errors.containsKey("baz"));
 
-            String message = (String) errors.get("baz");
+            List bazErrors = (List) errors.get("baz");
+            assertEquals(1, bazErrors.size());
+
+            String message = (String) bazErrors.get(0);
             assertEquals("baz out of range.", message);
             assertTrue(errors.containsKey("bar"));
-            message = (String) errors.get("bar");
+
+            List barErrors = (List) errors.get("bar");
+            assertEquals(1, barErrors.size());
+            message = (String) barErrors.get(0);
             assertEquals("bar must be between 6 and 10, current value is 42.", message);
         } catch (Exception e) {
             e.printStackTrace();
             assertTrue(((ValidationAware) proxy.getAction()).hasFieldErrors());
 
             Map errors = ((ValidationAware) proxy.getAction()).getFieldErrors();
-            String errorMessage = (String) errors.get("foo");
+            List fooErrors = (List) errors.get("foo");
+            assertEquals(1, fooErrors.size());
+
+            String errorMessage = (String) fooErrors.get(0);
             assertNotNull(errorMessage);
             assertEquals("Foo Range Message", errorMessage);
         } catch (Exception e) {
             assertTrue(((ValidationAware) proxy.getAction()).hasFieldErrors());
 
             Map errors = ((ValidationAware) proxy.getAction()).getFieldErrors();
-            String errorMessage = (String) errors.get("bar");
+            List barErrors = (List) errors.get("bar");
+            assertEquals(1, barErrors.size());
+
+            String errorMessage = (String) barErrors.get(0);
             assertNotNull(errorMessage);
             assertEquals("bar must be between 6 and 10, current value is 42.", errorMessage);
         } catch (Exception e) {

test/com/opensymphony/xwork/validator/VisitorFieldValidatorTest.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork.validator;
+
+import com.opensymphony.xwork.ActionContext;
+import com.opensymphony.xwork.TestBean;
+import com.opensymphony.xwork.VisitorValidatorTestAction;
+
+import junit.framework.TestCase;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * VisitorFieldValidatorTest
+ * @author Jason Carreira
+ * Created Aug 4, 2003 1:26:01 AM
+ */
+public class VisitorFieldValidatorTest extends TestCase {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    protected VisitorValidatorTestAction action;
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public void setUp() {
+        action = new VisitorValidatorTestAction();
+
+        TestBean bean = action.getBean();
+        Calendar cal = new GregorianCalendar(1900, 01, 01);
+        bean.setBirth(cal.getTime());
+        bean.setCount(-1);
+    }
+
+    public void testArrayValidation() throws Exception {
+        TestBean[] beanArray = action.getTestBeanArray();
+        TestBean testBean = beanArray[0];
+        testBean.setName("foo");
+        ActionValidatorManager.validate(action, "validateArray");
+
+        assertTrue(action.hasFieldErrors());
+
+        Map fieldErrors = action.getFieldErrors();
+        assertEquals(2, fieldErrors.size());
+        assertTrue(fieldErrors.containsKey("name"));
+
+        //the error from the action should be there too
+        assertTrue(fieldErrors.containsKey("context"));
+
+        List errors = (List) fieldErrors.get("name");
+        assertEquals(4, errors.size());
+    }
+
+    public void testCollectionValidation() throws Exception {
+        List testBeanList = action.getTestBeanList();
+        TestBean testBean = (TestBean) testBeanList.get(0);
+        testBean.setName("foo");
+        ActionValidatorManager.validate(action, "validateList");
+
+        assertTrue(action.hasFieldErrors());
+
+        Map fieldErrors = action.getFieldErrors();
+        assertEquals(2, fieldErrors.size());
+        assertTrue(fieldErrors.containsKey("name"));
+
+        //the error from the action should be there too
+        assertTrue(fieldErrors.containsKey("context"));
+
+        List errors = (List) fieldErrors.get("name");
+        assertEquals(4, errors.size());
+    }
+
+    public void testContextIsOverriddenByContextParamInValidationXML() throws Exception {
+        ActionValidatorManager.validate(action, "visitorValidationAlias");
+        assertTrue(action.hasFieldErrors());
+
+        Map fieldErrors = action.getFieldErrors();
+        assertEquals(3, fieldErrors.size());
+        assertTrue(fieldErrors.containsKey("count"));
+        assertTrue(fieldErrors.containsKey("name"));
+        assertTrue(!fieldErrors.containsKey("birth"));
+
+        //the error from the action should be there too
+        assertTrue(fieldErrors.containsKey("context"));
+    }
+
+    public void testContextIsPropagated() throws Exception {
+        ActionContext.getContext().setName("visitorValidation");
+        ActionValidatorManager.validate(action, "visitorValidation");
+        assertTrue(action.hasFieldErrors());
+
+        Map fieldErrors = action.getFieldErrors();
+        assertEquals(3, fieldErrors.size());
+        assertTrue(!fieldErrors.containsKey("count"));
+        assertTrue(fieldErrors.containsKey("name"));
+        assertTrue(fieldErrors.containsKey("birth"));
+
+        //the error from the action should be there too
+        assertTrue(fieldErrors.containsKey("context"));
+    }
+}

test/validators.xml

     <validator name="fieldexpression" class="com.opensymphony.xwork.validator.validators.FieldExpressionValidator"/>
     <validator name="email" class="com.opensymphony.xwork.validator.validators.EmailValidator"/>
     <validator name="url" class="com.opensymphony.xwork.validator.validators.URLValidator"/>
+    <validator name="visitor" class="com.opensymphony.xwork.validator.validators.VisitorFieldValidator"/>
 </validators>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.