From df3cf69a963081045559468fd457497935af8a62 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 21 Apr 2016 12:23:44 +0200 Subject: [PATCH] Polishing --- .../core/codec/support/Jaxb2Decoder.java | 5 +- .../io/buffer/support/DataBufferUtils.java | 27 +------ .../util/CollectionUtils2.java | 60 +++++++++++++++ .../xml/AbstractXMLEventReader.java} | 74 ++++++++----------- .../util/xml/ListBasedXMLEventReader.java | 72 ++++++++++++++++++ .../springframework/util/xml/StaxUtils2.java | 38 ++++++++++ .../xml/ListBasedXMLEventReaderTests.java | 67 +++++++++++++++++ 7 files changed, 273 insertions(+), 70 deletions(-) create mode 100644 spring-web-reactive/src/main/java/org/springframework/util/CollectionUtils2.java rename spring-web-reactive/src/main/java/org/springframework/{core/codec/support/ListBasedXMLEventReader.java => util/xml/AbstractXMLEventReader.java} (75%) create mode 100644 spring-web-reactive/src/main/java/org/springframework/util/xml/ListBasedXMLEventReader.java create mode 100644 spring-web-reactive/src/main/java/org/springframework/util/xml/StaxUtils2.java create mode 100644 spring-web-reactive/src/test/java/org/springframework/util/xml/ListBasedXMLEventReaderTests.java diff --git a/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Decoder.java b/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Decoder.java index 42287d10e7..bcd86ff262 100644 --- a/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Decoder.java +++ b/spring-web-reactive/src/main/java/org/springframework/core/codec/support/Jaxb2Decoder.java @@ -40,6 +40,7 @@ import org.springframework.core.io.buffer.DataBuffer; import org.springframework.util.ClassUtils; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; +import org.springframework.util.xml.StaxUtils2; /** * Decode from a bytes stream of XML elements to a stream of {@code Object} (POJO). @@ -193,10 +194,10 @@ public class Jaxb2Decoder extends AbstractDecoder { }); } - private Object unmarshal(List eventFlux, Class outputClass) { + private Object unmarshal(List events, Class outputClass) { try { Unmarshaller unmarshaller = this.jaxbContexts.createUnmarshaller(outputClass); - XMLEventReader eventReader = new ListBasedXMLEventReader(eventFlux); + XMLEventReader eventReader = StaxUtils2.createXMLEventReader(events); if (outputClass.isAnnotationPresent(XmlRootElement.class)) { return unmarshaller.unmarshal(eventReader); } diff --git a/spring-web-reactive/src/main/java/org/springframework/core/io/buffer/support/DataBufferUtils.java b/spring-web-reactive/src/main/java/org/springframework/core/io/buffer/support/DataBufferUtils.java index 16fd7e5b8e..9e495aa3aa 100644 --- a/spring-web-reactive/src/main/java/org/springframework/core/io/buffer/support/DataBufferUtils.java +++ b/spring-web-reactive/src/main/java/org/springframework/core/io/buffer/support/DataBufferUtils.java @@ -23,7 +23,6 @@ import java.io.SequenceInputStream; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.util.Enumeration; -import java.util.Iterator; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; @@ -36,6 +35,7 @@ import reactor.core.subscriber.SubscriberWithContext; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferAllocator; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils2; /**i * Utility class for working with {@link DataBuffer}s. @@ -67,7 +67,7 @@ public abstract class DataBufferUtils { toIterable(1); Enumeration enumeration = - new IteratorEnumeration(streams.iterator()); + CollectionUtils2.toEnumeration(streams.iterator()); return new SequenceInputStream(enumeration); } @@ -169,29 +169,6 @@ public abstract class DataBufferUtils { }); } - /** - * Enumeration wrapping an Iterator. - */ - // TODO: move to CollectionUtils when we merge with Spring Framework? - private static class IteratorEnumeration implements Enumeration { - - private final Iterator iterator; - - public IteratorEnumeration(Iterator iterator) { - this.iterator = iterator; - } - - @Override - public boolean hasMoreElements() { - return this.iterator.hasNext(); - } - - @Override - public T nextElement() { - return this.iterator.next(); - } - } - private static class ReadableByteChannelConsumer implements Consumer> { diff --git a/spring-web-reactive/src/main/java/org/springframework/util/CollectionUtils2.java b/spring-web-reactive/src/main/java/org/springframework/util/CollectionUtils2.java new file mode 100644 index 0000000000..45f2c0983a --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/util/CollectionUtils2.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.util; + +import java.util.Enumeration; +import java.util.Iterator; + +/** + * TODO: to be merged with {@link CollectionUtils} + * @author Arjen Poutsma + */ +public abstract class CollectionUtils2 { + + /** + * Adapt an iterator to an enumeration. + * @param iterator the iterator + * @return the enumeration + */ + public static Enumeration toEnumeration(Iterator iterator) { + return new IteratorEnumeration(iterator); + } + + /** + * Enumeration wrapping an Iterator. + */ + private static class IteratorEnumeration implements Enumeration { + + private final Iterator iterator; + + public IteratorEnumeration(Iterator iterator) { + this.iterator = iterator; + } + + @Override + public boolean hasMoreElements() { + return this.iterator.hasNext(); + } + + @Override + public T nextElement() { + return this.iterator.next(); + } + } + + +} diff --git a/spring-web-reactive/src/main/java/org/springframework/core/codec/support/ListBasedXMLEventReader.java b/spring-web-reactive/src/main/java/org/springframework/util/xml/AbstractXMLEventReader.java similarity index 75% rename from spring-web-reactive/src/main/java/org/springframework/core/codec/support/ListBasedXMLEventReader.java rename to spring-web-reactive/src/main/java/org/springframework/util/xml/AbstractXMLEventReader.java index 95a22c9080..5342d932ff 100644 --- a/spring-web-reactive/src/main/java/org/springframework/core/codec/support/ListBasedXMLEventReader.java +++ b/spring-web-reactive/src/main/java/org/springframework/util/xml/AbstractXMLEventReader.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package org.springframework.core.codec.support; +package org.springframework.util.xml; -import java.util.List; import java.util.NoSuchElementException; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLStreamConstants; @@ -24,60 +23,26 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.Characters; import javax.xml.stream.events.XMLEvent; -import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** - * TODO: move to org.springframework.util.xml when merging, hidden behind StaxUtils - * + * Abstract base class for {@code XMLEventReader}s. * @author Arjen Poutsma */ -class ListBasedXMLEventReader implements XMLEventReader { +abstract class AbstractXMLEventReader implements XMLEventReader { - private final XMLEvent[] events; - - private int cursor = 0; - - public ListBasedXMLEventReader(List events) { - Assert.notNull(events, "'events' must not be null"); - this.events = events.toArray(new XMLEvent[events.size()]); - } + private boolean closed; @Override - public boolean hasNext() { - Assert.notNull(events, "'events' must not be null"); - return cursor != events.length; - } - - @Override - public XMLEvent nextEvent() { - if (cursor < events.length) { - return events[cursor++]; + public Object next() { + try { + return nextEvent(); } - else { + catch (XMLStreamException ex) { throw new NoSuchElementException(); } } - @Override - public XMLEvent peek() { - if (cursor < events.length) { - return events[cursor]; - } - else { - return null; - } - } - - @Override - public Object next() { - return nextEvent(); - } - - /** - * Throws an {@code UnsupportedOperationException} when called. - * @throws UnsupportedOperationException when called - */ @Override public void remove() { throw new UnsupportedOperationException( @@ -86,6 +51,7 @@ class ListBasedXMLEventReader implements XMLEventReader { @Override public String getElementText() throws XMLStreamException { + checkIfClosed(); if (!peek().isStartElement()) { throw new XMLStreamException("Not at START_ELEMENT"); } @@ -110,6 +76,7 @@ class ListBasedXMLEventReader implements XMLEventReader { @Override public XMLEvent nextTag() throws XMLStreamException { + checkIfClosed(); while (true) { XMLEvent event = nextEvent(); switch (event.getEventType()) { @@ -145,7 +112,28 @@ class ListBasedXMLEventReader implements XMLEventReader { throw new IllegalArgumentException("Property not supported: [" + name + "]"); } + /** + * Returns {@code true} if closed; {@code false} otherwise. + * @see #close() + */ + protected boolean isClosed() { + return closed; + } + + /** + * Checks if the reader is closed, and throws a {@code XMLStreamException} if so. + * @throws XMLStreamException if the reader is closed + * @see #close() + * @see #isClosed() + */ + protected void checkIfClosed() throws XMLStreamException { + if (isClosed()) { + throw new XMLStreamException("XMLEventReader has been closed"); + } + } + @Override public void close() { + closed = true; } } diff --git a/spring-web-reactive/src/main/java/org/springframework/util/xml/ListBasedXMLEventReader.java b/spring-web-reactive/src/main/java/org/springframework/util/xml/ListBasedXMLEventReader.java new file mode 100644 index 0000000000..a8d2345cfa --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/util/xml/ListBasedXMLEventReader.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.util.xml; + +import java.util.Collections; +import java.util.List; +import java.util.NoSuchElementException; +import javax.xml.stream.events.XMLEvent; + +import org.springframework.util.Assert; + +/** + * Implementation of {@code XMLEventReader} based on a list of {@link XMLEvent}s. + * + * @author Arjen Poutsma + */ +class ListBasedXMLEventReader extends AbstractXMLEventReader { + + private final List events; + + private int cursor = 0; + + public ListBasedXMLEventReader(List events) { + Assert.notNull(events, "'events' must not be null"); + this.events = Collections.unmodifiableList(events); + } + + @Override + public boolean hasNext() { + return cursor != events.size(); + } + + @Override + public XMLEvent nextEvent() { + if (cursor < events.size()) { + return events.get(cursor++); + } + else { + throw new NoSuchElementException(); + } + } + + @Override + public XMLEvent peek() { + if (cursor < events.size()) { + return events.get(cursor); + } + else { + return null; + } + } + + @Override + public void close() { + super.close(); + this.events.clear(); + } +} diff --git a/spring-web-reactive/src/main/java/org/springframework/util/xml/StaxUtils2.java b/spring-web-reactive/src/main/java/org/springframework/util/xml/StaxUtils2.java new file mode 100644 index 0000000000..7d4c2c3f8f --- /dev/null +++ b/spring-web-reactive/src/main/java/org/springframework/util/xml/StaxUtils2.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.util.xml; + +import java.util.List; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.XMLEvent; + +/** + * TODO: to be merged with {@link StaxUtils}. + * @author Arjen Poutsma + */ +public abstract class StaxUtils2 { + + /** + * Create a {@link XMLEventReader} from the given list of {@link XMLEvent}. + * @param events the list of {@link XMLEvent}s. + * @return an {@code XMLEventReader} that reads from the given events + */ + public static XMLEventReader createXMLEventReader(List events) { + return new ListBasedXMLEventReader(events); + } + +} diff --git a/spring-web-reactive/src/test/java/org/springframework/util/xml/ListBasedXMLEventReaderTests.java b/spring-web-reactive/src/test/java/org/springframework/util/xml/ListBasedXMLEventReaderTests.java new file mode 100644 index 0000000000..30005c7f47 --- /dev/null +++ b/spring-web-reactive/src/test/java/org/springframework/util/xml/ListBasedXMLEventReaderTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.util.xml; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.XMLEvent; + +import org.junit.Test; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; + +/** + * @author Arjen Poutsma + */ +public class ListBasedXMLEventReaderTests { + + private final XMLInputFactory inputFactory = XMLInputFactory.newFactory(); + + private final XMLOutputFactory outputFactory = XMLOutputFactory.newFactory(); + + @Test + public void standard() throws Exception { + String xml = "baz"; + List events = readEvents(xml); + + ListBasedXMLEventReader reader = new ListBasedXMLEventReader(events); + + StringWriter resultWriter = new StringWriter(); + XMLEventWriter writer = this.outputFactory.createXMLEventWriter(resultWriter); + writer.add(reader); + + assertXMLEqual(xml, resultWriter.toString()); + } + + private List readEvents(String xml) throws XMLStreamException { + XMLEventReader reader = + this.inputFactory.createXMLEventReader(new StringReader(xml)); + List events = new ArrayList<>(); + while (reader.hasNext()) { + events.add(reader.nextEvent()); + } + return events; + } + +} \ No newline at end of file