Java tutorial
/* * Copyright 2010-2013 Duplichien, Wicksell, Springjutsu.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.springjutsu.validation.namespace; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.xml.DomUtils; import org.springjutsu.validation.rules.CollectionStrategy; import org.springjutsu.validation.rules.RuleErrorMode; import org.springjutsu.validation.rules.ValidationContext; import org.springjutsu.validation.rules.ValidationEntity; import org.springjutsu.validation.rules.ValidationRule; import org.springjutsu.validation.rules.ValidationTemplate; import org.springjutsu.validation.rules.ValidationTemplateReference; import org.springjutsu.validation.util.PathUtils; import org.springjutsu.validation.util.RequestUtils; import org.w3c.dom.Element; import org.w3c.dom.NodeList; /** * Parses XML from the validation namespace into a @link{ValidationEntity} * object to be stored in the @link{ValidationEntityContainer} * @author Clark Duplichien * @author Taylor Wicksell * */ public class ValidationEntityDefinitionParser implements BeanDefinitionParser { /** * Do actual parsing. * Since rules may be nested, delegate to {@link #parseNestedRules(Element, Class)} * where necessary. */ public BeanDefinition parse(Element entityNode, ParserContext parserContext) { RootBeanDefinition entityDefinition = new RootBeanDefinition(ValidationEntity.class); String className = entityNode.getAttribute("class"); Class<?> modelClass; try { modelClass = Class.forName(className); } catch (ClassNotFoundException cnfe) { throw new ValidationParseException("Class " + className + " does not exist as a model class."); } List<String> excludePaths = new ArrayList<String>(); NodeList excludes = entityNode.getElementsByTagNameNS(entityNode.getNamespaceURI(), "recursion-exclude"); for (int excludeNbr = 0; excludeNbr < excludes.getLength(); excludeNbr++) { Element excludeNode = (Element) excludes.item(excludeNbr); String path = excludeNode.getAttribute("propertyName"); if (path.contains(".")) { throw new ValidationParseException("Invalid recursion-exclude property name \"" + path + "\" Exclude paths should not be nested fields."); } else { excludePaths.add(path); } } List<String> includePaths = new ArrayList<String>(); NodeList includes = entityNode.getElementsByTagNameNS(entityNode.getNamespaceURI(), "recursion-include"); for (int includeNbr = 0; includeNbr < includes.getLength(); includeNbr++) { Element includeNode = (Element) includes.item(includeNbr); String path = includeNode.getAttribute("propertyName"); if (path.contains(".")) { throw new ValidationParseException("Invalid recursion-include property name \"" + path + "\" Include paths should not be nested fields."); } else { includePaths.add(path); } } ValidationStructure validationStructure = parseNestedValidation(entityNode, modelClass); List<ValidationTemplate> templates = new ArrayList<ValidationTemplate>(); NodeList templateNodes = entityNode.getElementsByTagNameNS(entityNode.getNamespaceURI(), "template"); for (int templateNbr = 0; templateNbr < templateNodes.getLength(); templateNbr++) { Element templateNode = (Element) templateNodes.item(templateNbr); String templateName = templateNode.getAttribute("name"); ValidationStructure templateValidation = parseNestedValidation(templateNode, modelClass); ValidationTemplate template = new ValidationTemplate(templateName, modelClass); template.setRules(templateValidation.rules); template.setTemplateReferences(templateValidation.refs); template.setValidationContexts(templateValidation.contexts); templates.add(template); } entityDefinition.getPropertyValues().add("rules", validationStructure.rules); entityDefinition.getPropertyValues().add("templateReferences", validationStructure.refs); entityDefinition.getPropertyValues().add("validationContexts", validationStructure.contexts); entityDefinition.getPropertyValues().add("validationTemplates", templates); entityDefinition.getPropertyValues().add("validationClass", modelClass); entityDefinition.getPropertyValues().add("includedPaths", includePaths); entityDefinition.getPropertyValues().add("excludedPaths", excludePaths); String entityName = parserContext.getReaderContext().registerWithGeneratedName(entityDefinition); parserContext.registerComponent(new BeanComponentDefinition(entityDefinition, entityName)); return null; } /** * Parses nested validation rule and template-ref structures * @param ruleNode the ValidationRule node * @param modelClass the model class * @return a @link{ValidationStructure} object whose * nested rules and template references are those * rules and template references stemming from the ruleNode. */ protected ValidationStructure parseNestedValidation(Element ruleNode, Class<?> modelClass) { ValidationStructure structure = new ValidationStructure(); if (ruleNode == null) { return structure; } List<Element> validationRuleNodes = DomUtils.getChildElementsByTagName(ruleNode, "rule"); if (validationRuleNodes != null) { for (Element rule : validationRuleNodes) { String path = rule.getAttribute("path"); if (path != null && path.length() > 0 && !path.contains("${") && !PathUtils.pathExists(modelClass, path)) { throw new ValidationParseException( "Path \"" + path + "\" does not exist on class " + modelClass.getCanonicalName()); } String type = rule.getAttribute("type"); String value = rule.getAttribute("value"); String message = rule.getAttribute("message"); String errorPath = rule.getAttribute("errorPath"); String collectionStrategy = rule.getAttribute("collectionStrategy"); String onFail = rule.getAttribute("onFail"); ValidationRule validationRule = new ValidationRule(path, type, value); validationRule.setMessage(message); validationRule.setErrorPath(errorPath); validationRule.setCollectionStrategy(CollectionStrategy.forXmlValue(collectionStrategy)); validationRule.setOnFail(RuleErrorMode.forXmlValue(onFail)); ValidationStructure subStructure = parseNestedValidation(rule, modelClass); validationRule.setRules(subStructure.rules); validationRule.setTemplateReferences(subStructure.refs); validationRule.setValidationContexts(subStructure.contexts); structure.rules.add(validationRule); } } List<Element> validationTemplateReferenceNodes = DomUtils.getChildElementsByTagName(ruleNode, "template-ref"); if (validationTemplateReferenceNodes != null) { for (Element templateReference : validationTemplateReferenceNodes) { String basePath = templateReference.getAttribute("basePath"); if (basePath != null && basePath.length() > 0 && !basePath.contains("${") && !PathUtils.pathExists(modelClass, basePath)) { throw new ValidationParseException( "Path \"" + basePath + "\" does not exist on class " + modelClass.getCanonicalName()); } String templateName = templateReference.getAttribute("templateName"); ValidationTemplateReference templateRef = new ValidationTemplateReference(basePath, templateName); structure.refs.add(templateRef); } } List<Element> formNodes = DomUtils.getChildElementsByTagName(ruleNode, "form"); if (formNodes != null) { for (Element formNode : formNodes) { // get form paths. String formPaths = formNode.getAttribute("path"); Set<String> formConstraints = new HashSet<String>(); for (String formPath : formPaths.split(",")) { String candidateFormPath = formPath.trim(); candidateFormPath = RequestUtils.removeLeadingAndTrailingSlashes(candidateFormPath).trim(); if (!candidateFormPath.isEmpty()) { formConstraints.add(candidateFormPath); } } ValidationContext formContext = new ValidationContext(); formContext.setType("form"); ValidationContext flowContext = new ValidationContext(); flowContext.setType("webflow"); for (String formConstraint : formConstraints) { if (formConstraint.contains(":")) { flowContext.getQualifiers().add(formConstraint); } else { formContext.getQualifiers().add(formConstraint); } } // get rules & templates. ValidationStructure formSpecificValidationStructure = parseNestedValidation(formNode, modelClass); formContext.setRules(formSpecificValidationStructure.rules); formContext.setTemplateReferences(formSpecificValidationStructure.refs); formContext.setValidationContexts(formSpecificValidationStructure.contexts); flowContext.setRules(formSpecificValidationStructure.rules); flowContext.setTemplateReferences(formSpecificValidationStructure.refs); flowContext.setValidationContexts(formSpecificValidationStructure.contexts); if (!formContext.getQualifiers().isEmpty()) { structure.contexts.add(formContext); } if (!flowContext.getQualifiers().isEmpty()) { structure.contexts.add(flowContext); } } } List<Element> groupNodes = DomUtils.getChildElementsByTagName(ruleNode, "group"); if (groupNodes != null) { for (Element groupNode : groupNodes) { // get form paths. String groupQualifiers = groupNode.getAttribute("qualifiers"); Set<String> groupConstraints = new HashSet<String>(); for (String qualifier : groupQualifiers.split(",")) { groupConstraints.add(qualifier.trim()); } ValidationContext groupContext = new ValidationContext(); groupContext.setType("group"); groupContext.setQualifiers(groupConstraints); // get rules & templates. ValidationStructure groupSpecificValidationStructure = parseNestedValidation(groupNode, modelClass); groupContext.setRules(groupSpecificValidationStructure.rules); groupContext.setTemplateReferences(groupSpecificValidationStructure.refs); groupContext.setValidationContexts(groupSpecificValidationStructure.contexts); structure.contexts.add(groupContext); } } List<Element> contextNodes = DomUtils.getChildElementsByTagName(ruleNode, "context"); if (contextNodes != null) { for (Element contextNode : contextNodes) { // get form paths. String contextQualifiers = contextNode.getAttribute("qualifiers"); Set<String> contextConstraints = new HashSet<String>(); for (String qualifier : contextQualifiers.split(",")) { contextConstraints.add(qualifier.trim()); } ValidationContext context = new ValidationContext(); context.setType(contextNode.getAttribute("type")); context.setQualifiers(contextConstraints); // get rules & templates. ValidationStructure contextSpecificValidationStructure = parseNestedValidation(contextNode, modelClass); context.setRules(contextSpecificValidationStructure.rules); context.setTemplateReferences(contextSpecificValidationStructure.refs); context.setValidationContexts(contextSpecificValidationStructure.contexts); structure.contexts.add(context); } } return structure; } /** * Exception when validation cannot be parsed. * @author Clark Duplichien * @author Taylor Wicksell * */ public class ValidationParseException extends RuntimeException { private static final long serialVersionUID = 1L; public ValidationParseException() { super(); } public ValidationParseException(String message) { super(message); } } protected class ValidationStructure { public List<ValidationRule> rules = new ArrayList<ValidationRule>(); public List<ValidationTemplateReference> refs = new ArrayList<ValidationTemplateReference>(); public List<ValidationContext> contexts = new ArrayList<ValidationContext>(); } }