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 1c92c458fcd..1bba07dc5f3 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 @@ -190,7 +190,7 @@ abstract class AbstractXMLStreamReader implements XMLStreamReader { @Override public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) { char[] source = getTextCharacters(); - length = Math.min(length, source.length); + length = Math.min(length, source.length - sourceStart); System.arraycopy(source, sourceStart, target, targetStart, length); return length; } diff --git a/spring-core/src/main/java/org/springframework/util/xml/XMLEventStreamReader.java b/spring-core/src/main/java/org/springframework/util/xml/XMLEventStreamReader.java index e554dbd6ebd..3e8725e3d76 100644 --- a/spring-core/src/main/java/org/springframework/util/xml/XMLEventStreamReader.java +++ b/spring-core/src/main/java/org/springframework/util/xml/XMLEventStreamReader.java @@ -34,9 +34,11 @@ import javax.xml.stream.events.XMLEvent; import org.jspecify.annotations.Nullable; /** - * Implementation of the {@link javax.xml.stream.XMLStreamReader} interface that wraps a - * {@link XMLEventReader}. Useful because the StAX {@link javax.xml.stream.XMLInputFactory} - * allows one to create an event reader from a stream reader, but not vice-versa. + * Implementation of the {@link javax.xml.stream.XMLStreamReader} interface that wraps an + * {@link XMLEventReader}. + * + *

Useful because the StAX {@link javax.xml.stream.XMLInputFactory} allows one + * to create an event reader from a stream reader, but not vice-versa. * * @author Arjen Poutsma * @since 3.0 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 40ef3b6410d..def40b8be90 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,13 +21,15 @@ import java.io.StringWriter; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stax.StAXSource; import javax.xml.transform.stream.StreamResult; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.w3c.dom.Node; import org.xmlunit.util.Predicate; @@ -35,21 +37,24 @@ import org.springframework.core.testfixture.xml.XmlContent; import static org.assertj.core.api.Assertions.assertThat; +/** + * Tests for {@link XMLEventStreamReader}. + */ class XMLEventStreamReaderTests { private static final String XML = - "content" - ; + "content"; - private XMLEventStreamReader streamReader; + private final XMLEventStreamReader streamReader; - @BeforeEach - void createStreamReader() throws Exception { + + XMLEventStreamReaderTests() throws Exception { XMLInputFactory inputFactory = XMLInputFactory.newInstance(); XMLEventReader eventReader = inputFactory.createXMLEventReader(new StringReader(XML)); - streamReader = new XMLEventStreamReader(eventReader); + this.streamReader = new XMLEventStreamReader(eventReader); } + @Test void readAll() throws Exception { while (streamReader.hasNext()) { @@ -68,4 +73,30 @@ class XMLEventStreamReaderTests { assertThat(XmlContent.from(writer)).isSimilarTo(XML, nodeFilter); } + @ParameterizedTest // gh-36914 + @CsvSource(textBlock = """ + 0, content + 1, ontent + 4, ent + 6, t + 7, '' + """) + void getTextCharactersHonorsSourceStart(int sourceStart, String expected) throws Exception { + advanceToCharacters(); + + // text node is "content" (7 chars); request an oversized buffer to ensure + // getTextCharacters(sourceStart, ...) does not read past the source + char[] target = new char[10]; + int count = streamReader.getTextCharacters(sourceStart, target, 0, 10); + + assertThat(count).isEqualTo(expected.length()); + assertThat(new String(target, 0, count)).isEqualTo(expected); + } + + private void advanceToCharacters() throws Exception { + while (streamReader.getEventType() != XMLStreamConstants.CHARACTERS) { + streamReader.next(); + } + } + }