You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2011/12/02 17:33:45 UTC

svn commit: r1209569 [31/50] - in /struts/struts2/branches/STRUTS_3_X: apps/blank/src/main/java/example/ apps/blank/src/test/java/example/ apps/jboss-blank/src/main/java/example/ apps/jboss-blank/src/test/java/example/ apps/mailreader/src/main/java/mai...

Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/AnnotationActionValidatorManager.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/AnnotationActionValidatorManager.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/AnnotationActionValidatorManager.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/AnnotationActionValidatorManager.java Fri Dec  2 16:33:03 2011
@@ -0,0 +1,424 @@
+/*
+ * 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 org.apache.struts2.xwork2.validator;
+
+
+import org.apache.struts2.xwork2.ActionContext;
+import org.apache.struts2.xwork2.ActionInvocation;
+import org.apache.struts2.xwork2.ActionProxy;
+import org.apache.struts2.xwork2.config.entities.ActionConfig;
+import org.apache.struts2.xwork2.inject.Inject;
+import org.apache.struts2.xwork2.util.FileManager;
+import org.apache.struts2.xwork2.util.ValueStack;
+import org.apache.struts2.xwork2.util.logging.Logger;
+import org.apache.struts2.xwork2.util.logging.LoggerFactory;
+import org.apache.struts2.xwork2.validator.validators.VisitorFieldValidator;
+import org.apache.commons.lang.StringUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * AnnotationActionValidatorManager is the entry point into XWork's annotations-based validator framework.
+ * Validation rules are specified as annotations within the source files.
+ *
+ * @author Rainer Hermanns
+ * @author jepjep
+ */
+public class AnnotationActionValidatorManager implements ActionValidatorManager {
+
+    /**
+     * The file suffix for any validation file.
+     */
+    protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
+
+    private final Map<String, List<ValidatorConfig>> validatorCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
+    private final Map<String, List<ValidatorConfig>> validatorFileCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
+    private static final Logger LOG = LoggerFactory.getLogger(AnnotationActionValidatorManager.class);
+
+    private ValidatorFactory validatorFactory;
+    private ValidatorFileParser validatorFileParser;
+
+    @Inject
+    public void setValidatorFactory(ValidatorFactory fac) {
+        this.validatorFactory = fac;
+    }
+
+    @Inject
+    public void setValidatorFileParser(ValidatorFileParser parser) {
+        this.validatorFileParser = parser;
+    }
+
+    public List<Validator> getValidators(Class clazz, String context) {
+        return getValidators(clazz, context, null);
+    }
+
+    public List<Validator> getValidators(Class clazz, String context, String method) {
+        final String validatorKey = buildValidatorKey(clazz);
+        final List<ValidatorConfig> cfgs;
+
+        if (validatorCache.containsKey(validatorKey)) {
+            if (FileManager.isReloadingConfigs()) {
+                validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
+            }
+        } else {
+            validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
+        }
+
+        // get the set of validator configs
+        cfgs = new ArrayList<ValidatorConfig>(validatorCache.get(validatorKey));
+
+        ValueStack stack = ActionContext.getContext().getValueStack();
+
+        // create clean instances of the validators for the caller's use
+        ArrayList<Validator> validators = new ArrayList<Validator>(cfgs.size());
+        for (ValidatorConfig cfg : cfgs) {
+            if (method == null || method.equals(cfg.getParams().get("methodName"))) {
+                Validator validator = validatorFactory.getValidator(
+                        new ValidatorConfig.Builder(cfg)
+                                .removeParam("methodName")
+                                .build());
+                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<Validator> validators = getValidators(object.getClass(), context, method);
+        Set<String> shortcircuitedFields = null;
+
+        for (final Validator validator : validators) {
+            try {
+                validator.setValidatorContext(validatorContext);
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Running validator: " + validator + " for object " + object + " and method " + method);
+                }
+
+                FieldValidator fValidator = null;
+                String fullFieldName = null;
+
+                if (validator instanceof FieldValidator) {
+                    fValidator = (FieldValidator) validator;
+                    fullFieldName = new InternalValidatorContextWrapper(fValidator.getValidatorContext()).getFullFieldName(fValidator.getFieldName());
+
+                    if ((shortcircuitedFields != null) && shortcircuitedFields.contains(fullFieldName)) {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("Short-circuited, skipping");
+                        }
+
+                        continue;
+                    }
+                }
+
+                if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit()) {
+                    // get number of existing errors
+                    List<String> errs = null;
+
+                    if (fValidator != null) {
+                        if (validatorContext.hasFieldErrors()) {
+                            Collection<String> fieldErrors = validatorContext.getFieldErrors().get(fullFieldName);
+
+                            if (fieldErrors != null) {
+                                errs = new ArrayList<String>(fieldErrors);
+                            }
+                        }
+                    } else if (validatorContext.hasActionErrors()) {
+                        Collection<String> actionErrors = validatorContext.getActionErrors();
+
+                        if (actionErrors != null) {
+                            errs = new ArrayList<String>(actionErrors);
+                        }
+                    }
+
+                    validator.validate(object);
+
+                    if (fValidator != null) {
+                        if (validatorContext.hasFieldErrors()) {
+                            Collection<String> errCol = validatorContext.getFieldErrors().get(fullFieldName);
+
+                            if ((errCol != null) && !errCol.equals(errs)) {
+                                if (LOG.isDebugEnabled()) {
+                                    LOG.debug("Short-circuiting on field validation");
+                                }
+
+                                if (shortcircuitedFields == null) {
+                                    shortcircuitedFields = new TreeSet<String>();
+                                }
+
+                                shortcircuitedFields.add(fullFieldName);
+                            }
+                        }
+                    } else if (validatorContext.hasActionErrors()) {
+                        Collection<String> errCol = validatorContext.getActionErrors();
+
+                        if ((errCol != null) && !errCol.equals(errs)) {
+                            if (LOG.isDebugEnabled()) {
+                                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.
+     * @return a validator key which is the class name plus context.
+     */
+    protected static String buildValidatorKey(Class clazz) {
+        ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
+        ActionProxy proxy = invocation.getProxy();
+        ActionConfig config = proxy.getConfig();
+
+        //the key needs to use the name of the action from the config file,
+        //instead of the url, so wild card actions will have the same validator
+        //see WW-2996
+        StringBuilder sb = new StringBuilder(clazz.getName());
+        sb.append("/");
+        if (StringUtils.isNotBlank(config.getPackageName())) {
+            sb.append(config.getPackageName());
+            sb.append("/");
+        }
+        sb.append(config.getName());
+        sb.append("|");
+        sb.append(proxy.getMethod());
+        return sb.toString();
+    }
+
+    private List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
+        String fileName = aClass.getName().replace('.', '/') + "-" + context.replace('/', '-') + VALIDATION_CONFIG_SUFFIX;
+
+        return loadFile(fileName, aClass, checkFile);
+    }
+
+
+    protected List<ValidatorConfig> buildClassValidatorConfigs(Class aClass, boolean checkFile) {
+
+        String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
+
+        List<ValidatorConfig> result = new ArrayList<ValidatorConfig>(loadFile(fileName, aClass, checkFile));
+
+        AnnotationValidationConfigurationBuilder builder = new AnnotationValidationConfigurationBuilder(validatorFactory);
+
+        List<ValidatorConfig> annotationResult = new ArrayList<ValidatorConfig>(builder.buildAnnotationClassValidatorConfigs(aClass));
+
+        result.addAll(annotationResult);
+
+        return result;
+
+    }
+
+    /**
+     * <p>This method 'collects' all the validator configurations for a given
+     * action invocation.</p>
+     * <p/>
+     * <p>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!</p>
+     * <p/>
+     * <p>Given the following class structure:
+     * <pre>
+     *   interface Thing;
+     *   interface Animal extends Thing;
+     *   interface Quadraped extends Animal;
+     *   class AnimalImpl implements Animal;
+     *   class QuadrapedImpl extends AnimalImpl implements Quadraped;
+     *   class Dog extends QuadrapedImpl;
+     * </pre></p>
+     * <p/>
+     * <p>This method will look for the following config files for Dog:
+     * <pre>
+     *   Animal
+     *   Animal-context
+     *   AnimalImpl
+     *   AnimalImpl-context
+     *   Quadraped
+     *   Quadraped-context
+     *   QuadrapedImpl
+     *   QuadrapedImpl-context
+     *   Dog
+     *   Dog-context
+     * </pre></p>
+     * <p/>
+     * <p>Note that the validation rules for Thing is never looked for because no class in the
+     * hierarchy directly implements Thing.</p>
+     *
+     * @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<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set<String> checked) {
+        List<ValidatorConfig> validatorConfigs = new ArrayList<ValidatorConfig>();
+
+        if (checked == null) {
+            checked = new TreeSet<String>();
+        } else if (checked.contains(clazz.getName())) {
+            return validatorConfigs;
+        }
+
+        if (clazz.isInterface()) {
+            Class[] interfaces = clazz.getInterfaces();
+
+            for (Class anInterface : interfaces) {
+                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
+        Class[] interfaces = clazz.getInterfaces();
+
+        for (Class anInterface1 : interfaces) {
+            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<ValidatorConfig> loadFile(String fileName, Class clazz, boolean checkFile) {
+        List<ValidatorConfig> retList = Collections.emptyList();
+
+        if ((checkFile && FileManager.fileNeedsReloading(fileName, clazz)) || !validatorFileCache.containsKey(fileName)) {
+            InputStream is = null;
+
+            try {
+                is = FileManager.loadFile(fileName, clazz);
+
+                if (is != null) {
+                    retList = new ArrayList<ValidatorConfig>(validatorFileParser.parseActionValidatorConfigs(validatorFactory, is, fileName));
+                }
+            } catch (Exception e) {
+                LOG.error("Caught exception while loading file " + fileName, e);
+            } finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (IOException e) {
+                        LOG.error("Unable to close input stream for " + fileName, e);
+                    }
+                }
+            }
+
+            validatorFileCache.put(fileName, retList);
+        } else {
+            retList = validatorFileCache.get(fileName);
+        }
+
+        return retList;
+    }
+
+
+    /**
+     * An {@link ValidatorContext} wrapper that
+     * returns the full field name
+     * {@link AnnotationActionValidatorManager.InternalValidatorContextWrapper#getFullFieldName(String)}
+     * by consulting it's parent if its an {@link org.apache.struts2.xwork2.validator.validators.VisitorFieldValidator.AppendingValidatorContext}.
+     * <p/>
+     * Eg. if we have nested Visitor
+     * AddressVisitor nested inside PersonVisitor, when using the normal #getFullFieldName, we will get
+     * "address.somefield", we lost the parent, with this wrapper, we will get "person.address.somefield".
+     * This is so that the key is used to register errors, so that we don't screw up short-curcuit feature
+     * when using nested visitor. See XW-571 (nested visitor validators break short-circuit functionality)
+     * at http://jira.opensymphony.com/browse/XW-571
+     */
+    protected class InternalValidatorContextWrapper {
+        private ValidatorContext validatorContext = null;
+
+        InternalValidatorContextWrapper(ValidatorContext validatorContext) {
+            this.validatorContext = validatorContext;
+        }
+
+        /**
+         * Get the full field name by consulting the parent, so that when we are using nested visitors (
+         * visitor nested inside visitor etc.) we still get the full field name including its parents.
+         * See XW-571 for more details.
+         *
+         * @param field
+         * @return String
+         */
+        public String getFullFieldName(String field) {
+            if (validatorContext instanceof VisitorFieldValidator.AppendingValidatorContext) {
+                VisitorFieldValidator.AppendingValidatorContext appendingValidatorContext =
+                        (VisitorFieldValidator.AppendingValidatorContext) validatorContext;
+                return appendingValidatorContext.getFullFieldNameFromParent(field);
+            }
+            return validatorContext.getFullFieldName(field);
+        }
+
+    }
+}

Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/AnnotationValidationConfigurationBuilder.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/AnnotationValidationConfigurationBuilder.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/AnnotationValidationConfigurationBuilder.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/AnnotationValidationConfigurationBuilder.java Fri Dec  2 16:33:03 2011
@@ -0,0 +1,833 @@
+/*
+ * 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 org.apache.struts2.xwork2.validator;
+
+import org.apache.struts2.xwork2.validator.annotations.ConditionalVisitorFieldValidator;
+import org.apache.struts2.xwork2.validator.annotations.ConversionErrorFieldValidator;
+import org.apache.struts2.xwork2.validator.annotations.CustomValidator;
+import org.apache.struts2.xwork2.validator.annotations.DateRangeFieldValidator;
+import org.apache.struts2.xwork2.validator.annotations.DoubleRangeFieldValidator;
+import org.apache.struts2.xwork2.validator.annotations.EmailValidator;
+import org.apache.struts2.xwork2.validator.annotations.ExpressionValidator;
+import org.apache.struts2.xwork2.validator.annotations.FieldExpressionValidator;
+import org.apache.struts2.xwork2.validator.annotations.IntRangeFieldValidator;
+import org.apache.struts2.xwork2.validator.annotations.RegexFieldValidator;
+import org.apache.struts2.xwork2.validator.annotations.RequiredFieldValidator;
+import org.apache.struts2.xwork2.validator.annotations.RequiredStringValidator;
+import org.apache.struts2.xwork2.validator.annotations.StringLengthFieldValidator;
+import org.apache.struts2.xwork2.validator.annotations.UrlValidator;
+import org.apache.struts2.xwork2.validator.annotations.Validation;
+import org.apache.struts2.xwork2.validator.annotations.ValidationParameter;
+import org.apache.struts2.xwork2.validator.annotations.Validations;
+import org.apache.struts2.xwork2.validator.annotations.VisitorFieldValidator;
+
+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;
+
+/**
+ * <code>AnnotationValidationConfigurationBuilder</code>
+ *
+ * @author Rainer Hermanns
+ * @author jepjep
+ * @version $Id: AnnotationValidationConfigurationBuilder.java 1209415 2011-12-02 11:24:48Z lukaszlenart $
+ */
+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<ValidatorConfig> processAnnotations(Object o) {
+
+        List<ValidatorConfig> result = new ArrayList<ValidatorConfig>();
+
+        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 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<ValidatorConfig> 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<String, String> params = new HashMap<String, String>();
+
+        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())
+            .build();
+
+    }
+
+    private ValidatorConfig processCustomValidatorAnnotation(CustomValidator v, String fieldName, String methodName) {
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            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())
+            .build();
+    }
+
+    private ValidatorConfig processRegexFieldValidatorAnnotation(RegexFieldValidator v, String fieldName, String methodName) {
+        String validatorType = "regex";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            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())
+            .build();
+    }
+
+    private ValidatorConfig processConditionalVisitorFieldValidatorAnnotation(ConditionalVisitorFieldValidator v, String fieldName, String methodName) {
+        String validatorType = "conditionalvisitor";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            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())
+            .build();
+    }
+
+
+
+    private ValidatorConfig processVisitorFieldValidatorAnnotation(VisitorFieldValidator v, String fieldName, String methodName) {
+
+        String validatorType = "visitor";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            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())
+            .build();
+    }
+
+    private ValidatorConfig processUrlValidatorAnnotation(UrlValidator v, String fieldName, String methodName) {
+        String validatorType = "url";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            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())
+            .build();
+    }
+
+    private ValidatorConfig processStringLengthFieldValidatorAnnotation(StringLengthFieldValidator v, String fieldName, String methodName) {
+        String validatorType = "stringlength";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            params.put("fieldName", v.fieldName());
+        }
+
+        if ( v.maxLength() != null && v.maxLength().length() > 0) {
+            params.put("maxLength", v.maxLength());
+        }
+        if ( v.minLength() != null && v.minLength().length() > 0) {
+            params.put("minLength", v.minLength());
+        }
+        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())
+            .build();
+    }
+
+    private Date parseDateString(String value) {
+
+        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 = {d1, d2, d3};
+        DateFormat df = null;
+        for (SimpleDateFormat df1 : dfs) {
+            try {
+                Date check = df1.parse(value);
+                df = df1;
+                if (check != null) {
+                    return check;
+                }
+            }
+            catch (ParseException ignore) {
+            }
+        }
+        return null;
+
+    }
+
+    private ValidatorConfig processRequiredStringValidatorAnnotation(RequiredStringValidator v, String fieldName, String methodName) {
+        String validatorType = "requiredstring";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            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())
+            .messageKey(v.key())
+            .build();
+    }
+
+    private ValidatorConfig processRequiredFieldValidatorAnnotation(RequiredFieldValidator v, String fieldName, String methodName) {
+        String validatorType = "required";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            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())
+            .build();
+    }
+
+    private ValidatorConfig processIntRangeFieldValidatorAnnotation(IntRangeFieldValidator v, String fieldName, String methodName) {
+        String validatorType = "int";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        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) {
+            params.put("min", v.min());
+        }
+        if ( v.max() != null && v.max().length() > 0) {
+            params.put("max", v.max());
+        }
+
+        validatorFactory.lookupRegisteredValidatorType(validatorType);
+        return new ValidatorConfig.Builder(validatorType)
+            .addParams(params)
+            .addParam("methodName", methodName)
+            .shortCircuit(v.shortCircuit())
+            .defaultMessage(v.message())
+            .messageKey(v.key())
+            .build();
+    }
+
+    private ValidatorConfig processDoubleRangeFieldValidatorAnnotation(DoubleRangeFieldValidator v, String fieldName, String methodName) {
+        String validatorType = "double";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        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());
+        }
+
+        validatorFactory.lookupRegisteredValidatorType(validatorType);
+        return new ValidatorConfig.Builder(validatorType)
+            .addParams(params)
+            .addParam("methodName", methodName)
+            .shortCircuit(v.shortCircuit())
+            .defaultMessage(v.message())
+            .messageKey(v.key())
+            .build();
+    }
+
+    private ValidatorConfig processFieldExpressionValidatorAnnotation(FieldExpressionValidator v, String fieldName, String methodName) {
+        String validatorType = "fieldexpression";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            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())
+            .build();
+    }
+
+    private ValidatorConfig processEmailValidatorAnnotation(EmailValidator v, String fieldName, String methodName) {
+        String validatorType = "email";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            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())
+            .build();
+    }
+
+    private ValidatorConfig processDateRangeFieldValidatorAnnotation(DateRangeFieldValidator v, String fieldName, String methodName) {
+        String validatorType = "date";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        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());
+             params.put("min", String.valueOf(minDate == null ? v.min() : minDate));
+        }
+        if ( v.max() != null && v.max().length() > 0) {
+             final Date maxDate = parseDateString(v.max());
+             params.put("max", String.valueOf(maxDate == null ? v.max() : maxDate));
+        }
+
+        validatorFactory.lookupRegisteredValidatorType(validatorType);
+        return new ValidatorConfig.Builder(validatorType)
+            .addParams(params)
+            .addParam("methodName", methodName)
+            .shortCircuit(v.shortCircuit())
+            .defaultMessage(v.message())
+            .messageKey(v.key())
+            .build();
+    }
+
+    private ValidatorConfig processConversionErrorFieldValidatorAnnotation(ConversionErrorFieldValidator v, String fieldName, String methodName) {
+        String validatorType = "conversion";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            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())
+            .build();
+    }
+
+    public List<ValidatorConfig> buildAnnotationClassValidatorConfigs(Class aClass) {
+
+        List<ValidatorConfig> result = new ArrayList<ValidatorConfig>();
+
+        List<ValidatorConfig> 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;
+    }
+
+}

Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/DefaultActionValidatorManager.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/DefaultActionValidatorManager.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/DefaultActionValidatorManager.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/DefaultActionValidatorManager.java Fri Dec  2 16:33:03 2011
@@ -0,0 +1,387 @@
+/*
+ * 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 org.apache.struts2.xwork2.validator;
+
+import org.apache.struts2.xwork2.ActionContext;
+import org.apache.struts2.xwork2.inject.Inject;
+import org.apache.struts2.xwork2.util.FileManager;
+import org.apache.struts2.xwork2.util.ValueStack;
+import org.apache.struts2.xwork2.util.logging.Logger;
+import org.apache.struts2.xwork2.util.logging.LoggerFactory;
+import org.apache.struts2.xwork2.validator.validators.VisitorFieldValidator;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+
+/**
+ * This is the entry point into XWork's rule-based validation framework.
+ * <p/>
+ * Validation rules are specified in XML configuration files named <code>className-contextName-validation.xml</code> 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<String, List<ValidatorConfig>> validatorCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
+    private final Map<String, List<ValidatorConfig>> validatorFileCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
+    private final Logger LOG = LoggerFactory.getLogger(DefaultActionValidatorManager.class);
+    private ValidatorFactory validatorFactory;
+    private ValidatorFileParser validatorFileParser;
+
+    @Inject
+    public void setValidatorFileParser(ValidatorFileParser parser) {
+        this.validatorFileParser = parser;
+    }
+    
+    @Inject
+    public void setValidatorFactory(ValidatorFactory fac) {
+        this.validatorFactory = fac;
+    }
+
+    public synchronized List<Validator> getValidators(Class clazz, String context) {
+        return getValidators(clazz, context, null);
+    }
+
+    public synchronized List<Validator> getValidators(Class clazz, String context, String method) {
+        final String validatorKey = buildValidatorKey(clazz, context);
+
+        if (validatorCache.containsKey(validatorKey)) {
+            if (FileManager.isReloadingConfigs()) {
+                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<ValidatorConfig> cfgs = validatorCache.get(validatorKey);
+
+        // create clean instances of the validators for the caller's use
+        ArrayList<Validator> validators = new ArrayList<Validator>(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<Validator> validators = getValidators(object.getClass(), context, method);
+        Set<String> shortcircuitedFields = null;
+
+        for (final Validator validator : validators) {
+        try {
+                validator.setValidatorContext(validatorContext);
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Running validator: " + validator + " for object " + object + " and method " + method);
+                }
+
+                FieldValidator fValidator = null;
+                String fullFieldName = null;
+
+                if (validator instanceof FieldValidator) {
+                    fValidator = (FieldValidator) validator;
+                    fullFieldName = new InternalValidatorContextWrapper(fValidator.getValidatorContext()).getFullFieldName(fValidator.getFieldName());
+
+                    // This is pretty crap, but needed to support short-circuited validations on nested visited objects
+                    if (validatorContext instanceof VisitorFieldValidator.AppendingValidatorContext) {
+                        VisitorFieldValidator.AppendingValidatorContext appendingValidatorContext =
+                                (VisitorFieldValidator.AppendingValidatorContext) validatorContext;
+                        fullFieldName = appendingValidatorContext.getFullFieldNameFromParent(fValidator.getFieldName());
+                    }
+
+                    if ((shortcircuitedFields != null) && shortcircuitedFields.contains(fullFieldName)) {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("Short-circuited, skipping");
+                        }
+
+                        continue;
+                    }
+                }
+
+                if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit()) {
+                    // get number of existing errors
+                    List<String> errs = null;
+
+                    if (fValidator != null) {
+                        if (validatorContext.hasFieldErrors()) {
+                            Collection<String> fieldErrors = validatorContext.getFieldErrors().get(fullFieldName);
+
+                            if (fieldErrors != null) {
+                                errs = new ArrayList<String>(fieldErrors);
+                            }
+                        }
+                    } else if (validatorContext.hasActionErrors()) {
+                        Collection<String> actionErrors = validatorContext.getActionErrors();
+
+                        if (actionErrors != null) {
+                            errs = new ArrayList<String>(actionErrors);
+                        }
+                    }
+
+                    validator.validate(object);
+
+                    if (fValidator != null) {
+                        if (validatorContext.hasFieldErrors()) {
+                            Collection<String> errCol = validatorContext.getFieldErrors().get(fullFieldName);
+
+                            if ((errCol != null) && !errCol.equals(errs)) {
+                                if (LOG.isDebugEnabled()) {
+                                    LOG.debug("Short-circuiting on field validation");
+                                }
+
+                                if (shortcircuitedFields == null) {
+                                    shortcircuitedFields = new TreeSet<String>();
+                                }
+
+                                shortcircuitedFields.add(fullFieldName);
+                            }
+                        }
+                    } else if (validatorContext.hasActionErrors()) {
+                        Collection<String> errCol = validatorContext.getActionErrors();
+
+                        if ((errCol != null) && !errCol.equals(errs)) {
+                            if (LOG.isDebugEnabled()) {
+                                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<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
+        String fileName = aClass.getName().replace('.', '/') + "-" + context + VALIDATION_CONFIG_SUFFIX;
+
+        return loadFile(fileName, aClass, checkFile);
+    }
+
+    private List<ValidatorConfig> buildClassValidatorConfigs(Class aClass, boolean checkFile) {
+        String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
+
+        return loadFile(fileName, aClass, checkFile);
+    }
+
+    /**
+     * <p>This method 'collects' all the validator configurations for a given
+     * action invocation.</p>
+     *
+     * <p>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!</p>
+     *
+     * <p>Given the following class structure:
+     * <pre>
+     *   interface Thing;
+     *   interface Animal extends Thing;
+     *   interface Quadraped extends Animal;
+     *   class AnimalImpl implements Animal;
+     *   class QuadrapedImpl extends AnimalImpl implements Quadraped;
+     *   class Dog extends QuadrapedImpl;
+     * </pre></p>
+     *
+     * <p>This method will look for the following config files for Dog:
+     * <pre>
+     *   Animal
+     *   Animal-context
+     *   AnimalImpl
+     *   AnimalImpl-context
+     *   Quadraped
+     *   Quadraped-context
+     *   QuadrapedImpl
+     *   QuadrapedImpl-context
+     *   Dog
+     *   Dog-context
+     * </pre></p>
+     *
+     * <p>Note that the validation rules for Thing is never looked for because no class in the
+     * hierarchy directly implements Thing.</p>
+     *
+     * @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<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set<String> checked) {
+        List<ValidatorConfig> validatorConfigs = new ArrayList<ValidatorConfig>();
+
+        if (checked == null) {
+            checked = new TreeSet<String>();
+        } 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<ValidatorConfig> loadFile(String fileName, Class clazz, boolean checkFile) {
+        List<ValidatorConfig> retList = Collections.emptyList();
+        if ((checkFile && FileManager.fileNeedsReloading(fileName, clazz)) || !validatorFileCache.containsKey(fileName)) {
+            InputStream is = null;
+
+            try {
+                is = FileManager.loadFile(fileName, clazz);
+
+                if (is != null) {
+                    retList = new ArrayList<ValidatorConfig>(validatorFileParser.parseActionValidatorConfigs(validatorFactory, is, fileName));
+                }
+            } finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (IOException e) {
+                        LOG.error("Unable to close input stream for " + fileName, e);
+                    }
+                }
+            }
+
+            validatorFileCache.put(fileName, retList);
+        } else {
+            retList = validatorFileCache.get(fileName);
+        }
+
+        return retList;
+    }
+
+
+    /**
+     * An {@link ValidatorContext} wrapper that
+     * returns the full field name
+     * {@link InternalValidatorContextWrapper#getFullFieldName(String)}
+     * by consulting it's parent if its an {@link org.apache.struts2.xwork2.validator.validators.VisitorFieldValidator.AppendingValidatorContext}.
+     * <p/>
+     * Eg. if we have nested Visitor
+     * AddressVisitor nested inside PersonVisitor, when using the normal #getFullFieldName, we will get
+     * "address.somefield", we lost the parent, with this wrapper, we will get "person.address.somefield".
+     * This is so that the key is used to register errors, so that we don't screw up short-curcuit feature
+     * when using nested visitor. See XW-571 (nested visitor validators break short-circuit functionality)
+     * at http://jira.opensymphony.com/browse/XW-571
+     */
+    protected class InternalValidatorContextWrapper {
+        private ValidatorContext validatorContext = null;
+
+        InternalValidatorContextWrapper(ValidatorContext validatorContext) {
+            this.validatorContext = validatorContext;
+        }
+
+        /**
+         * Get the full field name by consulting the parent, so that when we are using nested visitors (
+         * visitor nested inside visitor etc.) we still get the full field name including its parents.
+         * See XW-571 for more details.
+         * @param field The field name
+         * @return String
+         */
+        public String getFullFieldName(String field) {
+            if (validatorContext instanceof VisitorFieldValidator.AppendingValidatorContext) {
+                VisitorFieldValidator.AppendingValidatorContext appendingValidatorContext =
+                        (VisitorFieldValidator.AppendingValidatorContext) validatorContext;
+                return appendingValidatorContext.getFullFieldNameFromParent(field);
+            }
+            return validatorContext.getFullFieldName(field);
+        }
+
+    }    
+
+}

Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/DefaultValidatorFactory.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/DefaultValidatorFactory.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/DefaultValidatorFactory.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/validator/DefaultValidatorFactory.java Fri Dec  2 16:33:03 2011
@@ -0,0 +1,198 @@
+/*
+ * 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 org.apache.struts2.xwork2.validator;
+
+import org.apache.struts2.xwork2.ObjectFactory;
+import org.apache.struts2.xwork2.XWorkException;
+import org.apache.struts2.xwork2.config.ConfigurationException;
+import org.apache.struts2.xwork2.inject.Inject;
+import org.apache.struts2.xwork2.util.ClassLoaderUtil;
+import org.apache.struts2.xwork2.util.logging.Logger;
+import org.apache.struts2.xwork2.util.logging.LoggerFactory;
+
+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.ZipInputStream;
+import java.util.zip.ZipEntry;
+
+
+/**
+ * Default validator factory
+ *
+ * @version $Date: 2011-12-02 12:24:48 +0100 (Fri, 02 Dec 2011) $ $Id: DefaultValidatorFactory.java 1209415 2011-12-02 11:24:48Z lukaszlenart $
+ * @author Jason Carreira
+ * @author James House
+ */
+public class DefaultValidatorFactory implements ValidatorFactory {
+
+    protected Map<String, String> validators = new HashMap<String, String>();
+    private static Logger LOG = LoggerFactory.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(), null); // 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) {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Registering validator of class " + className + " with name " + 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() {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Loading validator definitions.");
+        }
+
+        List<File> files = new ArrayList<File>();
+        try {
+            // Get custom validator configurations via the classpath
+            Iterator<URL> 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")) {
+                                        if (LOG.isTraceEnabled()) {
+                                            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 #0", ex, u.toString());
+                }
+            }
+        } catch (IOException e) {
+            throw new ConfigurationException("Unable to parse validators", e);
+        }
+
+        // Parse default validator configurations
+        String resourceName = "org/apache/struts2/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);
+        }
+    }
+}