Skip to content

Commit 7653762

Browse files
committed
XMLEventReader.getElementText() properly checks for start element
Issue: SPR-17233 (cherry picked from commit 84ec382)
1 parent 89fca1b commit 7653762

File tree

3 files changed

+115
-83
lines changed

3 files changed

+115
-83
lines changed

spring-core/src/main/java/org/springframework/util/xml/AbstractXMLEventReader.java

Lines changed: 7 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,18 +18,15 @@
1818

1919
import java.util.NoSuchElementException;
2020
import javax.xml.stream.XMLEventReader;
21-
import javax.xml.stream.XMLStreamConstants;
2221
import javax.xml.stream.XMLStreamException;
23-
import javax.xml.stream.events.Characters;
24-
import javax.xml.stream.events.XMLEvent;
2522

26-
import org.springframework.lang.Nullable;
2723
import org.springframework.util.ClassUtils;
2824

2925
/**
3026
* Abstract base class for {@code XMLEventReader}s.
3127
*
3228
* @author Arjen Poutsma
29+
* @author Juergen Hoeller
3330
* @since 5.0
3431
*/
3532
abstract class AbstractXMLEventReader implements XMLEventReader {
@@ -43,7 +40,7 @@ public Object next() {
4340
return nextEvent();
4441
}
4542
catch (XMLStreamException ex) {
46-
throw new NoSuchElementException();
43+
throw new NoSuchElementException(ex.getMessage());
4744
}
4845
}
4946

@@ -53,64 +50,9 @@ public void remove() {
5350
"remove not supported on " + ClassUtils.getShortName(getClass()));
5451
}
5552

