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>