Author: lukaszlenart
Date: Fri May 24 08:56:41 2013
New Revision: 1485978

URL: http://svn.apache.org/r1485978
Log:
WW-600 Enables Client-side validation for visitor validations

Modified:
    
struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Form.java
    
struts/struts2/trunk/core/src/main/resources/template/xhtml/form-close-validate.ftl
    
struts/struts2/trunk/core/src/test/java/org/apache/struts2/components/FormTest.java
    
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-2.txt
    
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-22.txt
    
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-24.txt

Modified: 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Form.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Form.java?rev=1485978&r1=1485977&r2=1485978&view=diff
==============================================================================
--- 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Form.java 
(original)
+++ 
struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Form.java 
Fri May 24 08:56:41 2013
@@ -31,16 +31,22 @@ import com.opensymphony.xwork2.intercept
 import com.opensymphony.xwork2.util.ValueStack;
 import com.opensymphony.xwork2.validator.ActionValidatorManager;
 import com.opensymphony.xwork2.validator.FieldValidator;
+import com.opensymphony.xwork2.validator.ValidationException;
 import com.opensymphony.xwork2.validator.ValidationInterceptor;
 import com.opensymphony.xwork2.validator.Validator;
-import org.apache.struts2.StrutsConstants;
+import com.opensymphony.xwork2.validator.ValidatorContext;
+import com.opensymphony.xwork2.validator.validators.VisitorFieldValidator;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
 import org.apache.struts2.views.annotations.StrutsTag;
 import org.apache.struts2.views.annotations.StrutsTagAttribute;
-import org.apache.commons.lang3.StringUtils;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.util.*;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -116,14 +122,17 @@ public class Form extends ClosingUIBean 
         super(stack, request, response);
     }
 
+    @Override
     protected boolean evaluateNameValue() {
         return false;
     }
 
+    @Override
     public String getDefaultOpenTemplate() {
         return OPEN_TEMPLATE;
     }
 
+    @Override
     protected String getDefaultTemplate() {
         return TEMPLATE;
     }
@@ -153,6 +162,7 @@ public class Form extends ClosingUIBean 
     * Revised for Portlet actionURL as form action, and add wwAction as hidden
     * field. Refer to template.simple/form.vm
     */
+    @Override
     protected void evaluateExtraParams() {
         super.evaluateExtraParams();
         if (validate != null) {
@@ -210,6 +220,7 @@ public class Form extends ClosingUIBean 
      *    <li>if an 'action' attribute is specified, it will be used as the 
id.</li>
      * </ol>
      */
+    @Override
     protected void populateComponentHtmlId(Form form) {
         if (id != null) {
             addParameter("id", escape(id));
@@ -262,21 +273,145 @@ public class Form extends ClosingUIBean 
             return Collections.EMPTY_LIST;
         }
 
-        List<Validator> all = 
actionValidatorManager.getValidators(actionClass, (String) 
getParameters().get("actionName"));
+        String formActionValue = findString(action);
+        ActionMapping mapping = 
actionMapper.getMappingFromActionName(formActionValue);
+        String actionName = mapping.getName();
+        String methodName = mapping.getMethod();
+
+        List<Validator> actionValidators = 
actionValidatorManager.getValidators(actionClass, actionName, methodName);
         List<Validator> validators = new ArrayList<Validator>();
-        for (Validator validator : all) {
+
+        findFieldValidators(name, actionClass, actionName, actionValidators, 
validators, "");
+
+        return validators;
+    }
+
+    private void findFieldValidators(String name, Class actionClass, String 
actionName,
+            List<Validator> validatorList, List<Validator> retultValidators, 
String prefix) {
+
+        for (Validator validator : validatorList) {
             if (validator instanceof FieldValidator) {
                 FieldValidator fieldValidator = (FieldValidator) validator;
-                if (fieldValidator.getFieldName().equals(name)) {
-                    validators.add(fieldValidator);
+
+                if (validator instanceof VisitorFieldValidator) {
+                    VisitorFieldValidator vfValidator = 
(VisitorFieldValidator) fieldValidator;
+                    Class clazz = getVisitorReturnType(actionClass, 
vfValidator.getFieldName());
+                    if (clazz == null) {
+                        continue;
+                    }
+
+                    List<Validator> visitorValidators = 
actionValidatorManager.getValidators(clazz, actionName);
+                    String vPrefix = prefix + (vfValidator.isAppendPrefix() ? 
vfValidator.getFieldName() + "." : "");
+                    findFieldValidators(name, clazz, actionName, 
visitorValidators, retultValidators, vPrefix);
+                } else if ((prefix + 
fieldValidator.getFieldName()).equals(name)) {
+                    if (StringUtils.isNotBlank(prefix)) {
+                        //fixing field name for js side
+                        FieldVisitorValidatorWrapper wrap = new 
FieldVisitorValidatorWrapper(fieldValidator, prefix);
+                        retultValidators.add(wrap);
+                    } else {
+                        retultValidators.add(fieldValidator);
+                    }
                 }
             }
         }
+    }
 
-        return validators;
+    /**
+     * Wrap field validator, add visitor's field prefix to the field name.
+     * Javascript side is not aware of the visitor validators
+     * and does not know how to prefix the fields.
+     */
+    /*
+     * Class is public because Freemarker has problems accessing properties.
+     */
+    public static class FieldVisitorValidatorWrapper implements FieldValidator 
{
+        private FieldValidator fieldValidator;
+        private String namePrefix;
+        public FieldVisitorValidatorWrapper(FieldValidator fv, String 
namePrefix) {
+            this.fieldValidator = fv;
+            this.namePrefix = namePrefix;
+        }
+        public String getValidatorType() {
+            return "field-visitor";
+        }
+        public String getFieldName() {
+            return namePrefix + fieldValidator.getFieldName();
+        }
+        public FieldValidator getFieldValidator() {
+            return fieldValidator;
+        }
+        public void setFieldValidator(FieldValidator fieldValidator) {
+            this.fieldValidator = fieldValidator;
+        }
+        public String getDefaultMessage() {
+            return fieldValidator.getDefaultMessage();
+        }
+        public String getMessage(Object object) {
+            return fieldValidator.getMessage(object);
+        }
+        public String getMessageKey() {
+            return fieldValidator.getMessageKey();
+        }
+        public String[] getMessageParameters() {
+            return fieldValidator.getMessageParameters();
+        }
+        public ValidatorContext getValidatorContext() {
+            return fieldValidator.getValidatorContext();
+        }
+        public void setDefaultMessage(String message) {
+            fieldValidator.setDefaultMessage(message);
+        }
+        public void setFieldName(String fieldName) {
+            fieldValidator.setFieldName(fieldName);
+        }
+        public void setMessageKey(String key) {
+            fieldValidator.setMessageKey(key);
+        }
+        public void setMessageParameters(String[] messageParameters) {
+            fieldValidator.setMessageParameters(messageParameters);
+        }
+        public void setValidatorContext(ValidatorContext validatorContext) {
+            fieldValidator.setValidatorContext(validatorContext);
+        }
+        public void setValidatorType(String type) {
+            fieldValidator.setValidatorType(type);
+        }
+        public void setValueStack(ValueStack stack) {
+            fieldValidator.setValueStack(stack);
+        }
+        public void validate(Object object) throws ValidationException {
+            fieldValidator.validate(object);
+        }
+        public String getNamePrefix() {
+            return namePrefix;
+        }
+        public void setNamePrefix(String namePrefix) {
+            this.namePrefix = namePrefix;
+        }
     }
 
     /**
+     * Return type of visited object.
+     * @param actionClass
+     * @param visitorFieldName
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    protected Class getVisitorReturnType(Class actionClass, String 
visitorFieldName) {
+        if (visitorFieldName == null) {
+            return null;
+        }
+        String methodName = "get" + 
org.apache.commons.lang.StringUtils.capitalize(visitorFieldName);
+        try {
+            Method method = actionClass.getMethod(methodName, new Class[0]);
+            return method.getReturnType();
+        } catch (NoSuchMethodException e) {
+            return null;
+        }
+    }
+
+
+    /**
      * Get a incrementing sequence unique to this <code>Form</code> component.
      * It is used by <code>Form</code> component's child that might need a
      * sequence to make them unique.

Modified: 
struts/struts2/trunk/core/src/main/resources/template/xhtml/form-close-validate.ftl
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/template/xhtml/form-close-validate.ftl?rev=1485978&r1=1485977&r2=1485978&view=diff
==============================================================================
--- 
struts/struts2/trunk/core/src/main/resources/template/xhtml/form-close-validate.ftl
 (original)
+++ 
struts/struts2/trunk/core/src/main/resources/template/xhtml/form-close-validate.ftl
 Fri May 24 08:56:41 2013
@@ -36,6 +36,26 @@ END SNIPPET: supported-validators
 <#if ((parameters.validate!false == true) && 
(parameters.performValidation!false == true))>
 <script type="text/javascript">
     function validateForm_${parameters.id?replace('[^a-zA-Z0-9_]', '_', 
'r')}() {
+        <#--
+            In case of multiselect fields return only the first value.
+        -->
+        var getFieldValue = function(field) {
+            var type = field.type ? field.type : field[0].type;
+            if (type == 'select-one' || type == 'select-multiple') {
+                return (field.selectedIndex == -1 ? "" : 
field.options[field.selectedIndex].value);
+            } else if (type == 'checkbox' || type == 'radio') {
+                if (!field.length) {
+                    field = [field];
+                }
+                for (var i = 0; i < field.length; i++) {
+                    if (field[i].checked) {
+                        return field[i].value;
+                    }
+                }
+                return "";
+            }
+            return field.value;
+        }
         form = document.getElementById("${parameters.id}");
         clearErrorMessages(form);
         clearErrorLabels(form);
@@ -43,27 +63,36 @@ END SNIPPET: supported-validators
         var errors = false;
         var continueValidation = true;
     <#list parameters.tagNames as tagName>
-        <#list tag.getValidators("${tagName}") as validator>
-        // field name: ${validator.fieldName}
-        // validator name: ${validator.validatorType}
-        if (form.elements['${validator.fieldName}']) {
-            field = form.elements['${validator.fieldName}'];
+        <#list tag.getValidators("${tagName}") as aValidator>
+        // field name: ${aValidator.fieldName}
+        // validator name: ${aValidator.validatorType}
+        if (form.elements['${aValidator.fieldName}']) {
+            field = form.elements['${aValidator.fieldName}'];
+            <#if aValidator.validatorType = "field-visitor">
+                <#assign validator = aValidator.fieldValidator >
+                //visitor validator switched to: ${validator.validatorType}
+            <#else>
+                <#assign validator = aValidator >
+            </#if>
+
             var error = "${validator.getMessage(action)?js_string}";
+            var fieldValue = getFieldValue(field);
+            
             <#if validator.validatorType = "required">
-            if (field.value == "") {
+            if (fieldValue == "") {
                 addError(field, error);
                 errors = true;
                 <#if validator.shortCircuit>continueValidation = false;</#if>
             }
             <#elseif validator.validatorType = "requiredstring">
-            if (continueValidation && field.value != null && (field.value == 
"" || field.value.replace(/^\s+|\s+$/g,"").length == 0)) {
+            if (continueValidation && fieldValue != null && (fieldValue == "" 
|| fieldValue.replace(/^\s+|\s+$/g,"").length == 0)) {
                 addError(field, error);
                 errors = true;
                 <#if validator.shortCircuit>continueValidation = false;</#if>
             }
             <#elseif validator.validatorType = "stringlength">
-            if (continueValidation && field.value != null) {
-                var value = field.value;
+            if (continueValidation && fieldValue != null) {
+                var value = fieldValue;
                 <#if validator.trim>
                     //trim field value
                     while (value.substring(0,1) == ' ')
@@ -79,28 +108,28 @@ END SNIPPET: supported-validators
                 }
             }
             <#elseif validator.validatorType = "regex">
-            if (continueValidation && field.value != null && 
!field.value.match("${validator.regex?js_string}")) {
+            if (continueValidation && fieldValue != null && 
!fieldValue.match("${validator.regex?js_string}")) {
                 addError(field, error);
                 errors = true;
                 <#if validator.shortCircuit>continueValidation = false;</#if>
             }
             <#elseif validator.validatorType = "email">
-            if (continueValidation && field.value != null && 
field.value.length > 0 && 
field.value.match("${validator.regex?js_string}")==null) {
+            if (continueValidation && fieldValue != null && fieldValue.length 
> 0 && fieldValue.match("${validator.regex?js_string}")==null) {
                 addError(field, error);
                 errors = true;
                 <#if validator.shortCircuit>continueValidation = false;</#if>
             }
             <#elseif validator.validatorType = "url">
-            if (continueValidation && field.value != null && 
field.value.length > 0 && 
field.value.match(/^(ftp|http|https):\/\/((%[A-F0-9]{2}|[A-Z0-9-._~!$&'()*+,;=:])+@)?((%[A-F0-9]{2}|[A-Z0-9-._~!$&'()*+,;=])+)(:[0-9]+)?((\/(%[A-F0-9]{2}|[A-Z0-9-._~!$&'()*+,;=:@])*)*)(\?(%[A-F0-9]{2}|[A-Z0-9-._~!$&'()*+,;=:@/?])*)?(#(%[A-F0-9]{2}|[A-Z0-9-._~!$&'()*+,;=:@/?])*)?$/gi)==null)
 {
+            if (continueValidation && fieldValue != null && fieldValue.length 
> 0 && fieldValue.match("${validator.regex?js_string}")==null) {
                 addError(field, error);
                 errors = true;
                 <#if validator.shortCircuit>continueValidation = false;</#if>
             }
             <#elseif validator.validatorType = "int" || 
validator.validatorType = "short">
-            if (continueValidation && field.value != null) {
-                if (<#if validator.min??>parseInt(field.value) <
+            if (continueValidation && fieldValue != null) {
+                if (<#if validator.min??>parseInt(fieldValue) <
                      ${validator.min?c}<#else>false</#if> ||
-                        <#if validator.max??>parseInt(field.value) >
+                        <#if validator.max??>parseInt(fieldValue) >
                            ${validator.max?c}<#else>false</#if>) {
                     addError(field, error);
                     errors = true;
@@ -108,8 +137,8 @@ END SNIPPET: supported-validators
                 }
             }
             <#elseif validator.validatorType = "double">
-            if (continueValidation && field.value != null) {
-                var value = parseFloat(field.value);
+            if (continueValidation && fieldValue != null) {
+                var value = parseFloat(fieldValue);
                 if (<#if validator.minInclusive??>value < 
${validator.minInclusive?c}<#else>false</#if> ||
                         <#if validator.maxInclusive??>value > 
${validator.maxInclusive?c}<#else>false</#if> ||
                         <#if validator.minExclusive??>value <= 
${validator.minExclusive?c}<#else>false</#if> ||
@@ -126,4 +155,4 @@ END SNIPPET: supported-validators
         return !errors;
     }
 </script>
-</#if>
+</#if>
\ No newline at end of file

Modified: 
struts/struts2/trunk/core/src/test/java/org/apache/struts2/components/FormTest.java
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/components/FormTest.java?rev=1485978&r1=1485977&r2=1485978&view=diff
==============================================================================
--- 
struts/struts2/trunk/core/src/test/java/org/apache/struts2/components/FormTest.java
 (original)
+++ 
struts/struts2/trunk/core/src/test/java/org/apache/struts2/components/FormTest.java
 Fri May 24 08:56:41 2013
@@ -21,18 +21,17 @@
 
 package org.apache.struts2.components;
 
-import java.util.List;
-
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.config.entities.ActionConfig;
+import com.opensymphony.xwork2.validator.validators.RequiredFieldValidator;
 import org.apache.struts2.TestAction;
 import org.apache.struts2.views.jsp.AbstractUITagTest;
 import org.easymock.EasyMock;
 
-import com.opensymphony.xwork2.validator.validators.RequiredFieldValidator;
-import com.opensymphony.xwork2.config.entities.ActionConfig;
-import com.opensymphony.xwork2.ActionInvocation;
-import com.opensymphony.xwork2.ActionProxy;
-import com.opensymphony.xwork2.Action;
-import com.opensymphony.xwork2.ActionContext;
+import java.util.List;
 
 /**
  * <code>FormTest</code>
@@ -45,6 +44,7 @@ public class FormTest extends AbstractUI
         Form form = new Form(stack, request, response);
         container.inject(form);
         form.getParameters().put("actionClass", TestAction.class);
+        form.setAction("actionName");
         List v = form.getValidators("foo");
         assertEquals(1, v.size());
         assertEquals(RequiredFieldValidator.class, v.get(0).getClass());

Modified: 
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-2.txt
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-2.txt?rev=1485978&r1=1485977&r2=1485978&view=diff
==============================================================================
--- 
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-2.txt
 (original)
+++ 
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-2.txt
 Fri May 24 08:56:41 2013
@@ -32,12 +32,32 @@
 </script>
 
 <script type="text/javascript">
+
     function validateForm_myAction() {
+        var getFieldValue = function(field) {
+            var type = field.type ? field.type : field[0].type;
+            if(type == 'select-one' || type == 'select-multiple') {
+                return (field.selectedIndex == -1 ? "" : 
field.options[field.selectedIndex].value);
+            } else if(type=='checkbox'||type=='radio') {
+                if(!field.length){
+                    field=[field];
+                }
+                for(var i=0; i < field.length; i++){
+                    if(field[i].checked) {
+                        return field[i].value;
+                    }
+                }
+                return "";
+            }
+            return field.value;
+        }
         form = document.getElementById("myAction");
         clearErrorMessages(form);
         clearErrorLabels(form);
         var errors = false;
         var continueValidation=true;
-        return !errors; }
+        return !errors;
+    }
+
 </script>
 

Modified: 
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-22.txt
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-22.txt?rev=1485978&r1=1485977&r2=1485978&view=diff
==============================================================================
--- 
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-22.txt
 (original)
+++ 
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-22.txt
 Fri May 24 08:56:41 2013
@@ -33,23 +33,41 @@
 
 <script type="text/javascript">
     function validateForm_myAction() {
+        var getFieldValue = function(field) {
+            var type = field.type ? field.type : field[0].type;
+            if(type == 'select-one' || type == 'select-multiple') {
+                return (field.selectedIndex == -1 ? "" : 
field.options[field.selectedIndex].value);
+            } else if(type == 'checkbox' || type == 'radio') {
+                if(!field.length) {
+                    field = [field];
+                }
+                for(var i = 0; i < field.length; i++) {
+                    if(field[i].checked) {
+                        return field[i].value;
+                    }
+                }
+                return"";
+            }
+            return field.value;
+        }
         form = document.getElementById("myAction");
         clearErrorMessages(form);
         clearErrorLabels(form);
         var errors = false;
         var continueValidation = true;
-         //fieldname:myUpDownSelectTag
-         //validatorname:int
-         if(form.elements['myUpDownSelectTag']){
-            field=form.elements['myUpDownSelectTag'];
-            var error="bar must be between 6000 and 10000.";
-            if(continueValidation && field.value!=null){
-                if(parseInt(field.value)<6000||parseInt(field.value)>10000){
-                    addError(field,error);
-                    errors=true;
-                 }
-             }
-          }
-          return!errors;
-      }</script>
-
+        //fieldname:myUpDownSelectTag
+        //validatorname:int
+        if(form.elements['myUpDownSelectTag']) {
+            field = form.elements['myUpDownSelectTag'];
+            var error = "bar must be between 6000 and 10000.";
+            var fieldValue = getFieldValue(field);
+            if(continueValidation && fieldValue != null) {
+                if(parseInt(fieldValue) < 6000 || parseInt(fieldValue) > 
10000) {
+                    addError(field, error);
+                    errors = true;
+                }
+            }
+        }
+    return !errors;
+    }
+</script>
\ No newline at end of file

Modified: 
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-24.txt
URL: 
http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-24.txt?rev=1485978&r1=1485977&r2=1485978&view=diff
==============================================================================
--- 
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-24.txt
 (original)
+++ 
struts/struts2/trunk/core/src/test/resources/org/apache/struts2/views/jsp/ui/Formtag-24.txt
 Fri May 24 08:56:41 2013
@@ -33,24 +33,43 @@
 
 <script type="text/javascript">
     function validateForm_myAction() {
+        var getFieldValue = function(field) {
+            var type = field.type ? field.type : field[0].type;
+            if(type == 'select-one' || type == 'select-multiple') {
+                return (field.selectedIndex == -1 ? "" : 
field.options[field.selectedIndex].value);
+            } else if(type == 'checkbox' || type == 'radio') {
+                if(!field.length) {
+                    field=[field];
+                }
+                for(var i = 0; i < field.length; i++) {
+                    if(field[i].checked) {
+                        return field[i].value;
+                    }
+                }
+                return"";
+            }
+            return field.value;
+        }
         form = document.getElementById("myAction");
         clearErrorMessages(form);
         clearErrorLabels(form);
         var errors = false;
         var continueValidation = true;
-         //fieldname:myUpDownSelectTag
-         //validatorname:double
-         if(form.elements['myUpDownSelectTag']){
-            field=form.elements['myUpDownSelectTag'];
-            var error="bar must be between 6000.1 and 10000.1.";
-            if(continueValidation && field.value!=null){
-                var value = parseFloat(field.value);
-                if(value<6000.1||value>10000.1||false||false){
-                    addError(field,error);
-                    errors=true;
-                 }
-             }
-          }
-          return!errors;
-      }</script>
+        //fieldname:myUpDownSelectTag
+        //validatorname:double
+        if(form.elements['myUpDownSelectTag']) {
+            field = form.elements['myUpDownSelectTag'];
+            var error = "bar must be between 6000.1 and 10000.1.";
+            var fieldValue = getFieldValue(field);
+            if(continueValidation && fieldValue != null) {
+                var value = parseFloat(fieldValue);
+                if(value < 6000.1 || value > 10000.1 || false || false) {
+                    addError(field, error);
+                    errors = true;
+                }
+            }
+        }
+        return !errors;
+    }
+</script>
 


Reply via email to