56-
@Override
57-
public String getElementText() throws XMLStreamException {
58-
checkIfClosed();
59-
if (!peek().isStartElement()) {
60-
throw new XMLStreamException("Not at START_ELEMENT");
61-
}
62-
63-
StringBuilder builder = new StringBuilder();
64-
while (true) {
65-
XMLEvent event = nextEvent();
66-
if (event.isEndElement()) {
67-
break;
68-
}
69-
else if (!event.isCharacters()) {
70-
throw new XMLStreamException(
71-
"Unexpected event [" + event + "] in getElementText()");
72-
}
73-
Characters characters = event.asCharacters();
74-
if (!characters.isIgnorableWhiteSpace()) {
75-
builder.append(event.asCharacters().getData());
76-
}
77-
}
78-
return builder.toString();
79-
}
80-
81-
@Override
82-
@Nullable
83-
public XMLEvent nextTag() throws XMLStreamException {
84-
checkIfClosed();
85-
while (true) {
86-
XMLEvent event = nextEvent();
87-
switch (event.getEventType()) {
88-
case XMLStreamConstants.START_ELEMENT:
89-
case XMLStreamConstants.END_ELEMENT:
90-
return event;
91-
case XMLStreamConstants.END_DOCUMENT:
92-
return null;
93-
case XMLStreamConstants.SPACE:
94-
case XMLStreamConstants.COMMENT:
95-
case XMLStreamConstants.PROCESSING_INSTRUCTION:
96-
continue;
97-
case XMLStreamConstants.CDATA:
98-
case XMLStreamConstants.CHARACTERS:
99-
if (!event.asCharacters().isWhiteSpace()) {
100-
throw new XMLStreamException(
101-
"Non-ignorable whitespace CDATA or CHARACTERS event in nextTag()");
102-
}
103-
break;
104-
default:
105-
throw new XMLStreamException("Received event [" + event +
106-
"], instead of START_ELEMENT or END_ELEMENT.");
107-
}
108-
}
109-
}
110-
11153
/**
112-
* Throws an {@code IllegalArgumentException} when called.
113-
* @throws IllegalArgumentException when called.
54+
* This implementation throws an {@code IllegalArgumentException} for any property.
55+
* @throws IllegalArgumentException when called
11456
*/
11557
@Override
11658
public Object getProperty(String name) throws IllegalArgumentException {
@@ -123,21 +65,12 @@ public void close() {
12365
}
12466

12567
/**
126-
* Returns {@code true} if closed; {@code false} otherwise.
127-
* @see #close()
128-
*/
129-
protected boolean isClosed() {
130-
return this.closed;
131-
}
132-
133-
/**
134-
* Checks if the reader is closed, and throws a {@code XMLStreamException} if so.
68+
* Check if the reader is closed, and throws a {@code XMLStreamException} if so.
13569
* @throws XMLStreamException if the reader is closed
13670
* @see #close()
137-
* @see #isClosed()
13871
*/
13972
protected void checkIfClosed() throws XMLStreamException {
140-
if (isClosed()) {
73+
if (this.closed) {
14174
throw new XMLStreamException("XMLEventReader has been closed");
14275
}
14376
}

spring-core/src/main/java/org/springframework/util/xml/ListBasedXMLEventReader.java

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import java.util.ArrayList;
2020
import java.util.List;
2121
import java.util.NoSuchElementException;
22+
import javax.xml.stream.XMLStreamConstants;
23+
import javax.xml.stream.XMLStreamException;
24+
import javax.xml.stream.events.Characters;
2225
import javax.xml.stream.events.XMLEvent;
2326

2427
import org.springframework.lang.Nullable;
@@ -29,12 +32,16 @@
2932
* of {@link XMLEvent} elements.
3033
*
3134
* @author Arjen Poutsma
35+
* @author Juergen Hoeller
3236
* @since 5.0
3337
*/
3438
class ListBasedXMLEventReader extends AbstractXMLEventReader {
3539

3640
private final List<XMLEvent> events;
3741

42+
@Nullable
43+
private XMLEvent currentEvent;
44+
3845
private int cursor = 0;
3946

4047

@@ -46,13 +53,15 @@ public ListBasedXMLEventReader(List<XMLEvent> events) {
4653

4754
@Override
4855
public boolean hasNext() {
49-
return (this.cursor != this.events.size());
56+
return (this.cursor < this.events.size());
5057
}
5158

5259
@Override
5360
public XMLEvent nextEvent() {
54-
if (this.cursor < this.events.size()) {
55-
return this.events.get(this.cursor++);
61+
if (hasNext()) {
62+
this.currentEvent = this.events.get(this.cursor);
63+
this.cursor++;
64+
return this.currentEvent;
5665
}
5766
else {
5867
throw new NoSuchElementException();
@@ -62,14 +71,68 @@ public XMLEvent nextEvent() {
6271
@Override
6372
@Nullable
6473
public XMLEvent peek() {
65-
if (this.cursor < this.events.size()) {
74+
if (hasNext()) {
6675
return this.events.get(this.cursor);
6776
}
6877
else {
6978
return null;
7079
}
7180
}
7281

82+
@Override
83+
public String getElementText() throws XMLStreamException {
84+
checkIfClosed();
85+
if (this.currentEvent == null || !this.currentEvent.isStartElement()) {
86+
throw new XMLStreamException("Not at START_ELEMENT: " + this.currentEvent);
87+
}
88+
89+
StringBuilder builder = new StringBuilder();
90+
while (true) {
91+
XMLEvent event = nextEvent();
92+
if (event.isEndElement()) {
93+
break;
94+
}
95+
else if (!event.isCharacters()) {
96+
throw new XMLStreamException("Unexpected non-text event: " + event);
97+
}
98+
Characters characters = event.asCharacters();
99+
if (!characters.isIgnorableWhiteSpace()) {
100+
builder.append(event.asCharacters().getData());
101+
}
102+
}
103+
return builder.toString();
104+
}
105+
106+
@Override
107+
@Nullable
108+
public XMLEvent nextTag() throws XMLStreamException {
109+
checkIfClosed();
110+
111+
while (true) {
112+
XMLEvent event = nextEvent();
113+
switch (event.getEventType()) {
114+
case XMLStreamConstants.START_ELEMENT:
115+
case XMLStreamConstants.END_ELEMENT:
116+
return event;
117+
case XMLStreamConstants.END_DOCUMENT:
118+
return null;
119+
case XMLStreamConstants.SPACE:
120+
case XMLStreamConstants.COMMENT:
121+
case XMLStreamConstants.PROCESSING_INSTRUCTION:
122+
continue;
123+
case XMLStreamConstants.CDATA:
124+
case XMLStreamConstants.CHARACTERS:
125+
if (!event.asCharacters().isWhiteSpace()) {
126+
throw new XMLStreamException(
127+
"Non-ignorable whitespace CDATA or CHARACTERS event: " + event);
128+
}
129+
break;
130+
default:
131+
throw new XMLStreamException("Expected START_ELEMENT or END_ELEMENT: " + event);
132+
}
133+
}
134+
}
135+
73136
@Override
74137
public void close() {
75138
super.close();

spring-core/src/test/java/org/springframework/util/xml/ListBasedXMLEventReaderTests.java

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616

1717
package org.springframework.util.xml;
1818

19-
import static org.junit.Assert.assertThat;
20-
import static org.xmlunit.matchers.CompareMatcher.isSimilarTo;
21-
2219
import java.io.StringReader;
2320
import java.io.StringWriter;
2421
import java.util.ArrayList;
@@ -32,15 +29,21 @@
3229

3330
import org.junit.Test;
3431

32+
import static javax.xml.stream.XMLStreamConstants.*;
33+
import static org.junit.Assert.*;
34+
import static org.xmlunit.matchers.CompareMatcher.*;
35+
3536
/**
3637
* @author Arjen Poutsma
38+
* @author Andrzej Hołowko
3739
*/
3840
public class ListBasedXMLEventReaderTests {
3941

4042
private final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
4143

4244
private final XMLOutputFactory outputFactory = XMLOutputFactory.newFactory();
4345

46+
4447
@Test
4548
public void standard() throws Exception {
4649
String xml = "<foo><bar>baz</bar></foo>";
@@ -55,9 +58,42 @@ public void standard() throws Exception {
5558
assertThat(resultWriter.toString(), isSimilarTo(xml));
5659
}
5760

61+
@Test
62+
public void testGetElementText() throws Exception {
63+
String xml = "<foo><bar>baz</bar></foo>";
64+
List<XMLEvent> events = readEvents(xml);
65+
66+
ListBasedXMLEventReader reader = new ListBasedXMLEventReader(events);
67+
68+
assertEquals(START_DOCUMENT, reader.nextEvent().getEventType());
69+
assertEquals(START_ELEMENT, reader.nextEvent().getEventType());
70+
assertEquals(START_ELEMENT, reader.nextEvent().getEventType());
71+
assertEquals("baz", reader.getElementText());
72+
assertEquals(END_ELEMENT, reader.nextEvent().getEventType());
73+
assertEquals(END_DOCUMENT, reader.nextEvent().getEventType());
74+
}
75+
76+
@Test
77+
public void testGetElementTextThrowsExceptionAtWrongPosition() throws Exception {
78+
String xml = "<foo><bar>baz</bar></foo>";
79+
List<XMLEvent> events = readEvents(xml);
80+
81+
ListBasedXMLEventReader reader = new ListBasedXMLEventReader(events);
82+
83+
assertEquals(START_DOCUMENT, reader.nextEvent().getEventType());
84+
85+
try {
86+
reader.getElementText();
87+
fail("Should have thrown XMLStreamException");
88+
}
89+
catch (XMLStreamException ex) {
90+
// expected
91+
assertTrue(ex.getMessage().startsWith("Not at START_ELEMENT"));
92+
}
93+
}
94+
5895
private List<XMLEvent> readEvents(String xml) throws XMLStreamException {
59-
XMLEventReader reader =
60-
this.inputFactory.createXMLEventReader(new StringReader(xml));
96+
XMLEventReader reader = this.inputFactory.createXMLEventReader(new StringReader(xml));
6197
List<XMLEvent> events = new ArrayList<>();
6298
while (reader.hasNext()) {
6399
events.add(reader.nextEvent());

0 commit comments

Comments
 (0)