/*
* Copyright 2004-2005 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.springmodules.orm.support.validation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.util.ClassUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.Validator;
/**
* <p>An instance that holds validators tied to specific keys. These keys are determined by the configuration rules that have to be implemented by classes that extend this class.
*
* <p>{@link ValidatingSupport#validate(Object, String)} is a static method called by classes that want to execute the validators tied to a specific key.
*
* <p>Known subclasses of this class are:
*
* <p>To support scoped validation rules instances bind and unbind themselves to the current thread. When an instance is already
* bound to the thread other instances will register and unregister themselves with the thread-bound instance within the boundries of their scope.
*
* <p>When a least one validator fails to validate a domain object a org.springframework.validation.Errors containing the errors
* must be converted to a RuntimeException. This conversion is pluggable by means of the org.springmodules.orm.support.validation.ErrorsToRuntimeExceptionTransformer
* interface.
*
* @author Steven Devijver
* @since Jun 16, 2005
* @see org.springframework.validation.Errors
* @see org.springframework.validation.Validator
* @see org.springmodules.orm.support.validation.ErrorsToRuntimeExceptionTransformer
* @see org.springmodules.orm.support.validation.PolevValidatingMethodInterceptor
* @see org.springmodules.orm.hibernate3.support.PolevValidatingInterceptor
* @see org.springmodules.orm.hibernate.support.PolevValidatingInterceptor
*/
public abstract class ValidatingSupport {
protected static final String ALL = "all";
private Map validators = null;
private ErrorsToRuntimeExceptionTransformer transformer = new DefaultErrorsToRuntimeExceptionTransformer();
private static ThreadLocal otherValidatingSupportList = new ThreadLocal();
private static ThreadLocal validatingSupport = new ThreadLocal();
public ValidatingSupport() {
super();
}
protected void addValidator(String event, Class clazz, Validator validator) {
Map validatorMap = null;
String upperCaseEvent = event.toUpperCase();
if (this.validators == null) {
this.validators = new HashMap();
}
if (this.validators.containsKey(upperCaseEvent)) {
validatorMap = ((Map)this.validators.get(upperCaseEvent));
} else {
validatorMap = new HashMap();
this.validators.put(upperCaseEvent, validatorMap);
}
validatorMap.put(clazz, validator);
}
private Map getValidators(String event) {
Map validatorMap = new HashMap();
String upperCaseEvent = event.toUpperCase();
if (this.validators.containsKey(upperCaseEvent)) {
validatorMap.putAll(((Map)this.validators.get(upperCaseEvent)));
}
if (this.validators.containsKey(ALL)) {
validatorMap.putAll(((Map)this.validators.get(ALL)));
}
return validatorMap;
}
protected Class loadClass(String className) {
try {
return ClassUtils.forName(className);
} catch (ClassNotFoundException e) {
throw new BeansException("Could not find class [" + className + "]", e) {};
}
}
private void doValidation(Object target, String event) {
BindException errors = new BindException(target, "target");
doValidation(target, event, errors);
for (Iterator iter = getOtherValidationSupportList().iterator(); iter.hasNext();) {
ValidatingSupport otherValidatingSupport = (ValidatingSupport)iter.next();
otherValidatingSupport.doValidation(target, event, errors);
}
if (errors.hasErrors() || errors.hasGlobalErrors()) {
throw transformer.transform(errors);
}
}
private void doValidation(Object target, String event, BindException errors) {
Map validatorsMap = getValidators(event);
Class clazz = target.getClass();
for (Iterator iter = validatorsMap.keySet().iterator(); iter.hasNext();) {
Class tmpClazz = (Class)iter.next();
Validator validator = (Validator)validatorsMap.get(tmpClazz);
if (tmpClazz.isAssignableFrom(clazz)) {
validator.validate(target, errors);
}
}
}
public void setErrorsToRuntimeExceptionTransformer(ErrorsToRuntimeExceptionTransformer transformer) {
if (transformer == null) {
throw new IllegalArgumentException("Transformer should not be null!");
}
this.transformer = transformer;
}
protected void bindValidators() {
if (validatingSupport.get() != null) {
ValidatingSupport otherValidatingSupport = (ValidatingSupport)validatingSupport.get();
otherValidatingSupport.push(this);
} else {
validatingSupport.set(this);
}
}
protected void unbindValidators() {
if (validatingSupport.get() != null) {
ValidatingSupport otherValidatingSupport = (ValidatingSupport)validatingSupport.get();
if (otherValidatingSupport.hasRegisteredChildren()) {
otherValidatingSupport.pop();
} else {
validatingSupport.set(null);
otherValidatingSupportList.set(null);
}
}
}
private List getOtherValidationSupportList() {
if (otherValidatingSupportList.get() == null) {
otherValidatingSupportList.set(new ArrayList());
}
return ((List)otherValidatingSupportList.get());
}
public void push(ValidatingSupport validatingSupport) {
getOtherValidationSupportList().add(validatingSupport);
}
public void pop() {
getOtherValidationSupportList().remove(getOtherValidationSupportList().size() - 1);
}
public boolean hasRegisteredChildren() {
return !getOtherValidationSupportList().isEmpty();
}
public static void validate(Object target, String event) {
if (validatingSupport.get() != null) {
ValidatingSupport tmpValidatingSupport = ((ValidatingSupport)validatingSupport.get());
tmpValidatingSupport.doValidation(target, event);
} else {
throw new IllegalStateException("No validators bound to current thread!");
}
}
}