All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.webflow.validation.ValidationHelper Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
/*
 * Copyright 2008-2012 the original author or authors.
 *
 * 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.springframework.webflow.validation;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.mapping.MappingResults;
import org.springframework.binding.message.MessageContext;
import org.springframework.binding.message.MessageContextErrors;
import org.springframework.binding.validation.ValidationContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.SmartValidator;
import org.springframework.validation.Validator;
import org.springframework.webflow.definition.TransitionDefinition;
import org.springframework.webflow.execution.FlowExecutionException;
import org.springframework.webflow.execution.RequestContext;

/**
 * A helper class the encapsulates conventions to invoke validation logic.
 *
 * @author Scott Andrews
 * @author Canny Duck
 * @author Jeremy Grelle
 */
public class ValidationHelper {

	private static final Log logger = LogFactory.getLog(ValidationHelper.class);

	private final Object model;

	private final RequestContext requestContext;

	private final String eventId;

	private final String modelName;

	private final ExpressionParser expressionParser;

	private final MessageCodesResolver messageCodesResolver;

	private final MappingResults mappingResults;

	private Validator validator;

	private Object[] validationHints;


	/**
	 * Create a throwaway validation helper object. Validation is invoked by the {@link #validate()} method.
	 * 

* Validation methods invoked include a validation method on the model object or a validator bean. The method name * on either object is in the form of validate[viewStateId]. A ValidationContext is passed in the method signature * along with the model object for validator beans. A MessageContext can be substituted for the ValidationContext. *

* For example: model.validateEnterBookingDetails(VaticationContext) or * context.getBean("modelValidator").validateEnterBookingDetails(model, VaticationContext) * * @param model the object to validate * @param requestContext the context for the request * @param eventId the event triggering validation * @param modelName the name of the model object * @param expressionParser the expression parser * @param mappingResults object mapping results */ public ValidationHelper(Object model, RequestContext requestContext, String eventId, String modelName, ExpressionParser expressionParser, MessageCodesResolver messageCodesResolver, MappingResults mappingResults, ValidationHintResolver hintResolver) { Assert.notNull(model, "The model to validate is required"); Assert.notNull(requestContext, "The request context for the validator is required"); this.model = model; this.requestContext = requestContext; this.eventId = eventId; this.modelName = modelName; this.expressionParser = expressionParser; this.messageCodesResolver = messageCodesResolver; this.mappingResults = mappingResults; this.validationHints = initValidationHints(model, requestContext, eventId, hintResolver); } /** * An alternative constructor that creates an instance of {@link BeanValidationHintResolver}. */ public ValidationHelper(Object model, RequestContext requestContext, String eventId, String modelName, ExpressionParser expressionParser, MessageCodesResolver messageCodesResolver, MappingResults mappingResults) { this(model, requestContext, eventId, modelName, expressionParser, messageCodesResolver, mappingResults, new BeanValidationHintResolver()); } private static Object[] initValidationHints(Object model, RequestContext requestContext, String eventId, ValidationHintResolver hintResolver) { Expression expr = null; TransitionDefinition transition = requestContext.getMatchingTransition(eventId); if (transition != null) { expr = (Expression) transition.getAttributes().get("validationHints"); } if (expr == null) { expr = (Expression) requestContext.getCurrentState().getAttributes().get("validationHints"); } if (expr == null) { return null; } String flowId = requestContext.getActiveFlow().getId(); String stateId = requestContext.getCurrentState().getId(); try { Object hintsValue = expr.getValue(requestContext); if (hintsValue instanceof String) { String[] hints = StringUtils.commaDelimitedListToStringArray((String) hintsValue); return hintResolver.resolveValidationHints(model, flowId, stateId, hints); } else if (hintsValue instanceof Object[]) { return (Object[]) hintsValue; } else { throw new FlowExecutionException(flowId, stateId, "Failed to resolve validation hints [" + hintsValue + "]"); } } catch (EvaluationException e) { throw new FlowExecutionException(flowId, stateId, "Failed to resolve validation hints expression [" + expr + "]", e); } } /** * Configure a {@link Validator} to apply to the model. */ public void setValidator(Validator validator) { this.validator = validator; } /** * Provide validation hints (e.g. JSR-303 validation groups). Note that the constructor * automatically detects validation hints specified on a view state or a transition. * Therefore use of this method should not be needed. */ public void setValidationHints(Object[] validationHints) { this.validationHints = validationHints; } /** * Invoke the validators available by convention. */ public void validate() { if (this.validator != null) { invokeValidatorDefaultValidateMethod(this.validator); } invokeModelValidationMethod(model); Object modelValidator = getModelValidator(); if (modelValidator != null) { invokeModelValidator(modelValidator); } } private void invokeModelValidationMethod(Object model) { invokeValidateMethodForCurrentState(model); invokeDefaultValidateMethod(model); } private boolean invokeValidateMethodForCurrentState(Object model) { String methodName = "validate" + StringUtils.capitalize(requestContext.getCurrentState().getId()); // preferred Method validateMethod = ReflectionUtils.findMethod(model.getClass(), methodName, ValidationContext.class); if (validateMethod != null) { if (logger.isDebugEnabled()) { logger.debug("Invoking current state model validation method '" + methodName + "(ValidationContext)'"); } ReflectionUtils.invokeMethod(validateMethod, model, new DefaultValidationContext(requestContext, eventId, mappingResults)); return true; } // web flow 2.0.3 or < compatibility only validateMethod = ReflectionUtils.findMethod(model.getClass(), methodName, MessageContext.class); if (validateMethod != null) { ReflectionUtils.invokeMethod(validateMethod, model, requestContext.getMessageContext()); return true; } // mvc 2 compatibility only validateMethod = ReflectionUtils.findMethod(model.getClass(), methodName, Errors.class); if (validateMethod != null) { MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName, model, expressionParser, messageCodesResolver, mappingResults); if (logger.isDebugEnabled()) { logger.debug("Invoking current state model validation method '" + methodName + "(Errors)'"); } ReflectionUtils.invokeMethod(validateMethod, model, errors); return true; } return false; } private boolean invokeDefaultValidateMethod(Object model) { // preferred Method validateMethod = ReflectionUtils.findMethod(model.getClass(), "validate", ValidationContext.class); if (validateMethod != null) { if (logger.isDebugEnabled()) { logger.debug("Invoking default model validation method 'validate(ValidationContext)'"); } ReflectionUtils.invokeMethod(validateMethod, model, new DefaultValidationContext(requestContext, eventId, mappingResults)); return true; } // mvc 2 compatibility only validateMethod = ReflectionUtils.findMethod(model.getClass(), "validate", Errors.class); if (validateMethod != null) { if (logger.isDebugEnabled()) { logger.debug("Invoking default model validation method 'validate(Errors)'"); } MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName, model, expressionParser, messageCodesResolver, mappingResults); ReflectionUtils.invokeMethod(validateMethod, model, errors); return true; } return false; } private Object getModelValidator() { BeanFactory beanFactory = requestContext.getActiveFlow().getApplicationContext(); if (beanFactory != null && StringUtils.hasText(modelName)) { String validatorName = modelName + "Validator"; if (beanFactory.containsBean(validatorName)) { return beanFactory.getBean(validatorName); } } return null; } private void invokeModelValidator(Object validator) { invokeValidatorValidateMethodForCurrentState(validator); invokeValidatorDefaultValidateMethod(validator); } private boolean invokeValidatorValidateMethodForCurrentState(Object validator) { String methodName = "validate" + StringUtils.capitalize(requestContext.getCurrentState().getId()); // preferred Method validateMethod = findValidationMethod(model, validator, methodName, ValidationContext.class); if (validateMethod != null) { if (logger.isDebugEnabled()) { logger.debug("Invoking current state validator method '" + ClassUtils.getShortName(validator.getClass()) + "." + methodName + "(" + ClassUtils.getShortName(model.getClass()) + ", ValidationContext)'"); } ReflectionUtils.invokeMethod(validateMethod, validator, model, new DefaultValidationContext(requestContext, eventId, mappingResults)); return true; } // mvc 2 compatibility only validateMethod = findValidationMethod(model, validator, methodName, Errors.class); if (validateMethod != null) { if (logger.isDebugEnabled()) { logger.debug("Invoking current state validator method '" + ClassUtils.getShortName(validator.getClass()) + "." + methodName + "(" + ClassUtils.getShortName(model.getClass()) + ", Errors)'"); } MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName, model, expressionParser, messageCodesResolver, mappingResults); ReflectionUtils.invokeMethod(validateMethod, validator, model, errors); return true; } // web flow 2.0.0 to 2.0.3 compatibility only [to remove in web flow 3] validateMethod = findValidationMethod(model, validator, methodName, MessageContext.class); if (validateMethod != null) { ReflectionUtils.invokeMethod(validateMethod, validator, model, requestContext.getMessageContext()); return true; } return false; } private boolean invokeValidatorDefaultValidateMethod(Object validator) { if (validator instanceof Validator) { // Spring Framework Validator type Validator springValidator = (Validator) validator; if (logger.isDebugEnabled()) { logger.debug("Invoking Spring Validator '" + ClassUtils.getShortName(validator.getClass()) + "'"); } if (springValidator.supports(model.getClass())) { MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName, model, expressionParser, messageCodesResolver, mappingResults); if (this.validationHints != null) { if (springValidator instanceof SmartValidator) { ((SmartValidator) springValidator).validate(model, errors, this.validationHints); } else { logger.warn("Validation hints provided but validator not an instance of SmartValidator: [" + springValidator.getClass().getName() + "]"); } } else { springValidator.validate(model, errors); } } else { if (logger.isDebugEnabled()) { logger.debug("Spring Validator '" + ClassUtils.getShortName(validator.getClass()) + "' doesn't support model class " + model.getClass()); } } return true; } // preferred Method validateMethod = findValidationMethod(model, validator, "validate", ValidationContext.class); if (validateMethod != null) { if (logger.isDebugEnabled()) { logger.debug("Invoking default validator method '" + ClassUtils.getShortName(validator.getClass()) + ".validate(" + ClassUtils.getShortName(model.getClass()) + ", ValidationContext)'"); } ReflectionUtils.invokeMethod(validateMethod, validator, model, new DefaultValidationContext(requestContext, eventId, mappingResults)); return true; } // mvc 2 compatibility only validateMethod = findValidationMethod(model, validator, "validate", Errors.class); if (validateMethod != null) { if (logger.isDebugEnabled()) { logger.debug("Invoking default validator method '" + ClassUtils.getShortName(validator.getClass()) + ".validate(" + ClassUtils.getShortName(model.getClass()) + ", Errors)'"); } MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName, model, expressionParser, messageCodesResolver, mappingResults); ReflectionUtils.invokeMethod(validateMethod, validator, model, errors); return true; } return false; } private Method findValidationMethod(Object model, Object validator, String methodName, Class context) { Class modelClass = AopUtils.getTargetClass(model); List> modelSearchClasses = new ArrayList>(); while (modelClass != null) { modelSearchClasses.add(modelClass); modelClass = modelClass.getSuperclass(); } for (Class searchClass : modelSearchClasses) { Method method = ReflectionUtils.findMethod(validator.getClass(), methodName, searchClass, context); if (method != null) { return method; } } return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy