/**
* Copyright 2005-2016 hdiv.org
*
* 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.hdiv.config.xml;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.hdiv.config.factory.ValidationRepositoryFactoryBean;
import org.hdiv.config.factory.ValidationRepositoryFactoryBean.ValidationTargetData;
import org.hdiv.config.validations.DefaultValidationParser;
import org.hdiv.config.validations.DefaultValidationParser.ValidationParam;
import org.hdiv.validator.DefaultEditableDataValidationProvider;
import org.hdiv.validator.IValidation;
import org.hdiv.validator.Validation;
import org.hdiv.validator.ValidationRepository;
import org.hdiv.web.validator.EditableParameterValidator;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ListFactoryBean;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.ClassUtils;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* BeanDefinitionParser for <hdiv:editableValidations> element.
*/
public class EditableValidationsBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
public static final String EDITABLE_VALIDATION_PROVIDER_BEAN_NAME = "org.hdiv.validator.EditableDataValidationProvider";
public static final String DEFAULT_EDITABLE_VALIDATIONS_BEAN_NAME = "org.hdiv.defaultEditableValidations";
public static final String EDITABLE_VALIDATOR_BEAN_NAME = "hdivEditableValidator";
/**
* Is Spring MVC in classpath?
*/
private final boolean springMvcPresent = ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet",
EditableValidationsBeanDefinitionParser.class.getClassLoader());
/**
* Is JSR303 library in classpath?
*/
private static boolean jsr303Present = ClassUtils.isPresent("javax.validation.Validator",
EditableValidationsBeanDefinitionParser.class.getClassLoader());
/* HDIV module present flags */
protected final boolean springMvcModulePresent = ClassUtils.isPresent("org.hdiv.web.servlet.support.HdivRequestDataValueProcessor",
ConfigBeanDefinitionParser.class.getClassLoader());
/*
* (non-Javadoc)
*
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element,
* org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(final Element element, final AbstractBeanDefinition definition, final ParserContext parserContext) {
return EDITABLE_VALIDATION_PROVIDER_BEAN_NAME;
}
/*
* (non-Javadoc)
*
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
*/
@Override
protected Class<?> getBeanClass(final Element element) {
return DefaultEditableDataValidationProvider.class;
}
/*
* (non-Javadoc)
*
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element,
* org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
*/
@Override
protected void doParse(final Element element, final ParserContext parserContext, final BeanDefinitionBuilder bean) {
Object source = parserContext.extractSource(element);
Map<ValidationTargetData, List<String>> validationsData = new LinkedHashMap<ValidationTargetData, List<String>>();
List<IValidation> defaultValidations = new ArrayList<IValidation>();
RuntimeBeanReference repositoryRef = createValidationRepository(source, parserContext, validationsData, defaultValidations);
bean.getBeanDefinition().getPropertyValues().addPropertyValue("validationRepository", repositoryRef);
// Register default editable validation
boolean registerDefaults = true;
Node named = element.getAttributes().getNamedItem("registerDefaults");
if (named != null) {
String registerDefaultsValue = named.getTextContent();
if (registerDefaultsValue != null) {
registerDefaults = Boolean.TRUE.toString().equalsIgnoreCase(registerDefaultsValue);
}
}
if (registerDefaults) {
// Create beans for default validations
List<IValidation> defaultVals = createDefaultEditableValidations(element, parserContext);
defaultValidations.addAll(defaultVals);
}
NodeList list = element.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE && "validationRule".equalsIgnoreCase(node.getLocalName())) {
processValidationRule(node, validationsData);
}
}
if (springMvcPresent && springMvcModulePresent) {
parserContext.getRegistry().registerBeanDefinition(EDITABLE_VALIDATOR_BEAN_NAME, createValidator(source, parserContext));
}
}
protected RuntimeBeanReference createValidationRepository(final Object source, final ParserContext parserContext,
final Map<ValidationTargetData, List<String>> validationsData, final List<IValidation> defaultValidations) {
RootBeanDefinition bean = new RootBeanDefinition(ValidationRepositoryFactoryBean.class);
bean.setSource(source);
bean.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
RuntimeBeanReference beanRef = new RuntimeBeanReference(ConfigBeanDefinitionParser.PATTERN_MATCHER_FACTORY_NAME);
bean.getPropertyValues().addPropertyValue("patternMatcherFactory", beanRef);
bean.getPropertyValues().addPropertyValue("defaultValidations", defaultValidations);
bean.getPropertyValues().addPropertyValue("validationsData", validationsData);
String name = ValidationRepository.class.getName();
parserContext.getRegistry().registerBeanDefinition(name, bean);
return new RuntimeBeanReference(name);
}
/**
* Initialize Map with url, parameter and ValidationRule data.
*
* @param node processing xml node
* @param validationsData Map with url, parameter and ValidationRule data
*/
protected void processValidationRule(final Node node, final Map<ValidationTargetData, List<String>> validationsData) {
String value = node.getTextContent();
List<String> ids = convertToList(value);
NamedNodeMap attributes = node.getAttributes();
Node urlNode = attributes.getNamedItem("url");
Node paramsNode = attributes.getNamedItem("parameters");
if (urlNode != null || paramsNode != null) {
ValidationTargetData data = new ValidationTargetData();
if (urlNode != null) {
String url = urlNode.getTextContent();
data.setUrl(url);
}
if (paramsNode != null) {
String params = paramsNode.getTextContent();
List<String> paramsList = convertToList(params);
data.setParams(paramsList);
}
boolean enableDefaults = true;
Node named = attributes.getNamedItem("enableDefaults");
if (named != null) {
String enableDefaultsVal = named.getTextContent();
if (enableDefaultsVal != null) {
enableDefaults = Boolean.TRUE.toString().equalsIgnoreCase(enableDefaultsVal);
}
}
if (enableDefaults) {
// Add defaults
ids.add(DEFAULT_EDITABLE_VALIDATIONS_BEAN_NAME);
}
validationsData.put(data, ids);
}
}
/**
* Convert String with bean id's in List
*
* @param data String data
* @return List with bean id's
*/
protected List<String> convertToList(final String data) {
if (data == null || data.length() == 0) {
return new ArrayList<String>();
}
String[] result = data.split(",");
List<String> list = new ArrayList<String>();
for (String val : result) {
list.add(val.trim());
}
return list;
}
/**
* Create beans for the default editable validations.
*
* @param element xml element
* @param parserContext xml parser context
* @return default validations
*/
protected List<IValidation> createDefaultEditableValidations(final Element element, final ParserContext parserContext) {
// Load validations from xml
DefaultValidationParser parser = new DefaultValidationParser();
parser.readDefaultValidations();
List<Map<ValidationParam, String>> validations = parser.getValidations();
List<IValidation> defaultValidations = new ArrayList<IValidation>();
for (Map<ValidationParam, String> validation : validations) {
// Map contains validation id and regex extracted from the xml
String id = validation.get(ValidationParam.ID);
String regex = validation.get(ValidationParam.REGEX);
// Create validation instance
Validation validationBean = new Validation();
validationBean.setName(id);
validationBean.setDefaultValidation(true);
validationBean.setRejectedPattern(regex);
defaultValidations.add(validationBean);
}
Object source = parserContext.extractSource(element);
RootBeanDefinition bean = new RootBeanDefinition(ListFactoryBean.class);
bean.setSource(source);
bean.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
bean.getPropertyValues().addPropertyValue("sourceList", defaultValidations);
// Register validation list bean
parserContext.getRegistry().registerBeanDefinition(DEFAULT_EDITABLE_VALIDATIONS_BEAN_NAME, bean);
return defaultValidations;
}
protected RootBeanDefinition createValidator(final Object source, final ParserContext parserContext) {
RootBeanDefinition bean = new RootBeanDefinition(EditableParameterValidator.class);
bean.setSource(source);
bean.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
if (jsr303Present) {
RootBeanDefinition validatorDef = new RootBeanDefinition(LocalValidatorFactoryBean.class);
validatorDef.setSource(source);
validatorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String validatorName = parserContext.getReaderContext().registerWithGeneratedName(validatorDef);
parserContext.registerComponent(new BeanComponentDefinition(validatorDef, validatorName));
bean.getPropertyValues().addPropertyValue("innerValidator", new RuntimeBeanReference(validatorName));
}
return bean;
}
}