diff --git a/spring-core/src/main/java/org/springframework/util/xml/AbstractXMLStreamReader.java b/spring-core/src/main/java/org/springframework/util/xml/AbstractXMLStreamReader.java index 1c92c458fcd0..810464a94991 100644 --- a/spring-core/src/main/java/org/springframework/util/xml/AbstractXMLStreamReader.java +++ b/spring-core/src/main/java/org/springframework/util/xml/AbstractXMLStreamReader.java @@ -153,11 +153,25 @@ public int nextTag() throws XMLStreamException { } @Override - public void require(int expectedType, String namespaceURI, String localName) throws XMLStreamException { + public void require(int expectedType, @Nullable String namespaceURI, @Nullable String localName) + throws XMLStreamException { + int eventType = getEventType(); if (eventType != expectedType) { throw new XMLStreamException("Expected [" + expectedType + "] but read [" + eventType + "]"); } + // This reader only exposes a name and namespace for START_ELEMENT and END_ELEMENT, + // so a non-null namespace or local name can only ever match one of those events. + if ((namespaceURI != null || localName != null) && + eventType != XMLStreamConstants.START_ELEMENT && eventType != XMLStreamConstants.END_ELEMENT) { + throw new XMLStreamException("Current event is not a START_ELEMENT or END_ELEMENT"); + } + if (localName != null && !localName.equals(getLocalName())) { + throw new XMLStreamException("Expected local name [" + localName + "] but read [" + getLocalName() + "]"); + } + if (namespaceURI != null && !namespaceURI.equals(getNamespaceURI())) { + throw new XMLStreamException("Expected namespace [" + namespaceURI + "] but read [" + getNamespaceURI() + "]"); + } } @Override diff --git a/spring-core/src/test/java/org/springframework/util/xml/XMLEventStreamReaderTests.java b/spring-core/src/test/java/org/springframework/util/xml/XMLEventStreamReaderTests.java index 40ef3b6410d0..4c56c6284e70 100644 --- a/spring-core/src/test/java/org/springframework/util/xml/XMLEventStreamReaderTests.java +++ b/spring-core/src/test/java/org/springframework/util/xml/XMLEventStreamReaderTests.java @@ -21,6 +21,8 @@ import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stax.StAXSource; @@ -34,6 +36,7 @@ import org.springframework.core.testfixture.xml.XmlContent; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; class XMLEventStreamReaderTests { @@ -68,4 +71,25 @@ void readCorrect() throws Exception { assertThat(XmlContent.from(writer)).isSimilarTo(XML, nodeFilter); } + @Test // require(type, namespaceURI, localName) must validate the name and namespace + void requireValidatesNamespaceAndLocalName() throws Exception { + advanceToStartElement("root"); + + streamReader.require(XMLStreamConstants.START_ELEMENT, null, "root"); + streamReader.require(XMLStreamConstants.START_ELEMENT, "namespace", "root"); + streamReader.require(XMLStreamConstants.START_ELEMENT, "namespace", null); + + assertThatExceptionOfType(XMLStreamException.class).isThrownBy(() -> + streamReader.require(XMLStreamConstants.START_ELEMENT, null, "wrong")); + assertThatExceptionOfType(XMLStreamException.class).isThrownBy(() -> + streamReader.require(XMLStreamConstants.START_ELEMENT, "wrong-namespace", "root")); + } + + private void advanceToStartElement(String localName) throws Exception { + while (streamReader.getEventType() != XMLStreamConstants.START_ELEMENT || + !streamReader.getLocalName().equals(localName)) { + streamReader.next(); + } + } + }