From 18aceb73fc0004bea302d41aa2c6b20798e423cc Mon Sep 17 00:00:00 2001 From: Gunnar Hillert Date: Thu, 20 Oct 2011 16:59:15 -0400 Subject: [PATCH] INT-2191 - XPath Expression Namespace Parser: Improve Validation --- .../xml/config/XPathExpressionParser.java | 44 ++++----- .../config/XPathExpressionParserTests.java | 96 +++++++++++++++++-- 2 files changed, 109 insertions(+), 31 deletions(-) diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathExpressionParser.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathExpressionParser.java index 715db852cd..044c24b7b4 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathExpressionParser.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/config/XPathExpressionParser.java @@ -17,12 +17,9 @@ package org.springframework.integration.xml.config; import java.util.HashMap; +import java.util.List; import java.util.Map; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; @@ -30,11 +27,13 @@ import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; import org.springframework.xml.xpath.XPathExpressionFactory; +import org.w3c.dom.Element; /** * Parser for the <xpath-expression> element. - * + * * @author Jonas Partner */ public class XPathExpressionParser extends AbstractSingleBeanDefinitionParser { @@ -61,36 +60,37 @@ public class XPathExpressionParser extends AbstractSingleBeanDefinitionParser { String nsPrefix = element.getAttribute("ns-prefix"); String nsUri = element.getAttribute("ns-uri"); String namespaceMapRef = element.getAttribute("namespace-map"); + + List mapElements = DomUtils.getChildElementsByTagName(element, "map"); + boolean prefixProvided = StringUtils.hasText(nsPrefix); boolean namespaceProvided = StringUtils.hasText(nsUri); boolean namespaceMapProvided = StringUtils.hasText(namespaceMapRef); + + boolean mapSubElementProvided = !mapElements.isEmpty(); + if (prefixProvided || namespaceProvided) { Assert.isTrue(prefixProvided && namespaceProvided, "Both 'ns-prefix' and 'ns-uri' must be specified if one is specified."); - Assert.isTrue(!namespaceMapProvided, "It is not valid to specify both namespace and namespace-map."); + Assert.isTrue(!namespaceMapProvided, "It is not valid to specify both, the namespace attributes ('ns-prefix' and 'ns-uri') and the 'namespace-map' attribute."); + Assert.isTrue(!mapSubElementProvided, "It is not valid to specify both, the namespace attributes ('ns-prefix' and 'ns-uri') and the 'map' sub-element."); + } else if (mapSubElementProvided) { + Assert.isTrue(!namespaceMapProvided, "It is not valid to specify both, the 'namespace-map' attribute and the 'map' sub-element."); } + builder.setFactoryMethod("createXPathExpression"); builder.addConstructorArgValue(expression); + if (prefixProvided) { - Map namespaceMap = new HashMap(); + Map namespaceMap = new HashMap(1); namespaceMap.put(nsPrefix, nsUri); builder.addConstructorArgValue(namespaceMap); - } - else if (StringUtils.hasText(namespaceMapRef)) { + } else if (namespaceMapProvided) { builder.addConstructorArgReference(namespaceMapRef); - } - else if (element.getChildNodes().getLength() > 0) { - NodeList nodeList = element.getChildNodes(); - Element mapElement = null; - int elementCount = 0; - for (int i = 0; i < nodeList.getLength(); i++) { - Node currentNode = nodeList.item(i); - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - mapElement = (Element) currentNode; - elementCount++; - } - } - Assert.isTrue(elementCount == 1, "only one namespace map child allowed"); + } else if (mapSubElementProvided) { + + Element mapElement = mapElements.get(0); + if (mapElement != null) { builder.addConstructorArgValue(this.parseNamespaceMapElement( mapElement, parserContext, builder.getBeanDefinition())); diff --git a/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathExpressionParserTests.java b/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathExpressionParserTests.java index 7c4b54c409..cb4d5b35b9 100644 --- a/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathExpressionParserTests.java +++ b/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XPathExpressionParserTests.java @@ -16,21 +16,24 @@ package org.springframework.integration.xml.config; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import org.junit.Test; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.integration.xml.util.XmlTestUtil; import org.springframework.xml.xpath.XPathExpression; +import org.xml.sax.SAXParseException; public class XPathExpressionParserTests { - + @Test public void testSimpleStringExpression() throws Exception { String xmlDoc = ""; XPathExpression xPathExpression = getXPathExpression(xmlDoc); assertEquals("outputOne",xPathExpression.evaluateAsString(XmlTestUtil.getDocumentForString("outputOne"))); } - + @Test public void testNamespacedStringExpression() throws Exception { String xmlDoc = ""; @@ -38,7 +41,7 @@ public class XPathExpressionParserTests { assertEquals("outputOne",xPathExpression.evaluateAsString(XmlTestUtil.getDocumentForString("outputOne"))); assertEquals("",xPathExpression.evaluateAsString(XmlTestUtil.getDocumentForString("outputOne"))); } - + @Test public void testStringExpressionWithNamespaceMapReference() throws Exception { StringBuffer xmlDoc = new StringBuffer(""); @@ -47,17 +50,39 @@ public class XPathExpressionParserTests { assertEquals("outputOne",xPathExpression.evaluateAsString(XmlTestUtil.getDocumentForString("outputOne"))); assertEquals("",xPathExpression.evaluateAsString(XmlTestUtil.getDocumentForString("outputOne"))); } - + @Test public void testStringExpressionWithNamespaceInnerBean() throws Exception { - StringBuffer xmlDoc = new StringBuffer(""); - xmlDoc.append("").append(""); + + StringBuilder xmlDoc = new StringBuilder("") + .append(" ") + .append(""); + XPathExpression xPathExpression = getXPathExpression(xmlDoc.toString()); assertEquals("outputOne",xPathExpression.evaluateAsString(XmlTestUtil.getDocumentForString("outputOne"))); assertEquals("",xPathExpression.evaluateAsString(XmlTestUtil.getDocumentForString("outputOne"))); } - - + + @Test + public void testStringExpressionWithMultipleNamespaceInnerBean() throws Exception { + + StringBuilder xmlDoc = new StringBuilder( + "") + .append(" ") + .append(" ") + .append(""); + + try { + getXPathExpression(xmlDoc.toString()); + } catch (BeanDefinitionStoreException e) { + assertTrue(e.getCause() instanceof SAXParseException); + return; + } + + fail("Expected an Exceptions"); + + } + @Test(expected=BeanDefinitionStoreException.class) public void testNamespacePrefixButNoUri() throws Exception { String xmlDoc = ""; @@ -66,11 +91,64 @@ public class XPathExpressionParserTests { assertEquals("",xPathExpression.evaluateAsString(XmlTestUtil.getDocumentForString("outputOne"))); } - + + @Test + public void testNamespacedStringExpressionWithNamespaceMapReference() throws Exception { + StringBuilder xmlDoc = new StringBuilder(""); + xmlDoc.append(""); + + try { + getXPathExpression(xmlDoc.toString()); + } catch (BeanDefinitionStoreException e) { + assertEquals("It is not valid to specify both, the namespace attributes ('ns-prefix' and 'ns-uri') and the 'namespace-map' attribute.", e.getCause().getMessage()); + return; + } + + fail("Expected an Exceptions"); + + } + + @Test + public void testNamespacedStringExpressionWithNamespaceInnerBean() throws Exception { + StringBuilder xmlDoc = new StringBuilder( + "") + .append(" ") + .append(""); + try { + getXPathExpression(xmlDoc.toString()); + } catch (BeanDefinitionStoreException e) { + assertEquals("It is not valid to specify both, the namespace attributes ('ns-prefix' and 'ns-uri') and the 'map' sub-element.", e.getCause().getMessage()); + return; + } + + fail("Expected an Exceptions"); + + } + + @Test + public void testStringExpressionWithNamespaceInnerBeanAndWithNamespaceMapReference() throws Exception { + StringBuilder xmlDoc = new StringBuilder( + "") + .append(" ") + .append("") + .append(""); + try { + getXPathExpression(xmlDoc.toString()); + } catch (BeanDefinitionStoreException e) { + assertEquals("It is not valid to specify both, the 'namespace-map' attribute and the 'map' sub-element.", e.getCause().getMessage()); + return; + } + + fail("Expected an Exceptions"); + + } + public XPathExpression getXPathExpression(String contextXml){ TestXmlApplicationContext ctx = TestXmlApplicationContextHelper.getTestAppContext(contextXml); return (XPathExpression) ctx.getBean("xpathExpression"); } + + }