BATCH-2049: Support multiple fragmentRootElementNames in

StaxEventItemReader
This commit is contained in:
jpraet
2013-08-14 22:37:03 +02:00
committed by Michael Minella
parent 7f1130c9a2
commit a21df75ce9
2 changed files with 177 additions and 27 deletions

View File

@@ -17,6 +17,8 @@
package org.springframework.batch.item.xml;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import javax.xml.namespace.QName;
@@ -39,6 +41,7 @@ import org.springframework.core.io.Resource;
import org.springframework.oxm.Unmarshaller;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Item reader for reading XML input based on StAX.
@@ -66,14 +69,12 @@ ResourceAwareItemReaderItemStream<T>, InitializingBean {
private InputStream inputStream;
private String fragmentRootElementName;
private List<QName> fragmentRootElementNames;
private boolean noInput;
private boolean strict = true;
private String fragmentRootElementNameSpace;
public StaxEventItemReader() {
setName(ClassUtils.getShortName(StaxEventItemReader.class));
}
@@ -103,7 +104,17 @@ ResourceAwareItemReaderItemStream<T>, InitializingBean {
* @param fragmentRootElementName name of the root element of the fragment
*/
public void setFragmentRootElementName(String fragmentRootElementName) {
this.fragmentRootElementName = fragmentRootElementName;
setFragmentRootElementNames(new String[] {fragmentRootElementName});
}
/**
* @param fragmentRootElementNames list of the names of the root element of the fragment
*/
public void setFragmentRootElementNames(String[] fragmentRootElementNames) {
this.fragmentRootElementNames = new ArrayList<QName>();
for (String fragmentRootElementName : fragmentRootElementNames) {
this.fragmentRootElementNames.add(parseFragmentRootElementName(fragmentRootElementName));
}
}
/**
@@ -117,11 +128,11 @@ ResourceAwareItemReaderItemStream<T>, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(unmarshaller, "The Unmarshaller must not be null.");
Assert.hasLength(fragmentRootElementName, "The FragmentRootElementName must not be null");
if (fragmentRootElementName.contains("{")) {
fragmentRootElementNameSpace = fragmentRootElementName.replaceAll("\\{(.*)\\}.*", "$1");
fragmentRootElementName = fragmentRootElementName.replaceAll("\\{.*\\}(.*)", "$1");
}
Assert.notNull(fragmentRootElementNames, "The FragmentRootElementNames must not be null");
Assert.notNull(fragmentRootElementNames, "The FragmentRootElementNames must not be empty");
for (QName fragmentRootElementName : fragmentRootElementNames) {
Assert.hasText(fragmentRootElementName.getLocalPart(), "The FragmentRootElementNames must contain empty elements");
}
}
/**
@@ -145,11 +156,8 @@ ResourceAwareItemReaderItemStream<T>, InitializingBean {
return false;
}
QName startElementName = ((StartElement) reader.peek()).getName();
if (startElementName.getLocalPart().equals(fragmentRootElementName)) {
if (fragmentRootElementNameSpace == null
|| startElementName.getNamespaceURI().equals(fragmentRootElementNameSpace)) {
return true;
}
if (isFragmentRootElementName(startElementName)) {
return true;
}
reader.nextEvent();
@@ -249,8 +257,8 @@ ResourceAwareItemReaderItemStream<T>, InitializingBean {
protected void jumpToItem(int itemIndex) throws Exception {
for (int i = 0; i < itemIndex; i++) {
try {
readToStartFragment();
readToEndFragment();
QName fragmentName = readToStartFragment();
readToEndFragment(fragmentName);
} catch (NoSuchElementException e) {
if (itemIndex == (i + 1)) {
// we can presume a NoSuchElementException on the last item means the EOF was reached on the last run
@@ -264,16 +272,16 @@ ResourceAwareItemReaderItemStream<T>, InitializingBean {
}
/*
* Read until the first StartElement tag that matches the provided fragmentRootElementName. Because there may be any
* Read until the first StartElement tag that matches any of the provided fragmentRootElementNames. Because there may be any
* number of tags in between where the reader is now and the fragment start, this is done in a loop until the
* element type and name match.
*/
private void readToStartFragment() throws XMLStreamException {
private QName readToStartFragment() throws XMLStreamException {
while (true) {
XMLEvent nextEvent = eventReader.nextEvent();
if (nextEvent.isStartElement()
&& ((StartElement) nextEvent).getName().getLocalPart().equals(fragmentRootElementName)) {
return;
&& isFragmentRootElementName(((StartElement) nextEvent).getName())) {
return ((StartElement) nextEvent).getName();
}
}
}
@@ -283,13 +291,36 @@ ResourceAwareItemReaderItemStream<T>, InitializingBean {
* number of tags in between where the reader is now and the fragment end tag, this is done in a loop until the
* element type and name match
*/
private void readToEndFragment() throws XMLStreamException {
private void readToEndFragment(QName fragmentRootElementName) throws XMLStreamException {
while (true) {
XMLEvent nextEvent = eventReader.nextEvent();
if (nextEvent.isEndElement()
&& ((EndElement) nextEvent).getName().getLocalPart().equals(fragmentRootElementName)) {
&& fragmentRootElementName.equals(((EndElement) nextEvent).getName())) {
return;
}
}
}
private boolean isFragmentRootElementName(QName name) {
for (QName fragmentRootElementName : fragmentRootElementNames) {
if (fragmentRootElementName.getLocalPart().equals(name.getLocalPart())) {
if (!StringUtils.hasText(fragmentRootElementName.getNamespaceURI())
|| fragmentRootElementName.getNamespaceURI().equals(name.getNamespaceURI())) {
return true;
}
}
}
return false;
}
private QName parseFragmentRootElementName(String fragmentRootElementName) {
String name = fragmentRootElementName;
String nameSpace = null;
if (fragmentRootElementName.contains("{")) {
nameSpace = fragmentRootElementName.replaceAll("\\{(.*)\\}.*", "$1");
name = fragmentRootElementName.replaceAll("\\{.*\\}(.*)", "$1");
}
return new QName(nameSpace, name, "");
}
}