Return-Path:
X-Original-To: apmail-struts-commits-archive@minotaur.apache.org
Delivered-To: apmail-struts-commits-archive@minotaur.apache.org
Received: from mail.apache.org (hermes.apache.org [140.211.11.3])
by minotaur.apache.org (Postfix) with SMTP id A9D7D18F17
for ;
Wed, 17 Jun 2015 21:09:13 +0000 (UTC)
Received: (qmail 76456 invoked by uid 500); 17 Jun 2015 21:09:03 -0000
Delivered-To: apmail-struts-commits-archive@struts.apache.org
Received: (qmail 76370 invoked by uid 500); 17 Jun 2015 21:09:03 -0000
Mailing-List: contact commits-help@struts.apache.org; run by ezmlm
Precedence: bulk
List-Help:
List-Unsubscribe:
List-Post:
List-Id:
Reply-To: dev@struts.apache.org
Delivered-To: mailing list commits@struts.apache.org
Received: (qmail 75011 invoked by uid 99); 17 Jun 2015 21:09:02 -0000
Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org)
(140.211.11.23)
by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 17 Jun 2015 21:09:02 +0000
Received: by git1-us-west.apache.org (ASF Mail Server at
git1-us-west.apache.org, from userid 33)
id 67F1FDFFC0; Wed, 17 Jun 2015 21:09:02 +0000 (UTC)
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
From: lukaszlenart@apache.org
To: commits@struts.apache.org
Date: Wed, 17 Jun 2015 21:09:30 -0000
Message-Id: <932e2c704b60478a92a7fc5dedcf2b72@git.apache.org>
In-Reply-To:
References:
X-Mailer: ASF-Git Admin Mailer
Subject: [30/57] [partial] struts git commit: Merges xwork packages into
struts
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationValidationConfigurationBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationValidationConfigurationBuilder.java b/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationValidationConfigurationBuilder.java
new file mode 100644
index 0000000..dc8d856
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationValidationConfigurationBuilder.java
@@ -0,0 +1,901 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * 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 com.opensymphony.xwork2.validator;
+
+import com.opensymphony.xwork2.validator.annotations.*;
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * AnnotationValidationConfigurationBuilder
+ *
+ * @author Rainer Hermanns
+ * @author jepjep
+ * @version $Id$
+ */
+public class AnnotationValidationConfigurationBuilder {
+
+ private static final Pattern SETTER_PATTERN = Pattern.compile("set([A-Z][A-Za-z0-9]*)$");
+ private static final Pattern GETTER_PATTERN = Pattern.compile("(get|is|has)([A-Z][A-Za-z0-9]*)$");
+
+ private ValidatorFactory validatorFactory;
+
+ public AnnotationValidationConfigurationBuilder(ValidatorFactory fac) {
+ this.validatorFactory = fac;
+ }
+
+ private List processAnnotations(Object o) {
+
+ List result = new ArrayList<>();
+
+ String fieldName = null;
+ String methodName = null;
+
+ Annotation[] annotations = null;
+
+ if (o instanceof Class) {
+ Class clazz = (Class) o;
+ annotations = clazz.getAnnotations();
+ }
+
+ if (o instanceof Method) {
+ Method method = (Method) o;
+ fieldName = resolvePropertyName(method);
+ methodName = method.getName();
+
+ annotations = method.getAnnotations();
+ }
+
+ if (annotations != null) {
+ for (Annotation a : annotations) {
+
+ // Process collection of custom validations
+ if (a instanceof Validations) {
+ processValidationAnnotation(a, fieldName, methodName, result);
+ }
+
+ // Process single custom validator
+ if (a instanceof Validation) {
+ Validation v = (Validation) a;
+ if (v.validations() != null) {
+ for (Validations val : v.validations()) {
+ processValidationAnnotation(val, fieldName, methodName, result);
+ }
+ }
+ }
+ // Process single custom validator
+ else if (a instanceof ExpressionValidator) {
+ ExpressionValidator v = (ExpressionValidator) a;
+ ValidatorConfig temp = processExpressionValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process single custom validator
+ else if (a instanceof CustomValidator) {
+ CustomValidator v = (CustomValidator) a;
+ ValidatorConfig temp = processCustomValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process ConversionErrorFieldValidator
+ else if (a instanceof ConversionErrorFieldValidator) {
+ ConversionErrorFieldValidator v = (ConversionErrorFieldValidator) a;
+ ValidatorConfig temp = processConversionErrorFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+
+ }
+ // Process DateRangeFieldValidator
+ else if (a instanceof DateRangeFieldValidator) {
+ DateRangeFieldValidator v = (DateRangeFieldValidator) a;
+ ValidatorConfig temp = processDateRangeFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+
+ }
+ // Process EmailValidator
+ else if (a instanceof EmailValidator) {
+ EmailValidator v = (EmailValidator) a;
+ ValidatorConfig temp = processEmailValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process FieldExpressionValidator
+ else if (a instanceof FieldExpressionValidator) {
+ FieldExpressionValidator v = (FieldExpressionValidator) a;
+ ValidatorConfig temp = processFieldExpressionValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process IntRangeFieldValidator
+ else if (a instanceof IntRangeFieldValidator) {
+ IntRangeFieldValidator v = (IntRangeFieldValidator) a;
+ ValidatorConfig temp = processIntRangeFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process ShortRangeFieldValidator
+ else if (a instanceof ShortRangeFieldValidator) {
+ ShortRangeFieldValidator v = (ShortRangeFieldValidator) a;
+ ValidatorConfig temp = processShortRangeFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process DoubleRangeFieldValidator
+ else if (a instanceof DoubleRangeFieldValidator) {
+ DoubleRangeFieldValidator v = (DoubleRangeFieldValidator) a;
+ ValidatorConfig temp = processDoubleRangeFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process RequiredFieldValidator
+ else if (a instanceof RequiredFieldValidator) {
+ RequiredFieldValidator v = (RequiredFieldValidator) a;
+ ValidatorConfig temp = processRequiredFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process RequiredStringValidator
+ else if (a instanceof RequiredStringValidator) {
+ RequiredStringValidator v = (RequiredStringValidator) a;
+ ValidatorConfig temp = processRequiredStringValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process StringLengthFieldValidator
+ else if (a instanceof StringLengthFieldValidator) {
+ StringLengthFieldValidator v = (StringLengthFieldValidator) a;
+ ValidatorConfig temp = processStringLengthFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process UrlValidator
+ else if (a instanceof UrlValidator) {
+ UrlValidator v = (UrlValidator) a;
+ ValidatorConfig temp = processUrlValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+
+ }
+ // Process ConditionalVisitorFieldValidator
+ else if (a instanceof ConditionalVisitorFieldValidator) {
+ ConditionalVisitorFieldValidator v = (ConditionalVisitorFieldValidator) a;
+ ValidatorConfig temp = processConditionalVisitorFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process VisitorFieldValidator
+ else if (a instanceof VisitorFieldValidator) {
+ VisitorFieldValidator v = (VisitorFieldValidator) a;
+ ValidatorConfig temp = processVisitorFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ // Process RegexFieldValidator
+ else if (a instanceof RegexFieldValidator) {
+ RegexFieldValidator v = (RegexFieldValidator) a;
+ ValidatorConfig temp = processRegexFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private void processValidationAnnotation(Annotation a, String fieldName, String methodName, List result) {
+ Validations validations = (Validations) a;
+ CustomValidator[] cv = validations.customValidators();
+ if (cv != null) {
+ for (CustomValidator v : cv) {
+ ValidatorConfig temp = processCustomValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ ExpressionValidator[] ev = validations.expressions();
+ if (ev != null) {
+ for (ExpressionValidator v : ev) {
+ ValidatorConfig temp = processExpressionValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ ConversionErrorFieldValidator[] cef = validations.conversionErrorFields();
+ if (cef != null) {
+ for (ConversionErrorFieldValidator v : cef) {
+ ValidatorConfig temp = processConversionErrorFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ DateRangeFieldValidator[] drfv = validations.dateRangeFields();
+ if (drfv != null) {
+ for (DateRangeFieldValidator v : drfv) {
+ ValidatorConfig temp = processDateRangeFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ EmailValidator[] emv = validations.emails();
+ if (emv != null) {
+ for (EmailValidator v : emv) {
+ ValidatorConfig temp = processEmailValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ FieldExpressionValidator[] fev = validations.fieldExpressions();
+ if (fev != null) {
+ for (FieldExpressionValidator v : fev) {
+ ValidatorConfig temp = processFieldExpressionValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ IntRangeFieldValidator[] irfv = validations.intRangeFields();
+ if (irfv != null) {
+ for (IntRangeFieldValidator v : irfv) {
+ ValidatorConfig temp = processIntRangeFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ RegexFieldValidator[] rfv = validations.regexFields();
+ if (rfv != null) {
+ for (RegexFieldValidator v : rfv) {
+ ValidatorConfig temp = processRegexFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ RequiredFieldValidator[] rv = validations.requiredFields();
+ if (rv != null) {
+ for (RequiredFieldValidator v : rv) {
+ ValidatorConfig temp = processRequiredFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ RequiredStringValidator[] rsv = validations.requiredStrings();
+ if (rsv != null) {
+ for (RequiredStringValidator v : rsv) {
+ ValidatorConfig temp = processRequiredStringValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ StringLengthFieldValidator[] slfv = validations.stringLengthFields();
+ if (slfv != null) {
+ for (StringLengthFieldValidator v : slfv) {
+ ValidatorConfig temp = processStringLengthFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ UrlValidator[] uv = validations.urls();
+ if (uv != null) {
+ for (UrlValidator v : uv) {
+ ValidatorConfig temp = processUrlValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ ConditionalVisitorFieldValidator[] cvfv = validations.conditionalVisitorFields();
+ if (cvfv != null) {
+ for (ConditionalVisitorFieldValidator v : cvfv) {
+ ValidatorConfig temp = processConditionalVisitorFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ VisitorFieldValidator[] vfv = validations.visitorFields();
+ if (vfv != null) {
+ for (VisitorFieldValidator v : vfv) {
+ ValidatorConfig temp = processVisitorFieldValidatorAnnotation(v, fieldName, methodName);
+ if (temp != null) {
+ result.add(temp);
+ }
+ }
+ }
+ }
+
+ private ValidatorConfig processExpressionValidatorAnnotation(ExpressionValidator v, String fieldName, String methodName) {
+ String validatorType = "expression";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ }
+
+ params.put("expression", v.expression());
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processCustomValidatorAnnotation(CustomValidator v, String fieldName, String methodName) {
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+
+ String validatorType = v.type();
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+
+ Annotation[] recursedAnnotations = v.parameters();
+
+ if (recursedAnnotations != null) {
+ for (Annotation a2 : recursedAnnotations) {
+ if (a2 instanceof ValidationParameter) {
+ ValidationParameter parameter = (ValidationParameter) a2;
+ String parameterName = parameter.name();
+ String parameterValue = parameter.value();
+ params.put(parameterName, parameterValue);
+ }
+ }
+ }
+
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processRegexFieldValidatorAnnotation(RegexFieldValidator v, String fieldName, String methodName) {
+ String validatorType = "regex";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ params.put("regex", v.regex());
+ params.put("regexExpression", v.regexExpression());
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .addParam("trim", v.trim())
+ .addParam("trimExpression", v.trimExpression())
+ .addParam("caseSensitive", v.caseSensitive())
+ .addParam("caseSensitiveExpression", v.caseSensitiveExpression())
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processConditionalVisitorFieldValidatorAnnotation(ConditionalVisitorFieldValidator v, String fieldName, String methodName) {
+ String validatorType = "conditionalvisitor";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ params.put("expression", v.expression());
+ params.put("context", v.context());
+ params.put("appendPrefix", String.valueOf(v.appendPrefix()));
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+
+ private ValidatorConfig processVisitorFieldValidatorAnnotation(VisitorFieldValidator v, String fieldName, String methodName) {
+ String validatorType = "visitor";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ params.put("context", v.context());
+ params.put("appendPrefix", String.valueOf(v.appendPrefix()));
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processUrlValidatorAnnotation(UrlValidator v, String fieldName, String methodName) {
+ String validatorType = "url";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+ if (StringUtils.isNotEmpty(v.urlRegex())) {
+ params.put("urlRegex", v.urlRegex());
+ }
+ if (StringUtils.isNotEmpty(v.urlRegexExpression())) {
+ params.put("urlRegexExpression", v.urlRegexExpression());
+ }
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processStringLengthFieldValidatorAnnotation(StringLengthFieldValidator v, String fieldName, String methodName) {
+ String validatorType = "stringlength";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ if (StringUtils.isNotEmpty(v.maxLength())) {
+ params.put("maxLength", v.maxLength());
+ }
+ if (StringUtils.isNotEmpty(v.minLength())) {
+ params.put("minLength", v.minLength());
+ }
+ if (StringUtils.isNotEmpty(v.maxLengthExpression())) {
+ params.put("maxLengthExpression", v.maxLengthExpression());
+ }
+ if (StringUtils.isNotEmpty(v.minLengthExpression())) {
+ params.put("minLengthExpression", v.minLengthExpression());
+ }
+ if (StringUtils.isNotEmpty(v.trimExpression())){
+ params.put("trimExpression", v.trimExpression());
+ } else {
+ params.put("trim", String.valueOf(v.trim()));
+ }
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private Date parseDateString(String value, String format) {
+
+ SimpleDateFormat d0 = null;
+ if (StringUtils.isNotEmpty(format)) {
+ d0 = new SimpleDateFormat(format);
+ }
+ SimpleDateFormat d1 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, Locale.getDefault());
+ SimpleDateFormat d2 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, Locale.getDefault());
+ SimpleDateFormat d3 = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault());
+ SimpleDateFormat[] dfs = (d0 != null ? new SimpleDateFormat[]{d0, d1, d2, d3} : new SimpleDateFormat[]{d1, d2, d3});
+ for (SimpleDateFormat df : dfs)
+ try {
+ Date check = df.parse(value);
+ if (check != null) {
+ return check;
+ }
+ } catch (ParseException ignore) {
+ }
+ return null;
+ }
+
+ private ValidatorConfig processRequiredStringValidatorAnnotation(RequiredStringValidator v, String fieldName, String methodName) {
+ String validatorType = "requiredstring";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ params.put("trim", String.valueOf(v.trim()));
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageParams(v.messageParams())
+ .messageKey(v.key())
+ .build();
+ }
+
+ private ValidatorConfig processRequiredFieldValidatorAnnotation(RequiredFieldValidator v, String fieldName, String methodName) {
+ String validatorType = "required";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processIntRangeFieldValidatorAnnotation(IntRangeFieldValidator v, String fieldName, String methodName) {
+ String validatorType = "int";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ if (v.min() != null && v.min().length() > 0) {
+ params.put("min", v.min());
+ }
+ if (v.max() != null && v.max().length() > 0) {
+ params.put("max", v.max());
+ }
+ if (StringUtils.isNotEmpty(v.maxExpression())) {
+ params.put("maxExpression", v.maxExpression());
+ }
+ if (StringUtils.isNotEmpty(v.minExpression())) {
+ params.put("minExpression", v.minExpression());
+ }
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processShortRangeFieldValidatorAnnotation(ShortRangeFieldValidator v, String fieldName, String methodName) {
+ String validatorType = "short";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ if (StringUtils.isNotEmpty(v.min())) {
+ params.put("min", v.min());
+ }
+ if (StringUtils.isNotEmpty(v.max())) {
+ params.put("max", v.max());
+ }
+ if (StringUtils.isNotEmpty(v.maxExpression())) {
+ params.put("maxExpression", v.maxExpression());
+ }
+ if (StringUtils.isNotEmpty(v.minExpression())) {
+ params.put("minExpression", v.minExpression());
+ }
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processDoubleRangeFieldValidatorAnnotation(DoubleRangeFieldValidator v, String fieldName, String methodName) {
+ String validatorType = "double";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (v.fieldName() != null && v.fieldName().length() > 0) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ if (v.minInclusive() != null && v.minInclusive().length() > 0) {
+ params.put("minInclusive", v.minInclusive());
+ }
+ if (v.maxInclusive() != null && v.maxInclusive().length() > 0) {
+ params.put("maxInclusive", v.maxInclusive());
+ }
+
+ if (v.minExclusive() != null && v.minExclusive().length() > 0) {
+ params.put("minExclusive", v.minExclusive());
+ }
+ if (v.maxExclusive() != null && v.maxExclusive().length() > 0) {
+ params.put("maxExclusive", v.maxExclusive());
+ }
+
+ if (StringUtils.isNotEmpty(v.minInclusiveExpression())) {
+ params.put("minInclusiveExpression", v.minInclusiveExpression());
+ }
+ if (StringUtils.isNotEmpty(v.maxInclusiveExpression())) {
+ params.put("maxInclusiveExpression", v.maxInclusiveExpression());
+ }
+
+ if (StringUtils.isNotEmpty(v.minExclusiveExpression())) {
+ params.put("minExclusiveExpression", v.minExclusiveExpression());
+ }
+ if (StringUtils.isNotEmpty(v.maxExclusiveExpression())) {
+ params.put("maxExclusiveExpression", v.maxExclusiveExpression());
+ }
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processFieldExpressionValidatorAnnotation(FieldExpressionValidator v, String fieldName, String methodName) {
+ String validatorType = "fieldexpression";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ params.put("expression", v.expression());
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processEmailValidatorAnnotation(EmailValidator v, String fieldName, String methodName) {
+ String validatorType = "email";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processDateRangeFieldValidatorAnnotation(DateRangeFieldValidator v, String fieldName, String methodName) {
+ String validatorType = "date";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (v.fieldName() != null && v.fieldName().length() > 0) {
+ params.put("fieldName", v.fieldName());
+ }
+ if (v.min() != null && v.min().length() > 0) {
+ final Date minDate = parseDateString(v.min(), v.dateFormat());
+ params.put("min", minDate == null ? v.min() : minDate);
+ }
+ if (v.max() != null && v.max().length() > 0) {
+ final Date maxDate = parseDateString(v.max(), v.dateFormat());
+ params.put("max", maxDate == null ? v.max() : maxDate);
+ }
+
+ if (StringUtils.isNotEmpty(v.minExpression())) {
+ params.put("minExpression", v.minExpression());
+ }
+ if (StringUtils.isNotEmpty(v.maxExpression())) {
+ params.put("maxExpression", v.maxExpression());
+ }
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ private ValidatorConfig processConversionErrorFieldValidatorAnnotation(ConversionErrorFieldValidator v, String fieldName, String methodName) {
+ String validatorType = "conversion";
+
+ Map params = new HashMap<>();
+
+ if (fieldName != null) {
+ params.put("fieldName", fieldName);
+ } else if (StringUtils.isNotEmpty(v.fieldName())) {
+ params.put("fieldName", v.fieldName());
+ }
+
+ validatorFactory.lookupRegisteredValidatorType(validatorType);
+ return new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .addParam("methodName", methodName)
+ .addParam("repopulateField", v.repopulateField())
+ .shortCircuit(v.shortCircuit())
+ .defaultMessage(v.message())
+ .messageKey(v.key())
+ .messageParams(v.messageParams())
+ .build();
+ }
+
+ public List buildAnnotationClassValidatorConfigs(Class aClass) {
+
+ List result = new ArrayList<>();
+
+ List temp = processAnnotations(aClass);
+ if (temp != null) {
+ result.addAll(temp);
+ }
+
+ Method[] methods = aClass.getDeclaredMethods();
+
+ if (methods != null) {
+ for (Method method : methods) {
+ temp = processAnnotations(method);
+ if (temp != null) {
+ result.addAll(temp);
+ }
+ }
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Returns the property name for a method.
+ * This method is independant from property fields.
+ *
+ * @param method The method to get the property name for.
+ * @return the property name for given method; null if non could be resolved.
+ */
+ public String resolvePropertyName(Method method) {
+
+ Matcher matcher = SETTER_PATTERN.matcher(method.getName());
+ if (matcher.matches() && method.getParameterTypes().length == 1) {
+ String raw = matcher.group(1);
+ return raw.substring(0, 1).toLowerCase() + raw.substring(1);
+ }
+
+ matcher = GETTER_PATTERN.matcher(method.getName());
+ if (matcher.matches() && method.getParameterTypes().length == 0) {
+ String raw = matcher.group(2);
+ return raw.substring(0, 1).toLowerCase() + raw.substring(1);
+ }
+
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManager.java b/core/src/main/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManager.java
new file mode 100644
index 0000000..d9d7401
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManager.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ *
+ * 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 com.opensymphony.xwork2.validator;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.FileManager;
+import com.opensymphony.xwork2.FileManagerFactory;
+import com.opensymphony.xwork2.XWorkConstants;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.ClassLoaderUtil;
+import com.opensymphony.xwork2.util.ValueStack;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+
+
+/**
+ * This is the entry point into XWork's rule-based validation framework.
+ *
+ * Validation rules are specified in XML configuration files named className-contextName-validation.xml
where
+ * className is the name of the class the configuration is for and -contextName is optional
+ * (contextName is an arbitrary key that is used to look up additional validation rules for a
+ * specific context).
+ *
+ * @author Jason Carreira
+ * @author Mark Woon
+ * @author James House
+ * @author Rainer Hermanns
+ */
+public class DefaultActionValidatorManager implements ActionValidatorManager {
+
+ /** The file suffix for any validation file. */
+ protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
+
+ private final Map> validatorCache = Collections.synchronizedMap(new HashMap>());
+ private final Map> validatorFileCache = Collections.synchronizedMap(new HashMap>());
+ private final Logger LOG = LogManager.getLogger(DefaultActionValidatorManager.class);
+ private ValidatorFactory validatorFactory;
+ private ValidatorFileParser validatorFileParser;
+ private FileManager fileManager;
+ private boolean reloadingConfigs;
+
+ @Inject
+ public void setValidatorFileParser(ValidatorFileParser parser) {
+ this.validatorFileParser = parser;
+ }
+
+ @Inject
+ public void setValidatorFactory(ValidatorFactory fac) {
+ this.validatorFactory = fac;
+ }
+
+ @Inject
+ public void setFileManagerFactory(FileManagerFactory fileManagerFactory) {
+ this.fileManager = fileManagerFactory.getFileManager();
+ }
+
+ @Inject(value = XWorkConstants.RELOAD_XML_CONFIGURATION, required = false)
+ public void setReloadingConfigs(String reloadingConfigs) {
+ this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
+ }
+
+ public synchronized List getValidators(Class clazz, String context) {
+ return getValidators(clazz, context, null);
+ }
+
+ public synchronized List getValidators(Class clazz, String context, String method) {
+ final String validatorKey = buildValidatorKey(clazz, context);
+
+ if (validatorCache.containsKey(validatorKey)) {
+ if (reloadingConfigs) {
+ validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
+ }
+ } else {
+ validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
+ }
+ ValueStack stack = ActionContext.getContext().getValueStack();
+
+ // get the set of validator configs
+ List cfgs = validatorCache.get(validatorKey);
+
+ // create clean instances of the validators for the caller's use
+ ArrayList validators = new ArrayList<>(cfgs.size());
+ for (ValidatorConfig cfg : cfgs) {
+ if (method == null || method.equals(cfg.getParams().get("methodName"))) {
+ Validator validator = validatorFactory.getValidator(cfg);
+ validator.setValidatorType(cfg.getType());
+ validator.setValueStack(stack);
+ validators.add(validator);
+ }
+ }
+ return validators;
+ }
+
+ public void validate(Object object, String context) throws ValidationException {
+ validate(object, context, (String) null);
+ }
+
+ public void validate(Object object, String context, String method) throws ValidationException {
+ ValidatorContext validatorContext = new DelegatingValidatorContext(object);
+ validate(object, context, validatorContext, method);
+ }
+
+ public void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException {
+ validate(object, context, validatorContext, null);
+ }
+
+ public void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException {
+ List validators = getValidators(object.getClass(), context, method);
+ Set shortcircuitedFields = null;
+
+ for (final Validator validator : validators) {
+ try {
+ validator.setValidatorContext(validatorContext);
+
+ LOG.debug("Running validator: {} for object {} and method {}", validator, object, method);
+
+ FieldValidator fValidator = null;
+ String fullFieldName = null;
+
+ if (validator instanceof FieldValidator) {
+ fValidator = (FieldValidator) validator;
+ fullFieldName = fValidator.getValidatorContext().getFullFieldName(fValidator.getFieldName());
+
+ if ((shortcircuitedFields != null) && shortcircuitedFields.contains(fullFieldName)) {
+ LOG.debug("Short-circuited, skipping");
+ continue;
+ }
+ }
+
+ if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit()) {
+ // get number of existing errors
+ List errs = null;
+
+ if (fValidator != null) {
+ if (validatorContext.hasFieldErrors()) {
+ Collection fieldErrors = validatorContext.getFieldErrors().get(fullFieldName);
+
+ if (fieldErrors != null) {
+ errs = new ArrayList<>(fieldErrors);
+ }
+ }
+ } else if (validatorContext.hasActionErrors()) {
+ Collection actionErrors = validatorContext.getActionErrors();
+
+ if (actionErrors != null) {
+ errs = new ArrayList(actionErrors);
+ }
+ }
+
+ validator.validate(object);
+
+ if (fValidator != null) {
+ if (validatorContext.hasFieldErrors()) {
+ Collection errCol = validatorContext.getFieldErrors().get(fullFieldName);
+
+ if ((errCol != null) && !errCol.equals(errs)) {
+ LOG.debug("Short-circuiting on field validation");
+
+ if (shortcircuitedFields == null) {
+ shortcircuitedFields = new TreeSet<>();
+ }
+
+ shortcircuitedFields.add(fullFieldName);
+ }
+ }
+ } else if (validatorContext.hasActionErrors()) {
+ Collection errCol = validatorContext.getActionErrors();
+
+ if ((errCol != null) && !errCol.equals(errs)) {
+ LOG.debug("Short-circuiting");
+ break;
+ }
+ }
+ continue;
+ }
+
+ validator.validate(object);
+ }
+ finally {
+ validator.setValidatorContext(null);
+ }
+ }
+ }
+
+ /**
+ * Builds a key for validators - used when caching validators.
+ *
+ * @param clazz the action.
+ * @param context the action's context.
+ * @return a validator key which is the class name plus context.
+ */
+ protected static String buildValidatorKey(Class clazz, String context) {
+ StringBuilder sb = new StringBuilder(clazz.getName());
+ sb.append("/");
+ sb.append(context);
+ return sb.toString();
+ }
+
+ private List buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
+ String fileName = aClass.getName().replace('.', '/') + "-" + context + VALIDATION_CONFIG_SUFFIX;
+
+ return loadFile(fileName, aClass, checkFile);
+ }
+
+ private List buildClassValidatorConfigs(Class aClass, boolean checkFile) {
+ String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
+
+ return loadFile(fileName, aClass, checkFile);
+ }
+
+ /**
+ * This method 'collects' all the validator configurations for a given
+ * action invocation.
+ *
+ * It will traverse up the class hierarchy looking for validators for every super class
+ * and directly implemented interface of the current action, as well as adding validators for
+ * any alias of this invocation. Nifty!
+ *
+ * Given the following class structure:
+ *
+ * interface Thing;
+ * interface Animal extends Thing;
+ * interface Quadraped extends Animal;
+ * class AnimalImpl implements Animal;
+ * class QuadrapedImpl extends AnimalImpl implements Quadraped;
+ * class Dog extends QuadrapedImpl;
+ *
+ *
+ * This method will look for the following config files for Dog:
+ *
+ * Animal
+ * Animal-context
+ * AnimalImpl
+ * AnimalImpl-context
+ * Quadraped
+ * Quadraped-context
+ * QuadrapedImpl
+ * QuadrapedImpl-context
+ * Dog
+ * Dog-context
+ *
+ *
+ * Note that the validation rules for Thing is never looked for because no class in the
+ * hierarchy directly implements Thing.
+ *
+ * @param clazz the Class to look up validators for.
+ * @param context the context to use when looking up validators.
+ * @param checkFile true if the validation config file should be checked to see if it has been
+ * updated.
+ * @param checked the set of previously checked class-contexts, null if none have been checked
+ * @return a list of validator configs for the given class and context.
+ */
+ private List buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set checked) {
+ List validatorConfigs = new ArrayList<>();
+
+ if (checked == null) {
+ checked = new TreeSet();
+ } else if (checked.contains(clazz.getName())) {
+ return validatorConfigs;
+ }
+
+ if (clazz.isInterface()) {
+ for (Class anInterface : clazz.getInterfaces()) {
+ validatorConfigs.addAll(buildValidatorConfigs(anInterface, context, checkFile, checked));
+ }
+ } else {
+ if (!clazz.equals(Object.class)) {
+ validatorConfigs.addAll(buildValidatorConfigs(clazz.getSuperclass(), context, checkFile, checked));
+ }
+ }
+
+ // look for validators for implemented interfaces
+ for (Class anInterface1 : clazz.getInterfaces()) {
+ if (checked.contains(anInterface1.getName())) {
+ continue;
+ }
+
+ validatorConfigs.addAll(buildClassValidatorConfigs(anInterface1, checkFile));
+
+ if (context != null) {
+ validatorConfigs.addAll(buildAliasValidatorConfigs(anInterface1, context, checkFile));
+ }
+
+ checked.add(anInterface1.getName());
+ }
+
+ validatorConfigs.addAll(buildClassValidatorConfigs(clazz, checkFile));
+
+ if (context != null) {
+ validatorConfigs.addAll(buildAliasValidatorConfigs(clazz, context, checkFile));
+ }
+
+ checked.add(clazz.getName());
+
+ return validatorConfigs;
+ }
+
+ private List loadFile(String fileName, Class clazz, boolean checkFile) {
+ List retList = Collections.emptyList();
+ URL fileUrl = ClassLoaderUtil.getResource(fileName, clazz);
+ if ((checkFile && fileManager.fileNeedsReloading(fileUrl)) || !validatorFileCache.containsKey(fileName)) {
+ try (InputStream is = fileManager.loadFile(fileUrl)) {
+ if (is != null) {
+ retList = new ArrayList<>(validatorFileParser.parseActionValidatorConfigs(validatorFactory, is, fileName));
+ }
+ } catch (IOException e) {
+ LOG.error("Caught exception while loading file {}", fileName, e);
+ }
+
+ validatorFileCache.put(fileName, retList);
+ } else {
+ retList = validatorFileCache.get(fileName);
+ }
+
+ return retList;
+ }
+}
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/DefaultValidatorFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/DefaultValidatorFactory.java b/core/src/main/java/com/opensymphony/xwork2/validator/DefaultValidatorFactory.java
new file mode 100644
index 0000000..d4d7ff7
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/validator/DefaultValidatorFactory.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * 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 com.opensymphony.xwork2.validator;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.ClassLoaderUtil;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+
+/**
+ * Default validator factory
+ *
+ * @version $Date$ $Id$
+ * @author Jason Carreira
+ * @author James House
+ */
+public class DefaultValidatorFactory implements ValidatorFactory {
+
+ protected Map validators = new HashMap<>();
+ private static Logger LOG = LogManager.getLogger(DefaultValidatorFactory.class);
+ protected ObjectFactory objectFactory;
+ protected ValidatorFileParser validatorFileParser;
+
+ @Inject
+ public DefaultValidatorFactory(@Inject ObjectFactory objectFactory, @Inject ValidatorFileParser parser) {
+ this.objectFactory = objectFactory;
+ this.validatorFileParser = parser;
+ parseValidators();
+ }
+
+ public Validator getValidator(ValidatorConfig cfg) {
+
+ String className = lookupRegisteredValidatorType(cfg.getType());
+
+ Validator validator;
+
+ try {
+ // instantiate the validator, and set configured parameters
+ //todo - can this use the ThreadLocal?
+ validator = objectFactory.buildValidator(className, cfg.getParams(), ActionContext.getContext().getContextMap());
+ } catch (Exception e) {
+ final String msg = "There was a problem creating a Validator of type " + className + " : caused by " + e.getMessage();
+ throw new XWorkException(msg, e, cfg);
+ }
+
+ // set other configured properties
+ validator.setMessageKey(cfg.getMessageKey());
+ validator.setDefaultMessage(cfg.getDefaultMessage());
+ validator.setMessageParameters(cfg.getMessageParams());
+ if (validator instanceof ShortCircuitableValidator) {
+ ((ShortCircuitableValidator) validator).setShortCircuit(cfg.isShortCircuit());
+ }
+
+ return validator;
+ }
+
+ public void registerValidator(String name, String className) {
+ LOG.debug("Registering validator of class {} with name {}", className, name);
+ validators.put(name, className);
+ }
+
+ public String lookupRegisteredValidatorType(String name) {
+ // lookup the validator class mapped to the type name
+ String className = validators.get(name);
+
+ if (className == null) {
+ throw new IllegalArgumentException("There is no validator class mapped to the name " + name);
+ }
+
+ return className;
+ }
+
+ private void parseValidators() {
+ LOG.debug("Loading validator definitions.");
+
+ List files = new ArrayList<>();
+ try {
+ // Get custom validator configurations via the classpath
+ Iterator urls = ClassLoaderUtil.getResources("", DefaultValidatorFactory.class, false);
+ while (urls.hasNext()) {
+ URL u = urls.next();
+ try {
+ URI uri = new URI(u.toExternalForm().replaceAll(" ", "%20"));
+ if (!uri.isOpaque() && "file".equalsIgnoreCase(uri.getScheme())) {
+ File f = new File(uri);
+ FilenameFilter filter = new FilenameFilter() {
+ public boolean accept(File file, String fileName) {
+ return fileName.contains("-validators.xml");
+ }
+ };
+ // First check if this is a directory
+ // If yes, then just do a "list" to get all files in this directory
+ // and match the filenames with *-validators.xml. If the filename
+ // matches then add to the list of files to be parsed
+ if (f.isDirectory()) {
+ try {
+ File[] ff = f.listFiles(filter);
+ if ( ff != null && ff.length > 0) {
+ files.addAll(Arrays.asList(ff));
+ }
+ } catch (SecurityException se) {
+ LOG.error("Security Exception while accessing directory '{}'", f, se);
+ }
+
+ } else {
+ // If this is not a directory, then get hold of the inputstream.
+ // If its not a ZipInputStream, then create a ZipInputStream out
+ // of it. The intention is to allow nested jar files to be scanned
+ // for *-validators.xml.
+ // Ex: struts-app.jar -> MyApp.jar -> Login-validators.xml should be
+ // parsed and loaded.
+ ZipInputStream zipInputStream = null;
+ try (InputStream inputStream = u.openStream()) {
+ if (inputStream instanceof ZipInputStream) {
+ zipInputStream = (ZipInputStream) inputStream;
+ } else {
+ zipInputStream = new ZipInputStream(inputStream);
+ }
+ ZipEntry zipEntry = zipInputStream.getNextEntry();
+ while (zipEntry != null) {
+ if (zipEntry.getName().endsWith("-validators.xml")) {
+ LOG.trace("Adding validator {}", zipEntry.getName());
+ files.add(new File(zipEntry.getName()));
+ }
+ zipEntry = zipInputStream.getNextEntry();
+ }
+ } finally {
+ //cleanup
+ if (zipInputStream != null) {
+ zipInputStream.close();
+ }
+ }
+ }
+ }
+ } catch (Exception ex) {
+ LOG.error("Unable to load {}", u, ex);
+ }
+ }
+ } catch (IOException e) {
+ throw new ConfigurationException("Unable to parse validators", e);
+ }
+
+ // Parse default validator configurations
+ String resourceName = "com/opensymphony/xwork2/validator/validators/default.xml";
+ retrieveValidatorConfiguration(resourceName);
+
+ // Overwrite and extend defaults with application specific validator configurations
+ resourceName = "validators.xml";
+ retrieveValidatorConfiguration(resourceName);
+
+ // Add custom (plugin) specific validator configurations
+ for (File file : files) {
+ retrieveValidatorConfiguration(file.getName());
+ }
+ }
+
+ private void retrieveValidatorConfiguration(String resourceName) {
+ InputStream is = ClassLoaderUtil.getResourceAsStream(resourceName, DefaultValidatorFactory.class);
+ if (is != null) {
+ validatorFileParser.parseValidatorDefinitions(validators, is, resourceName);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/DefaultValidatorFileParser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/DefaultValidatorFileParser.java b/core/src/main/java/com/opensymphony/xwork2/validator/DefaultValidatorFileParser.java
new file mode 100644
index 0000000..b640996
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/validator/DefaultValidatorFileParser.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * 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 com.opensymphony.xwork2.validator;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.config.providers.XmlHelper;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.DomHelper;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+import org.w3c.dom.*;
+import org.xml.sax.InputSource;
+
+import java.io.InputStream;
+import java.util.*;
+
+
+/**
+ * Parse the validation file. (eg. MyAction-validation.xml, MyAction-actionAlias-validation.xml)
+ * to return a List of ValidatorConfig encapsulating the validator information.
+ *
+ * @author Jason Carreira
+ * @author James House
+ * @author tm_jee ( tm_jee (at) yahoo.co.uk )
+ * @author Rob Harrop
+ * @author Rene Gielen
+ * @author Martin Gilday
+ *
+ * @see com.opensymphony.xwork2.validator.ValidatorConfig
+ */
+public class DefaultValidatorFileParser implements ValidatorFileParser {
+
+ private static Logger LOG = LogManager.getLogger(DefaultValidatorFileParser.class);
+
+ static final String DEFAULT_MULTI_TEXTVALUE_SEPARATOR = " ";
+ static final String MULTI_TEXTVALUE_SEPARATOR_CONFIG_KEY = "xwork.validatorfileparser.multi_textvalue_separator";
+
+ private ObjectFactory objectFactory;
+ private String multiTextvalueSeparator=DEFAULT_MULTI_TEXTVALUE_SEPARATOR;
+
+ @Inject(value=MULTI_TEXTVALUE_SEPARATOR_CONFIG_KEY, required = false)
+ public void setMultiTextvalueSeparator(String type) {
+ multiTextvalueSeparator = type;
+ }
+
+ public String getMultiTextvalueSeparator() {
+ return multiTextvalueSeparator;
+ }
+
+ @Inject
+ public void setObjectFactory(ObjectFactory fac) {
+ this.objectFactory = fac;
+ }
+
+ public List parseActionValidatorConfigs(ValidatorFactory validatorFactory, InputStream is, final String resourceName) {
+ List validatorCfgs = new ArrayList();
+
+ InputSource in = new InputSource(is);
+ in.setSystemId(resourceName);
+
+ Map dtdMappings = new HashMap();
+ dtdMappings.put("-//Apache Struts//XWork Validator 1.0//EN", "xwork-validator-1.0.dtd");
+ dtdMappings.put("-//Apache Struts//XWork Validator 1.0.2//EN", "xwork-validator-1.0.2.dtd");
+ dtdMappings.put("-//Apache Struts//XWork Validator 1.0.3//EN", "xwork-validator-1.0.3.dtd");
+ dtdMappings.put("-//Apache Struts//XWork Validator Config 1.0//EN", "xwork-validator-config-1.0.dtd");
+
+ Document doc = DomHelper.parse(in, dtdMappings);
+
+ if (doc != null) {
+ NodeList fieldNodes = doc.getElementsByTagName("field");
+
+ // BUG: xw-305: Let validator be parsed first and hence added to
+ // the beginning of list and therefore evaluated first, so short-circuting
+ // it will not cause field-level validator to be kicked off.
+ {
+ NodeList validatorNodes = doc.getElementsByTagName("validator");
+ addValidatorConfigs(validatorFactory, validatorNodes, new HashMap(), validatorCfgs);
+ }
+
+ for (int i = 0; i < fieldNodes.getLength(); i++) {
+ Element fieldElement = (Element) fieldNodes.item(i);
+ String fieldName = fieldElement.getAttribute("name");
+ Map extraParams = new HashMap();
+ extraParams.put("fieldName", fieldName);
+
+ NodeList validatorNodes = fieldElement.getElementsByTagName("field-validator");
+ addValidatorConfigs(validatorFactory, validatorNodes, extraParams, validatorCfgs);
+ }
+ }
+
+ return validatorCfgs;
+ }
+
+
+ public void parseValidatorDefinitions(Map validators, InputStream is, String resourceName) {
+
+ InputSource in = new InputSource(is);
+ in.setSystemId(resourceName);
+
+ Map dtdMappings = new HashMap();
+ dtdMappings.put("-//Apache Struts//XWork Validator Config 1.0//EN", "xwork-validator-config-1.0.dtd");
+ dtdMappings.put("-//Apache Struts//XWork Validator Definition 1.0//EN", "xwork-validator-definition-1.0.dtd");
+
+ Document doc = DomHelper.parse(in, dtdMappings);
+
+ if (doc != null) {
+ NodeList nodes = doc.getElementsByTagName("validator");
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Element validatorElement = (Element) nodes.item(i);
+ String name = validatorElement.getAttribute("name");
+ String className = validatorElement.getAttribute("class");
+
+ try {
+ // catch any problems here
+ objectFactory.buildValidator(className, new HashMap(), ActionContext.getContext().getContextMap());
+ validators.put(name, className);
+ } catch (Exception e) {
+ throw new ConfigurationException("Unable to load validator class " + className, e, validatorElement);
+ }
+ }
+ }
+ }
+
+ /**
+ * Extract trimmed text value from the given DOM element, ignoring XML comments. Appends all CharacterData nodes
+ * and EntityReference nodes into a single String value, excluding Comment nodes.
+ * This method is based on a method originally found in DomUtils class of Springframework.
+ *
+ * @see org.w3c.dom.CharacterData
+ * @see org.w3c.dom.EntityReference
+ * @see org.w3c.dom.Comment
+ */
+ public String getTextValue(Element valueEle) {
+ StringBuilder value = new StringBuilder();
+ NodeList nl = valueEle.getChildNodes();
+ boolean firstCDataFound = false;
+ for (int i = 0; i < nl.getLength(); i++) {
+ Node item = nl.item(i);
+ if ((item instanceof CharacterData && !(item instanceof Comment)) || item instanceof EntityReference) {
+ final String nodeValue = item.getNodeValue();
+ if (nodeValue != null) {
+ if (firstCDataFound) {
+ value.append(getMultiTextvalueSeparator());
+ } else {
+ firstCDataFound = true;
+ }
+ value.append(nodeValue.trim());
+ }
+ }
+ }
+ return value.toString().trim();
+ }
+
+ private void addValidatorConfigs(ValidatorFactory factory, NodeList validatorNodes, Map extraParams, List validatorCfgs) {
+ for (int j = 0; j < validatorNodes.getLength(); j++) {
+ Element validatorElement = (Element) validatorNodes.item(j);
+ String validatorType = validatorElement.getAttribute("type");
+ Map params = new HashMap(extraParams);
+
+ params.putAll(XmlHelper.getParams(validatorElement));
+
+ // ensure that the type is valid...
+ try {
+ factory.lookupRegisteredValidatorType(validatorType);
+ } catch (IllegalArgumentException ex) {
+ throw new ConfigurationException("Invalid validation type: " + validatorType, validatorElement);
+ }
+
+ ValidatorConfig.Builder vCfg = new ValidatorConfig.Builder(validatorType)
+ .addParams(params)
+ .location(DomHelper.getLocationObject(validatorElement))
+ .shortCircuit(Boolean.valueOf(validatorElement.getAttribute("short-circuit")).booleanValue());
+
+ NodeList messageNodes = validatorElement.getElementsByTagName("message");
+ Element messageElement = (Element) messageNodes.item(0);
+
+ final Node defaultMessageNode = messageElement.getFirstChild();
+ String defaultMessage = (defaultMessageNode == null) ? "" : defaultMessageNode.getNodeValue();
+ vCfg.defaultMessage(defaultMessage);
+
+ Map messageParams = XmlHelper.getParams(messageElement);
+ String key = messageElement.getAttribute("key");
+
+
+ if ((key != null) && (key.trim().length() > 0)) {
+ vCfg.messageKey(key);
+
+ // Get the default message when pattern 2 is used. We are only interested in the
+ // i18n message parameters when an i18n message key is specified.
+ // pattern 1:
+ // Default message
+ // pattern 2:
+ //
+ // 'param1'
+ // 'param2'
+ // sortedMessageParameters = new TreeMap();
+ for (Map.Entry messageParamEntry : messageParams.entrySet()) {
+
+ try {
+ int _order = Integer.parseInt(messageParamEntry.getKey());
+ sortedMessageParameters.put(Integer.valueOf(_order), messageParamEntry.getValue().toString());
+ }
+ catch (NumberFormatException e) {
+ // ignore if its not numeric.
+ }
+ }
+ vCfg.messageParams(sortedMessageParameters.values().toArray(new String[sortedMessageParameters.values().size()]));
+ } else {
+ if (messageParams != null && (messageParams.size() > 0)) {
+ // we are i18n message parameters defined but no i18n message,
+ // let's warn the user.
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("validator of type ["+validatorType+"] have i18n message parameters defined but no i18n message key, it's parameters will be ignored");
+ }
+ }
+ }
+
+ validatorCfgs.add(vCfg.build());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java b/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java
new file mode 100644
index 0000000..5b11921
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ *
+ * 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 com.opensymphony.xwork2.validator;
+
+import com.opensymphony.xwork2.*;
+import com.opensymphony.xwork2.util.ValueStack;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.*;
+
+
+/**
+ * A default implementation of the {@link ValidatorContext} interface.
+ *
+ * @author Jason Carreira
+ * @author Rainer Hermanns
+ */
+public class DelegatingValidatorContext implements ValidatorContext {
+
+ private LocaleProvider localeProvider;
+ private TextProvider textProvider;
+ private ValidationAware validationAware;
+
+ /**
+ * Creates a new validation context given a ValidationAware object, and a text and locale provider. These objects
+ * are used internally to set errors and get and set error text.
+ */
+ public DelegatingValidatorContext(ValidationAware validationAware, TextProvider textProvider,
+ LocaleProvider localeProvider) {
+ this.textProvider = textProvider;
+ this.validationAware = validationAware;
+ this.localeProvider = localeProvider;
+ }
+
+ /**
+ * Creates a new validation context given an object - usually an Action. The internal objects
+ * (validation aware instance and a locale and text provider) are created based on the given action.
+ *
+ * @param object the object to use for validation (usually an Action).
+ */
+ public DelegatingValidatorContext(Object object) {
+ this.localeProvider = makeLocaleProvider(object);
+ this.validationAware = makeValidationAware(object);
+ this.textProvider = makeTextProvider(object, localeProvider);
+ }
+
+ /**
+ * Create a new validation context given a Class definition. The locale provider, text provider and
+ * the validation context are created based on the class.
+ *
+ * @param clazz the class to initialize the context with.
+ */
+ public DelegatingValidatorContext(Class clazz) {
+ localeProvider = new ActionContextLocaleProvider();
+ textProvider = new TextProviderFactory().createInstance(clazz, localeProvider);
+ validationAware = new LoggingValidationAware(clazz);
+ }
+
+ public void setActionErrors(Collection errorMessages) {
+ validationAware.setActionErrors(errorMessages);
+ }
+
+ public Collection getActionErrors() {
+ return validationAware.getActionErrors();
+ }
+
+ public void setActionMessages(Collection messages) {
+ validationAware.setActionMessages(messages);
+ }
+
+ public Collection getActionMessages() {
+ return validationAware.getActionMessages();
+ }
+
+ public void setFieldErrors(Map> errorMap) {
+ validationAware.setFieldErrors(errorMap);
+ }
+
+ public Map> getFieldErrors() {
+ return validationAware.getFieldErrors();
+ }
+
+ public String getFullFieldName(String fieldName) {
+ return fieldName;
+ }
+
+ public Locale getLocale() {
+ return localeProvider.getLocale();
+ }
+
+ public boolean hasKey(String key) {
+ return textProvider.hasKey(key);
+ }
+
+ public String getText(String aTextName) {
+ return textProvider.getText(aTextName);
+ }
+
+ public String getText(String aTextName, String defaultValue) {
+ return textProvider.getText(aTextName, defaultValue);
+ }
+
+ public String getText(String aTextName, String defaultValue, String obj) {
+ return textProvider.getText(aTextName, defaultValue, obj);
+ }
+
+ public String getText(String aTextName, List> args) {
+ return textProvider.getText(aTextName, args);
+ }
+
+ public String getText(String key, String[] args) {
+ return textProvider.getText(key, args);
+ }
+
+ public String getText(String aTextName, String defaultValue, List> args) {
+ return textProvider.getText(aTextName, defaultValue, args);
+ }
+
+ public String getText(String key, String defaultValue, String[] args) {
+ return textProvider.getText(key, defaultValue, args);
+ }
+
+ public ResourceBundle getTexts(String aBundleName) {
+ return textProvider.getTexts(aBundleName);
+ }
+
+ public String getText(String key, String defaultValue, List> args, ValueStack stack) {
+ return textProvider.getText(key, defaultValue, args, stack);
+ }
+
+ public String getText(String key, String defaultValue, String[] args, ValueStack stack) {
+ return textProvider.getText(key, defaultValue, args, stack);
+ }
+
+ public ResourceBundle getTexts() {
+ return textProvider.getTexts();
+ }
+
+ public void addActionError(String anErrorMessage) {
+ validationAware.addActionError(anErrorMessage);
+ }
+
+ public void addActionMessage(String aMessage) {
+ validationAware.addActionMessage(aMessage);
+ }
+
+ public void addFieldError(String fieldName, String errorMessage) {
+ validationAware.addFieldError(fieldName, errorMessage);
+ }
+
+ public boolean hasActionErrors() {
+ return validationAware.hasActionErrors();
+ }
+
+ public boolean hasActionMessages() {
+ return validationAware.hasActionMessages();
+ }
+
+ public boolean hasErrors() {
+ return validationAware.hasErrors();
+ }
+
+ public boolean hasFieldErrors() {
+ return validationAware.hasFieldErrors();
+ }
+
+ public static TextProvider makeTextProvider(Object object, LocaleProvider localeProvider) {
+ // the object argument passed through here will most probably be an ActionSupport decendant which does
+ // implements TextProvider.
+ if (object != null && object instanceof DelegatingValidatorContext) {
+ return ((DelegatingValidatorContext) object).getTextProvider();
+ } else if (localeProvider != null && localeProvider instanceof DelegatingValidatorContext) {
+ return ((DelegatingValidatorContext) localeProvider).getTextProvider();
+ }
+
+ if ((object != null) && (object instanceof TextProvider)) {
+ if (object instanceof CompositeTextProvider) {
+ return (CompositeTextProvider) object;
+ }
+ return new CompositeTextProvider(new TextProvider[]{
+ ((TextProvider) object),
+ new TextProviderSupport(object.getClass(), localeProvider)
+ });
+ } else if (localeProvider != null && localeProvider instanceof TextProvider) {
+ if (localeProvider instanceof CompositeTextProvider) {
+ return (CompositeTextProvider) localeProvider;
+ }
+ return new CompositeTextProvider(new TextProvider[]{
+ ((TextProvider) localeProvider),
+ new TextProviderSupport(localeProvider.getClass(), localeProvider)
+ });
+ } else {
+ return new TextProviderFactory().createInstance(
+ object != null ? object.getClass() : DelegatingValidatorContext.class,
+ localeProvider);
+ }
+ }
+
+ protected static LocaleProvider makeLocaleProvider(Object object) {
+ if (object instanceof LocaleProvider) {
+ return (LocaleProvider) object;
+ } else {
+ return new ActionContextLocaleProvider();
+ }
+ }
+
+ protected static ValidationAware makeValidationAware(Object object) {
+ if (object instanceof ValidationAware) {
+ return (ValidationAware) object;
+ } else {
+ return new LoggingValidationAware(object);
+ }
+ }
+
+ protected void setTextProvider(TextProvider textProvider) {
+ this.textProvider = textProvider;
+ }
+
+ protected TextProvider getTextProvider() {
+ return textProvider;
+ }
+
+ protected void setValidationAware(ValidationAware validationAware) {
+ this.validationAware = validationAware;
+ }
+
+ protected ValidationAware getValidationAware() {
+ return validationAware;
+ }
+
+ /**
+ * An implementation of LocaleProvider which gets the locale from the action context.
+ */
+ private static class ActionContextLocaleProvider implements LocaleProvider {
+ public Locale getLocale() {
+ return ActionContext.getContext().getLocale();
+ }
+ }
+
+ /**
+ * An implementation of ValidationAware which logs errors and messages.
+ */
+ private static class LoggingValidationAware implements ValidationAware {
+
+ private Logger log;
+
+ public LoggingValidationAware(Class clazz) {
+ log = LogManager.getLogger(clazz);
+ }
+
+ public LoggingValidationAware(Object obj) {
+ log = LogManager.getLogger(obj.getClass());
+ }
+
+ public void setActionErrors(Collection errorMessages) {
+ for (Object errorMessage : errorMessages) {
+ String s = (String) errorMessage;
+ addActionError(s);
+ }
+ }
+
+ public Collection getActionErrors() {
+ return null;
+ }
+
+ public void setActionMessages(Collection messages) {
+ for (Object message : messages) {
+ String s = (String) message;
+ addActionMessage(s);
+ }
+ }
+
+ public Collection getActionMessages() {
+ return null;
+ }
+
+ public void setFieldErrors(Map> errorMap) {
+ for (Map.Entry> entry : errorMap.entrySet()) {
+ addFieldError(entry.getKey(), entry.getValue().toString());
+ }
+ }
+
+ public Map> getFieldErrors() {
+ return null;
+ }
+
+ public void addActionError(String anErrorMessage) {
+ log.error("Validation error: {}", anErrorMessage);
+ }
+
+ public void addActionMessage(String aMessage) {
+ log.info("Validation Message: {}", aMessage);
+ }
+
+ public void addFieldError(String fieldName, String errorMessage) {
+ log.error("Validation error for {}:{}", fieldName, errorMessage);
+ }
+
+ public boolean hasActionErrors() {
+ return false;
+ }
+
+ public boolean hasActionMessages() {
+ return false;
+ }
+
+ public boolean hasErrors() {
+ return false;
+ }
+
+ public boolean hasFieldErrors() {
+ return false;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/FieldValidator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/FieldValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/FieldValidator.java
new file mode 100644
index 0000000..3077671
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/validator/FieldValidator.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ *
+ * 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 com.opensymphony.xwork2.validator;
+
+/**
+ * The FieldValidator interface defines the methods to be implemented by FieldValidators.
+ * Which are used by the XWork validation framework to validate Action properties before
+ * executing the Action.
+ */
+public interface FieldValidator extends Validator {
+
+ /**
+ * Sets the field name to validate with this FieldValidator
+ *
+ * @param fieldName the field name
+ */
+ void setFieldName(String fieldName);
+
+ /**
+ * Gets the field name to be validated
+ *
+ * @return the field name
+ */
+ String getFieldName();
+
+}
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/ShortCircuitableValidator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/ShortCircuitableValidator.java b/core/src/main/java/com/opensymphony/xwork2/validator/ShortCircuitableValidator.java
new file mode 100644
index 0000000..651270d
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/validator/ShortCircuitableValidator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2002-2007,2009 The Apache Software Foundation.
+ *
+ * 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 com.opensymphony.xwork2.validator;
+
+
+/**
+ * This interface should be implemented by validators that can short-circuit the validator queue
+ * that it is in.
+ *
+ * @author Mark Woon
+ */
+public interface ShortCircuitableValidator {
+
+ /**
+ * Sets whether this field validator should short circuit the validator queue
+ * it's in if validation fails.
+ *
+ * @param shortcircuit true if this field validator should short circuit on
+ * failure, false otherwise
+ */
+ public void setShortCircuit(boolean shortcircuit);
+
+ /**
+ * Gets whether this field validator should short circuit the validator queue
+ * it's in if validation fails.
+ *
+ * @return true if this field validator should short circuit on failure,
+ * false otherwise
+ */
+ public boolean isShortCircuit();
+}