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);
+ }
+ }
+}