/** * 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; } }