You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2017/01/09 08:15:11 UTC
svn commit: r1777933 - in /sis/branches/JDK8:
core/sis-utility/src/test/java/org/apache/sis/test/
storage/sis-storage/src/main/java/org/apache/sis/internal/storage/
storage/sis-storage/src/main/java/org/apache/sis/storage/
storage/sis-storage/src/test/...
Author: desruisseaux
Date: Mon Jan 9 08:15:11 2017
New Revision: 1777933
URL: http://svn.apache.org/viewvc?rev=1777933&view=rev
Log:
StAX data store can now switch between read and write modes.
Modified:
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelFactory.java
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/IOUtilities.java
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/InputStreamAdapter.java
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/OutputStreamAdapter.java
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataOutputTest.java
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelImageInputStreamTest.java
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/IOUtilitiesTest.java
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/storage/StorageConnectorTest.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Reader.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Store.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/OutputType.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxDataStore.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamReader.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamWriter.java
sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/ReaderTest.java
sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/WriterTest.java
Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -23,6 +23,14 @@ import java.util.Iterator;
import java.util.Random;
import java.util.concurrent.Callable;
import java.io.PrintWriter;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.SeekableByteChannel;
import java.lang.reflect.UndeclaredThrowableException;
import java.text.Format;
import java.text.DateFormat;
@@ -46,7 +54,7 @@ import static org.apache.sis.internal.ut
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.7
+ * @version 0.8
* @module
*/
public final strictfp class TestUtilities extends Static {
@@ -437,4 +445,32 @@ public final strictfp class TestUtilitie
} while (!stop);
return true;
}
+
+ /**
+ * Copies the full content of the given test resource in a temporary file and returns the channel for that file.
+ * The file is opened with {@link StandardOpenOption#DELETE_ON_CLOSE}, together with read and write options.
+ *
+ * @param caller defines the root from which to search for the {@code resource}.
+ * @param resource path (relative to the {@code caller}) of the test file to copy.
+ * @return a channel opened on a copy of the content of the given test resource.
+ * @throws IOException if an error occurred while copying the data.
+ *
+ * @since 0.8
+ */
+ public static SeekableByteChannel createTemporaryFile(final Class<?> caller, final String resource) throws IOException {
+ final SeekableByteChannel channel;
+ try (final ReadableByteChannel in = Channels.newChannel(caller.getResourceAsStream(resource))) {
+ final int s = resource.lastIndexOf('.');
+ final Path file = Files.createTempFile("SIS", (s >= 0) ? resource.substring(s) : null);
+ channel = Files.newByteChannel(file, StandardOpenOption.DELETE_ON_CLOSE,
+ StandardOpenOption.READ, StandardOpenOption.WRITE);
+ final ByteBuffer buffer = ByteBuffer.allocate(4000);
+ while (in.read(buffer) >= 0) {
+ buffer.flip();
+ channel.write(buffer);
+ buffer.clear();
+ }
+ }
+ return channel.position(0);
+ }
}
Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelFactory.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelFactory.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelFactory.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -79,14 +79,15 @@ public abstract class ChannelFactory {
/**
* Returns a byte channel factory from the given input or output,
- * or {@code null} if the input is of unknown type.
+ * or {@code null} if the given input/output is of unknown type.
* More specifically:
*
* <ul>
* <li>If the given storage is {@code null}, then this method returns {@code null}.</li>
* <li>If the given storage is a {@link ReadableByteChannel} or an {@link InputStream},
* then the factory will return that input directly or indirectly as a wrapper.</li>
- * <li>If the given storage is a {@link WritableByteChannel} or an {@link OutputStream},
+ * <li>If the given storage is a {@link WritableByteChannel} or an {@link OutputStream}
+ * and the {@code allowWriteOnly} argument is {@code true},
* then the factory will return that output directly or indirectly as a wrapper.</li>
* <li>If the given storage if a {@link Path}, {@link File}, {@link URL}, {@link URI}
* or {@link CharSequence}, then the factory will open new channels on demand.</li>
@@ -98,24 +99,29 @@ public abstract class ChannelFactory {
* This is because the channel may be opened by {@link URL#openStream()}, in which case the
* options are ignored.
*
- * <p>The following options are illegal and will cause an exception to be thrown if provided:
+ * <p>The following options are illegal for read operations and will cause an exception to be
+ * thrown if provided while {@code allowWriteOnly}�is {@code false}:
* {@code APPEND}, {@code TRUNCATE_EXISTING}, {@code DELETE_ON_CLOSE}. We reject those options
* because this method is primarily designed for readable channels, with optional data edition.
* Since the write option is not guaranteed to be honored, we have to reject the options that
* would alter significatively the channel behavior depending on whether we have been able to
* honor the options or not.</p>
*
- * @param storage the stream or the file to open, or {@code null}.
- * @param encoding if the input is an encoded URL, the character encoding (normally {@code "UTF-8"}).
- * If the URL is not encoded, then {@code null}. This argument is ignored if the given
- * input does not need to be converted from URL to {@code File}.
- * @param options the options to use for creating a new byte channel. Can be null or empty for read-only.
+ * @param storage the stream or the file to open, or {@code null}.
+ * @param encoding if the input is an encoded URL, the character encoding (normally {@code "UTF-8"}).
+ * If the URL is not encoded, then {@code null}. This argument is ignored if the given
+ * input does not need to be converted from URL to {@code File}.
+ * @param allowWriteOnly whether to allow wrapping {@link WritableByteChannel} and {@link OutputStream}.
+ * @param options the options to use for creating a new byte channel. Can be null or empty for read-only.
* @return the channel factory for the given input, or {@code null} if the given input is of unknown type.
* @throws IOException if an error occurred while processing the given input.
*/
- public static ChannelFactory prepare(Object storage, final String encoding, OpenOption... options) throws IOException {
+ public static ChannelFactory prepare(Object storage, final String encoding,
+ final boolean allowWriteOnly, OpenOption... options) throws IOException
+ {
/*
- * Unconditionally verify the options, even if we may not use them.
+ * Unconditionally verify the options (unless 'allowWriteOnly' is true),
+ * even if we may not use them.
*/
final Set<OpenOption> optionSet;
if (options == null || options.length == 0) {
@@ -123,7 +129,7 @@ public abstract class ChannelFactory {
} else {
optionSet = new HashSet<>(Arrays.asList(options));
optionSet.add(StandardOpenOption.READ);
- if (optionSet.removeAll(ILLEGAL_OPTIONS)) {
+ if (!allowWriteOnly && optionSet.removeAll(ILLEGAL_OPTIONS)) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2,
"options", Arrays.toString(options)));
}
@@ -134,12 +140,12 @@ public abstract class ChannelFactory {
* to its getChannel() method, but only if the input stream type is exactly FileInputStream, not a subtype.
* If Apache SIS defines its own FileInputStream subclass someday, we may need to add a special case here.
*/
- if (storage instanceof ReadableByteChannel || storage instanceof WritableByteChannel) {
+ if (storage instanceof ReadableByteChannel || (allowWriteOnly && storage instanceof WritableByteChannel)) {
return new Stream((Channel) storage);
} else if (storage instanceof InputStream) {
return new Stream(Channels.newChannel((InputStream) storage));
-// } else if (storage instanceof OutputStream) {
-// return new Stream(Channels.newChannel((OutputStream) storage));
+ } else if (allowWriteOnly && storage instanceof OutputStream) {
+ return new Stream(Channels.newChannel((OutputStream) storage));
}
/*
* In the following cases, we will try hard to convert to Path objects before to fallback
Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/IOUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/IOUtilities.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/IOUtilities.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/IOUtilities.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -19,19 +19,25 @@ package org.apache.sis.internal.storage;
import java.util.Locale;
import java.io.File;
import java.io.FileInputStream;
-import java.io.IOException;
import java.io.LineNumberReader;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URISyntaxException;
import java.net.MalformedURLException;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.nio.channels.SeekableByteChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.nio.file.FileSystemNotFoundException;
import java.nio.file.OpenOption;
import java.nio.file.StandardOpenOption;
+import java.nio.file.FileSystemNotFoundException;
import java.nio.charset.StandardCharsets;
+import javax.imageio.stream.ImageInputStream;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamReader;
import org.apache.sis.util.CharSequences;
@@ -385,6 +391,102 @@ public final class IOUtilities extends S
}
/**
+ * Converts the given output stream to an input stream. It is caller's responsibility to flush
+ * the stream and reset its position to the beginning of file before to invoke this method.
+ * The data read by the input stream will be the data that have been written in the output stream
+ * before this method is invoked.
+ *
+ * <p>The given output stream should not be used anymore after this method invocation, but should
+ * not be closed neither since the returned input stream may be backed by the same channel.</p>
+ *
+ * @param stream the input or output stream to converts to an {@code InputStream}.
+ * @return the input stream, or {@code null} if the given stream can not be converted.
+ * @throws IOException if an error occurred during input stream creation.
+ *
+ * @since 0.8
+ */
+ public static InputStream toInputStream(AutoCloseable stream) throws IOException {
+ if (stream != null) {
+ if (stream instanceof InputStream) {
+ return (InputStream) stream;
+ }
+ if (stream instanceof OutputStreamAdapter) {
+ stream = ((OutputStreamAdapter) stream).output;
+ }
+ if (stream instanceof ChannelDataOutput) {
+ final ChannelDataOutput c = (ChannelDataOutput) stream;
+ if (c.channel instanceof ReadableByteChannel) {
+ stream = new ChannelImageInputStream(c.filename, (ReadableByteChannel) c.channel, c.buffer, true);
+ }
+ }
+ if (stream instanceof ImageInputStream) {
+ return new InputStreamAdapter((ImageInputStream) stream);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Converts the given input stream to an output stream. It is caller's responsibility to reset
+ * the stream position to the beginning of file before to invoke this method. The data written
+ * by the output stream will overwrite the previous data, but the caller may need to
+ * {@linkplain #truncate truncate} the output stream after he finished to write in it.
+ *
+ * <p>The given input stream should not be used anymore after this method invocation, but should
+ * not be closed neither since the returned output stream may be backed by the same channel.</p>
+ *
+ * @param stream the input or output stream to converts to an {@code OutputStream}.
+ * @return the output stream, or {@code null} if the given stream can not be converted.
+ * @throws IOException if an error occurred during output stream creation.
+ *
+ * @since 0.8
+ */
+ public static OutputStream toOutputStream(AutoCloseable stream) throws IOException {
+ if (stream != null) {
+ if (stream instanceof OutputStream) {
+ return (OutputStream) stream;
+ }
+ if (stream instanceof InputStreamAdapter) {
+ stream = ((InputStreamAdapter) stream).input;
+ }
+ if (stream instanceof ChannelDataInput) {
+ final ChannelDataInput c = (ChannelDataInput) stream;
+ if (c.channel instanceof WritableByteChannel) {
+ stream = new ChannelImageOutputStream(c.filename, (WritableByteChannel) c.channel, c.buffer);
+ }
+ }
+ if (stream instanceof ChannelImageOutputStream) {
+ return new OutputStreamAdapter((ChannelImageOutputStream) stream);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Truncates the given output stream at its current position.
+ * This method works with Apache SIS implementations backed (sometime indirectly) by {@link SeekableByteChannel}.
+ * Callers may need to {@linkplain java.io.Flushable#flush() flush} the stream before to invoke this method.
+ *
+ * @param stream the output stream or writable channel to truncate.
+ * @return whether this method has been able to truncate the given stream.
+ * @throws IOException if an error occurred while truncating the stream.
+ */
+ public static boolean truncate(AutoCloseable stream) throws IOException {
+ if (stream instanceof OutputStreamAdapter) {
+ stream = ((OutputStreamAdapter) stream).output;
+ }
+ if (stream instanceof ChannelDataOutput) {
+ stream = ((ChannelDataOutput) stream).channel;
+ }
+ if (stream instanceof SeekableByteChannel) {
+ final SeekableByteChannel s = (SeekableByteChannel) stream;
+ s.truncate(s.position());
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Returns {@code true} if the given options would open a file mostly for writing.
* This method returns {@code true} if the following conditions are true:
*
Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/InputStreamAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/InputStreamAdapter.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/InputStreamAdapter.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/InputStreamAdapter.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -45,6 +45,7 @@ public final class InputStreamAdapter ex
* @param input the stream to wrap.
*/
public InputStreamAdapter(final ImageInputStream input) {
+ assert !(input instanceof InputStream);
this.input = input;
}
Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/OutputStreamAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/OutputStreamAdapter.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/OutputStreamAdapter.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/OutputStreamAdapter.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -18,13 +18,10 @@ package org.apache.sis.internal.storage;
import java.io.OutputStream;
import java.io.IOException;
-import java.io.DataOutput;
-import java.io.Flushable;
-import java.io.Closeable;
/**
- * Wraps a {@link DataOutput} as a standard {@link OutputStream}.
+ * Wraps a {@link ChannelDataOutput} as a standard {@link OutputStream}.
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.8
@@ -33,20 +30,23 @@ import java.io.Closeable;
*
* @see InputStreamAdapter
*/
-public final class OutputStreamAdapter extends OutputStream {
+final class OutputStreamAdapter extends OutputStream implements Markable {
/**
* The underlying data output stream. In principle, public access to this field breaks encapsulation.
* But since {@code OutputStreamAdapter} does not hold any state and just forwards every method calls
- * to that {@code DataOutput}, using on object or the other does not make a difference.
+ * to that {@code ChannelDataOutput}, using on object or the other does not make a difference.
+ *
+ * @todo to be replaced by a reference to {@link javax.imageio.stream.ImageOutputStream} if the
+ * {@link ChannelImageOutputStream} class implements that interface in a future version.
*/
- public final DataOutput output;
+ final ChannelImageOutputStream output;
/**
* Constructs a new output stream.
*
* @param output the stream to wrap.
*/
- public OutputStreamAdapter(final DataOutput output) {
+ OutputStreamAdapter(final ChannelImageOutputStream output) {
this.output = output;
}
@@ -86,15 +86,42 @@ public final class OutputStreamAdapter e
}
/**
+ * Marks the current position in this output stream.
+ */
+ @Override
+ public void mark() {
+ output.mark();
+ }
+
+ /**
+ * Repositions this stream to the position at the time the {@code mark} method was last called.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ @Override
+ public void reset() throws IOException {
+ output.reset();
+ }
+
+ /**
+ * Returns the current byte position of the stream.
+ *
+ * @return the position of the stream.
+ * @throws IOException if the position can not be obtained.
+ */
+ @Override
+ public long getStreamPosition() throws IOException {
+ return output.getStreamPosition();
+ }
+
+ /**
* Forces any buffered output bytes to be written out.
*
* @throws IOException if an I/O error occurs.
*/
@Override
public void flush() throws IOException {
- if (output instanceof Flushable) {
- ((Flushable) output).flush();
- }
+ output.flush();
}
/**
@@ -104,8 +131,6 @@ public final class OutputStreamAdapter e
*/
@Override
public void close() throws IOException {
- if (output instanceof Closeable) {
- ((Closeable) output).close();
- }
+ output.close();
}
}
Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -464,7 +464,7 @@ public class StorageConnector implements
* URL, URI, File, Path or other types that may be added in future SIS versions.
*/
final ChannelFactory factory = ChannelFactory.prepare(storage,
- getOption(OptionKey.URL_ENCODING), getOption(OptionKey.OPEN_OPTIONS));
+ getOption(OptionKey.URL_ENCODING), false, getOption(OptionKey.OPEN_OPTIONS));
ChannelDataInput asDataInput = null;
if (factory != null) {
Modified: sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataInputTest.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -51,7 +51,7 @@ public final strictfp class ChannelDataI
* We allocate a small buffer for the {@code ChannelDataInput} in order to force frequent
* interactions between the buffer and the channel.
*
- * @throws IOException Should never happen.
+ * @throws IOException should never happen since we read and write in memory only.
*/
@Test
public void testAllReadMethods() throws IOException {
@@ -136,7 +136,7 @@ public final strictfp class ChannelDataI
/**
* Tests the {@link ChannelDataInput#readString(int, String)} method.
*
- * @throws IOException Should never happen.
+ * @throws IOException should never happen since we read and write in memory only.
*/
@Test
public void testReadString() throws IOException {
@@ -154,7 +154,7 @@ public final strictfp class ChannelDataI
* Tests {@link ChannelDataInput#seek(long)} on a channel that do not implement
* {@link java.nio.channels.SeekableByteChannel}.
*
- * @throws IOException Should never happen.
+ * @throws IOException should never happen since we read and write in memory only.
*/
@Test
public void testSeekOnForwardOnlyChannel() throws IOException {
@@ -177,7 +177,7 @@ public final strictfp class ChannelDataI
/**
* Tests {@link ChannelDataInput#prefetch()}.
*
- * @throws IOException Should never happen.
+ * @throws IOException should never happen since we read and write in memory only.
*/
@Test
public void testPrefetch() throws IOException {
Modified: sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataOutputTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataOutputTest.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataOutputTest.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataOutputTest.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -72,10 +72,10 @@ public strictfp class ChannelDataOutputT
/**
* Initializes all non-final fields before to execute a test.
*
- * @param testName The name of the test method to be executed.
- * @param streamLength Length of stream to create.
- * @param bufferLength Length of the {@code ByteBuffer} to use for the tests.
- * @throws IOException Should never happen.
+ * @param testName the name of the test method to be executed.
+ * @param streamLength length of stream to create.
+ * @param bufferLength length of the {@code ByteBuffer} to use for the tests.
+ * @throws IOException should never happen since we read and write in memory only.
*/
void initialize(final String testName, final int streamLength, final int bufferLength) throws IOException {
expectedData = new ByteArrayOutputStream(streamLength);
@@ -104,7 +104,7 @@ public strictfp class ChannelDataOutputT
* We allocate a small buffer for the {@code ChannelDataOutput} in order to force frequent
* interactions between the buffer and the channel.
*
- * @throws IOException Should never happen.
+ * @throws IOException should never happen since we read and write in memory only.
*/
@Test
public void testAllWriteMethods() throws IOException {
@@ -127,7 +127,7 @@ public strictfp class ChannelDataOutputT
/**
* Tests write operations followed by seek operations.
*
- * @throws IOException Should never happen.
+ * @throws IOException should never happen since we read and write in memory only.
*/
@Test
@DependsOnMethod("testAllWriteMethods")
@@ -153,7 +153,7 @@ public strictfp class ChannelDataOutputT
/**
* Tests seeking ahead of buffer capacity.
*
- * @throws IOException Should never happen.
+ * @throws IOException should never happen since we read and write in memory only.
*/
@Test
@DependsOnMethod("testWriteAndSeek")
@@ -177,7 +177,7 @@ public strictfp class ChannelDataOutputT
testedStream.writeLong(v);
}
assertEquals("getStreamPosition()", 24, testedStream.getStreamPosition());
- testedStream.seek(40); // Move 2 long ahead. Space shall be filled by 0.
+ testedStream.seek(40); // Move 2 long ahead. Space shall be filled by 0.
referenceStream.writeLong(0);
referenceStream.writeLong(0);
final long v = random.nextLong();
@@ -190,7 +190,7 @@ public strictfp class ChannelDataOutputT
* Tests the argument checks performed by various methods. For example this method
* tests {@link ChannelDataOutput#seek(long)} with an invalid seek position.
*
- * @throws IOException Should never happen.
+ * @throws IOException should never happen since we read and write in memory only.
*/
@Test
public void testArgumentChecks() throws IOException {
@@ -236,7 +236,7 @@ public strictfp class ChannelDataOutputT
/**
* Writes the same random data in both {@link #testedStream} and {@link #referenceStream}.
*
- * @throws IOException Should never happen.
+ * @throws IOException should never happen since we read and write in memory only.
*/
private void writeInStreams() throws IOException {
transferRandomData(testedStream, testedStreamBackingArray.length - ARRAY_MAX_LENGTH,
Modified: sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelImageInputStreamTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelImageInputStreamTest.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelImageInputStreamTest.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelImageInputStreamTest.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -56,7 +56,7 @@ public final strictfp class ChannelImage
* We will allocate a small buffer for the {@code ChannelImageInputStream} in order to force
* frequent interactions between the buffer and the channel.
*
- * @throws IOException Should never happen.
+ * @throws IOException should never happen since we read and write in memory only.
*/
@Test
public void testWithRandomData() throws IOException {
Modified: sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/IOUtilitiesTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/IOUtilitiesTest.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/IOUtilitiesTest.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/IOUtilitiesTest.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -43,8 +43,8 @@ public final strictfp class IOUtilitiesT
/**
* Tests {@link IOUtilities#filename(Object)}.
*
- * @throws URISyntaxException Should never happen.
- * @throws MalformedURLException Should never happen.
+ * @throws URISyntaxException if a URI can not be parsed.
+ * @throws MalformedURLException if a URL can not be parsed.
*/
@Test
public void testFilename() throws URISyntaxException, MalformedURLException {
@@ -59,8 +59,8 @@ public final strictfp class IOUtilitiesT
/**
* Tests {@link IOUtilities#extension(Object)}.
*
- * @throws URISyntaxException Should never happen.
- * @throws MalformedURLException Should never happen.
+ * @throws URISyntaxException if a URI can not be parsed.
+ * @throws MalformedURLException if a URL can not be parsed.
*/
@Test
@DependsOnMethod("testFilename")
@@ -79,8 +79,8 @@ public final strictfp class IOUtilitiesT
* Tests again {@link IOUtilities#filename(Object)} and {@link IOUtilities#extension(Object)}, but with a URI
* that point to a JAR entry. Such URI are opaque, in which case {@link URI#getPath()} returns {@code null}.
*
- * @throws URISyntaxException Should never happen.
- * @throws MalformedURLException Should never happen.
+ * @throws URISyntaxException if a URI can not be parsed.
+ * @throws MalformedURLException if a URL can not be parsed.
*/
@Test
@DependsOnMethod({"testFilename", "testExtension"})
@@ -98,8 +98,8 @@ public final strictfp class IOUtilitiesT
/**
* Tests {@link IOUtilities#toString(Object)}.
*
- * @throws URISyntaxException Should never happen.
- * @throws MalformedURLException Should never happen.
+ * @throws URISyntaxException if a URI can not be parsed.
+ * @throws MalformedURLException if a URL can not be parsed.
*/
@Test
public void testToString() throws URISyntaxException, MalformedURLException {
@@ -123,8 +123,8 @@ public final strictfp class IOUtilitiesT
/**
* Tests {@link IOUtilities#toURI(URL, String)}.
*
- * @throws IOException Should not happen.
- * @throws URISyntaxException Should not happen.
+ * @throws IOException if a URL can not be parsed.
+ * @throws URISyntaxException if a URI can not be parsed.
*/
@Test
@DependsOnMethod("testEncodeURI")
@@ -148,7 +148,7 @@ public final strictfp class IOUtilitiesT
* (e.g. {@code "file:///C:/some/path/Map.png"}), since the result is different on Windows or
* Unix platforms.
*
- * @throws IOException Should not happen.
+ * @throws IOException if a URL can not be parsed.
*/
@Test
@DependsOnMethod("testToURI")
@@ -159,7 +159,7 @@ public final strictfp class IOUtilitiesT
/**
* Same test than {@link #testToFile()}, but using the UTF-8 encoding.
*
- * @throws IOException Should never happen.
+ * @throws IOException if a URL can not be parsed.
*/
@Test
@DependsOnMethod("testToFile")
@@ -171,9 +171,9 @@ public final strictfp class IOUtilitiesT
* Implementation of {@link #testToFile()} using the given encoding.
* If the encoding is null, then the {@code URLDecoder} will not be used.
*
- * @param encoding The encoding, or {@code null} if none.
- * @param plus The representation for the {@code '+'} sign.
- * @throws IOException Should not happen.
+ * @param encoding the encoding, or {@code null} if none.
+ * @param plus the representation for the {@code '+'} sign.
+ * @throws IOException if a URL can not be parsed.
*/
private void testToFile(final String encoding, final String plus) throws IOException {
assertEquals("Unix absolute path.", new File("/Users/name/Map.png"),
@@ -187,7 +187,7 @@ public final strictfp class IOUtilitiesT
/**
* Tests {@link IOUtilities#toFileOrURL(String, String)}.
*
- * @throws IOException Should not happen.
+ * @throws IOException if a URL can not be parsed.
*/
@Test
@DependsOnMethod("testToFileFromUTF8")
Modified: sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/storage/StorageConnectorTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/storage/StorageConnectorTest.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/storage/StorageConnectorTest.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/storage/StorageConnectorTest.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -89,7 +89,7 @@ public final strictfp class StorageConne
* Tests the {@link StorageConnector#getStorageAs(Class)} method for the {@link String} type.
*
* @throws DataStoreException if an error occurred while using the storage connector.
- * @throws IOException Should never happen.
+ * @throws IOException should never happen since we do not open any file.
*/
@Test
public void testGetAsString() throws DataStoreException, IOException {
@@ -202,11 +202,12 @@ public final strictfp class StorageConne
assertNotSame(connection.getStorage(), in);
assertSame("Expected cached value.", in, connection.getStorageAs(InputStream.class));
assertInstanceOf("Expected Channel backend", InputStreamAdapter.class, in);
- assertInstanceOf("Expected Channel backend", ChannelImageInputStream.class, ((InputStreamAdapter) in).input);
- assertSame(((InputStreamAdapter) in).input, connection.getStorageAs(DataInput.class));
- assertSame(((InputStreamAdapter) in).input, connection.getStorageAs(ImageInputStream.class));
+ final ImageInputStream input = ((InputStreamAdapter) in).input;
+ assertInstanceOf("Expected Channel backend", ChannelImageInputStream.class, input);
+ assertSame(input, connection.getStorageAs(DataInput.class));
+ assertSame(input, connection.getStorageAs(ImageInputStream.class));
- final ReadableByteChannel channel = ((ChannelImageInputStream) ((InputStreamAdapter) in).input).channel;
+ final ReadableByteChannel channel = ((ChannelImageInputStream) input).channel;
assertTrue(channel.isOpen());
connection.closeAllExcept(null);
assertFalse(channel.isOpen());
@@ -286,7 +287,7 @@ public final strictfp class StorageConne
public void testGetAsTemporaryByteBuffer() throws DataStoreException, IOException {
StorageConnector connection = create(true);
final DataInput in = ImageIO.createImageInputStream(connection.getStorage());
- assertNotNull("ImageIO.createImageInputStream(InputStream)", in); // Sanity check.
+ assertNotNull("ImageIO.createImageInputStream(InputStream)", in); // Sanity check.
connection = new StorageConnector(in);
assertSame(in, connection.getStorageAs(DataInput.class));
@@ -301,7 +302,7 @@ public final strictfp class StorageConne
* Tests the {@link StorageConnector#getStorageAs(Class)} method for the {@link Connection} type.
*
* @throws DataStoreException if an error occurred while using the storage connector.
- * @throws IOException should never happen.
+ * @throws IOException should never happen since we do not open any file.
*/
public void testGetAsConnection() throws DataStoreException, IOException {
final StorageConnector connection = create(false);
Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Reader.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Reader.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Reader.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Reader.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -389,6 +389,7 @@ parse: while (reader.hasNext()) {
/**
* Parses a {@code <wpt>}, {@code <rtept>} or {@code <trkpt>} element.
* The STAX reader {@linkplain XMLStreamReader#getEventType() current event} must be a {@link #START_ELEMENT}.
+ * After this method invocation, the reader will be on {@link #END_ELEMENT}.
*
* @throws Exception see the list of exceptions documented in {@link #parse(Consumer, boolean)}.
*/
@@ -410,12 +411,13 @@ parse: while (reader.hasNext()) {
feature.setPropertyValue("@identifier", index);
feature.setPropertyValue("@geometry", new Point(parseDouble(lon), parseDouble(lat)));
List<Link> links = null;
+ int event = reader.next();
while (true) {
/*
* We do not need to check 'reader.hasNext()' in above loop
* since this check is done by the END_DOCUMENT case below.
*/
- switch (reader.next()) {
+ switch (event) {
case START_ELEMENT: {
final Object value;
final String name = reader.getLocalName();
@@ -437,16 +439,19 @@ parse: while (reader.hasNext()) {
case Tags.SATELITTES: // Fallthrough to getElementAsInteger()
case Tags.DGPS_ID: value = getElementAsInteger(); break;
case Tags.FIX: value = Fix.fromGPX(getElementText()); break;
- case Tags.LINK: links = Metadata.addIfNonNull(links, unmarshal(Link.class)); continue;
- case Tags.URL: links = Metadata.addIfNonNull(links, Link.valueOf(getElementAsURI())); continue;
+ case Tags.LINK: links = Metadata.addIfNonNull(links, unmarshal(Link.class)); event = reader.getEventType(); continue;
+ case Tags.URL: links = Metadata.addIfNonNull(links, Link.valueOf(getElementAsURI())); event = reader.next(); continue;
default: {
if (name.equals(tagName)) {
throw new DataStoreContentException(nestedElement(name));
}
- continue;
+ value = null;
+ break;
}
}
- feature.setPropertyValue(name, value);
+ if (value != null) {
+ feature.setPropertyValue(name, value);
+ }
break;
}
case END_ELEMENT: {
@@ -460,6 +465,7 @@ parse: while (reader.hasNext()) {
throw new EOFException(endOfFile());
}
}
+ event = reader.next();
}
}
@@ -475,33 +481,37 @@ parse: while (reader.hasNext()) {
feature.setPropertyValue("@identifier", index);
List<Feature> wayPoints = null;
List<Link> links = null;
+ int event = reader.next();
while (true) {
/*
* We do not need to check 'reader.hasNext()' in above loop
* since this check is done by the END_DOCUMENT case below.
*/
- switch (reader.next()) {
+ switch (event) {
case START_ELEMENT: {
final Object value;
final String name = reader.getLocalName();
switch (isGPX() ? name : "") {
- default: continue;
+ default: value = null; break;
case Tags.NAME: // Fallthrough to getElementText()
case Tags.COMMENT: // \ufe19
case Tags.DESCRIPTION: // \ufe19
case Tags.SOURCE: // \ufe19
case Tags.TYPE: value = getElementText(); break;
case Tags.NUMBER: value = getElementAsInteger(); break;
- case Tags.LINK: links = Metadata.addIfNonNull(links, unmarshal(Link.class)); continue;
- case Tags.URL: links = Metadata.addIfNonNull(links, Link.valueOf(getElementAsURI())); continue;
+ case Tags.LINK: links = Metadata.addIfNonNull(links, unmarshal(Link.class)); event = reader.getEventType(); continue;
+ case Tags.URL: links = Metadata.addIfNonNull(links, Link.valueOf(getElementAsURI())); event = reader.next(); continue;
case Tags.ROUTES: throw new DataStoreContentException(nestedElement(name));
case Tags.ROUTE_POINTS: {
if (wayPoints == null) wayPoints = new ArrayList<>(8);
wayPoints.add(parseWayPoint(wayPoints.size() + 1));
+ event = reader.next();
continue;
}
}
- feature.setPropertyValue(name, value);
+ if (value != null) {
+ feature.setPropertyValue(name, value);
+ }
break;
}
case END_ELEMENT: {
@@ -516,6 +526,7 @@ parse: while (reader.hasNext()) {
throw new EOFException(endOfFile());
}
}
+ event = reader.next();
}
}
@@ -574,33 +585,37 @@ parse: while (reader.hasNext()) {
feature.setPropertyValue("@identifier", index);
List<Feature> segments = null;
List<Link> links = null;
+ int event = reader.next();
while (true) {
/*
* We do not need to check 'reader.hasNext()' in above loop
* since this check is done by the END_DOCUMENT case below.
*/
- switch (reader.next()) {
+ switch (event) {
case START_ELEMENT: {
final Object value;
final String name = reader.getLocalName();
switch (isGPX() ? name : "") {
- default: continue;
+ default: value = null; break;
case Tags.NAME: // Fallthrough to getElementText()
case Tags.COMMENT: // \ufe19
case Tags.DESCRIPTION: // \ufe19
case Tags.SOURCE: // \ufe19
case Tags.TYPE: value = getElementText(); break;
case Tags.NUMBER: value = getElementAsInteger(); break;
- case Tags.LINK: links = Metadata.addIfNonNull(links, unmarshal(Link.class)); continue;
- case Tags.URL: links = Metadata.addIfNonNull(links, Link.valueOf(getElementAsURI())); continue;
+ case Tags.LINK: links = Metadata.addIfNonNull(links, unmarshal(Link.class)); event = reader.getEventType(); continue;
+ case Tags.URL: links = Metadata.addIfNonNull(links, Link.valueOf(getElementAsURI())); event = reader.next(); continue;
case Tags.TRACKS: throw new DataStoreContentException(nestedElement(name));
case Tags.TRACK_SEGMENTS: {
if (segments == null) segments = new ArrayList<>(8);
segments.add(parseTrackSegment(segments.size() + 1));
+ event = reader.next();
continue;
}
}
- feature.setPropertyValue(name, value);
+ if (value != null) {
+ feature.setPropertyValue(name, value);
+ }
break;
}
case END_ELEMENT: {
@@ -615,6 +630,7 @@ parse: while (reader.hasNext()) {
throw new EOFException(endOfFile());
}
}
+ event = reader.next();
}
}
}
Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Store.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Store.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Store.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/Store.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -204,12 +204,27 @@ public final class Store extends StaxDat
public synchronized void write(final Metadata metadata, final Stream<? extends Feature> features)
throws DataStoreException
{
- try (final Writer writer = new Writer(this, org.apache.sis.internal.gpx.Metadata.castOrCopy(metadata, locale))) {
- writer.writeStartDocument();
- if (features != null) {
- features.forEachOrdered(writer);
+ try {
+ /*
+ * If we created a reader for reading metadata, we need to close that reader now otherwise the call
+ * to 'new Writer(\u2026)' will fail. Note that if that reader was in use by someone else, the 'reader'
+ * field would be null and the 'new Writer(\u2026)' call should detect that a reader is in use somewhere.
+ */
+ final Reader r = reader;
+ if (r != null) {
+ reader = null;
+ r.close();
+ }
+ /*
+ * Get the writer if no read or other write operation is in progress, then write the data.
+ */
+ try (final Writer writer = new Writer(this, org.apache.sis.internal.gpx.Metadata.castOrCopy(metadata, locale))) {
+ writer.writeStartDocument();
+ if (features != null) {
+ features.forEachOrdered(writer);
+ }
+ writer.writeEndDocument();
}
- writer.writeEndDocument();
} catch (BackingStoreException e) {
final Throwable cause = e.getCause();
if (cause instanceof DataStoreException) {
Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/OutputType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/OutputType.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/OutputType.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/OutputType.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -22,9 +22,7 @@ import java.io.StringWriter;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.DataOutput;
-import java.io.IOException;
-import java.nio.channels.WritableByteChannel;
+import java.io.Closeable;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
@@ -35,10 +33,6 @@ import javax.xml.transform.sax.SAXResult
import javax.xml.transform.stax.StAXResult;
import org.xml.sax.ContentHandler;
import org.w3c.dom.Node;
-import org.apache.sis.internal.storage.ChannelDataInput;
-import org.apache.sis.internal.storage.ChannelImageOutputStream;
-import org.apache.sis.internal.storage.InputStreamAdapter;
-import org.apache.sis.internal.storage.OutputStreamAdapter;
/**
@@ -73,11 +67,11 @@ enum OutputType {
return (ds.encoding != null) ? f.createXMLStreamWriter((OutputStream) s, ds.encoding.name())
: f.createXMLStreamWriter((OutputStream) s);
}
- @Override Object toReader(final Object s) {
+ @Override Closeable snapshot(final Object s) {
if (s instanceof ByteArrayOutputStream) {
return new ByteArrayInputStream(((ByteArrayOutputStream) s).toByteArray());
}
- return super.toReader(s);
+ return super.snapshot(s);
}
},
@@ -88,11 +82,11 @@ enum OutputType {
@Override XMLStreamWriter create(StaxDataStore ds, Object s) throws XMLStreamException {
return ds.outputFactory().createXMLStreamWriter((Writer) s);
}
- @Override Object toReader(final Object s) {
+ @Override Closeable snapshot(final Object s) {
if (s instanceof StringWriter) {
return new StringReader(s.toString());
}
- return super.toReader(s);
+ return super.snapshot(s);
}
},
@@ -112,9 +106,6 @@ enum OutputType {
@Override XMLStreamWriter create(StaxDataStore ds, Object s) throws XMLStreamException {
return ds.outputFactory().createXMLStreamWriter(new DOMResult((Node) s));
}
- @Override Object toReader(final Object s) {
- return s;
- }
},
/**
@@ -165,37 +156,21 @@ enum OutputType {
/**
* Returns a reader for the data written by the given writer, or {@code null} if we can not read the data.
- * The returned input can be used by {@link #inputType}.
+ * If non-null, the value returned by this method is a snapshot of the given stream content, i.e. changes
+ * in the output stream will not affect the returned input stream or reader. In particular, contrarily to
+ * {@link org.apache.sis.internal.storage.IOUtilities#toInputStream(AutoCloseable)} this method does not
+ * invalidate the output stream.
+ *
+ * <p>The returned input can be used by {@link #inputType}.</p>
*
* @param s the output from which to get the data that we wrote.
* @return the input for the data written by the given stream, or {@code null} if none.
*/
- Object toReader(final Object s) {
+ Closeable snapshot(final Object s) {
return null;
}
/**
- * Converts the given stream, usually (but not necessarily) an input stream, to an output stream.
- * It is caller's responsibility to reset the stream position to the beginning of file before to
- * invoke this method.
- */
- static OutputStream fromInput(AutoCloseable stream) throws IOException {
- if (stream instanceof OutputStream) {
- return (OutputStream) stream;
- }
- if (stream instanceof InputStreamAdapter) {
- stream = ((InputStreamAdapter) stream).input;
- }
- if (stream instanceof ChannelDataInput) {
- final ChannelDataInput c = (ChannelDataInput) stream;
- if (c.channel instanceof WritableByteChannel) {
- stream = new ChannelImageOutputStream(c.filename, (WritableByteChannel) c.channel, c.buffer);
- }
- }
- return (stream instanceof DataOutput) ? new OutputStreamAdapter((DataOutput) stream) : null;
- }
-
- /**
* Returns a {@code WriterFactory} for the given output type. The {@code type} argument given to this method
* shall be the class of the {@code s} argument to be given in {@link #create(StaxDataStore, Object)} calls.
*
Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxDataStore.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxDataStore.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxDataStore.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxDataStore.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -20,6 +20,7 @@ import java.util.Locale;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.LogRecord;
+import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
@@ -39,6 +40,7 @@ import org.apache.sis.storage.StorageCon
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.internal.storage.ChannelFactory;
import org.apache.sis.internal.storage.FeatureStore;
+import org.apache.sis.internal.storage.IOUtilities;
import org.apache.sis.internal.storage.Markable;
import org.apache.sis.internal.util.AbstractMap;
import org.apache.sis.storage.ConcurrentReadException;
@@ -259,6 +261,7 @@ public abstract class StaxDataStore exte
} while (p > streamPosition);
if (p == streamPosition) {
m.mark();
+ state = START;
return true;
}
}
@@ -400,43 +403,29 @@ public abstract class StaxDataStore exte
*/
@SuppressWarnings("fallthrough")
final synchronized XMLStreamReader createReader(final StaxStreamReader target) throws Exception {
- Object input = storage;
- if (input == null) {
+ Object inputOrFile = storage;
+ if (inputOrFile == null) {
throw new DataStoreClosedException(getLocale(), getFormatName(), StandardOpenOption.READ);
}
- /*
- * If the storage given by the user was not one of InputStream, Reader or other type recognized
- * by InputType, then maybe that storage was a Path, File or URL, in which case the constructor
- * should have opened an InputStream for it. If not, then this was an unsupported storage type.
- * Note that we may have a null 'storageToReader' but a non-null 'storageToWriter', which is why
- * the UnsupportedStorageException has not been thrown at StaxDataStore construction time.
- */
- AutoCloseable opened = stream;
+ AutoCloseable input = stream;
InputType type = storageToReader;
- if (type == null) {
- if (opened == null) {
- throw new UnsupportedStorageException(getLocale(), getFormatName(), input, StandardOpenOption.READ);
- }
- input = opened;
- type = InputType.STREAM;
- }
/*
* If the stream has already been used by a previous read operation, then we need to rewind
* it to the start position determined at construction time. It the stream does not support
- * mark, then we can not re-read the data.
+ * mark, then we can not re-read the data unless we know how to create new input streams.
*/
switch (state) {
- default: throw new AssertionError(state);
- case WRITING: throw new ConcurrentWriteException(getLocale(), getDisplayName());
- case START: break; // Stream already at the data start; nothing to do.
+ default: throw new AssertionError(state);
+ case WRITING: throw new ConcurrentWriteException(getLocale(), getDisplayName());
+ case START: break; // Stream already at the data start; nothing to do.
case FINISHED: {
- if (reset()) break;
- if (opened != null) {
- stream = null; // Cleared first in case of error during 'close()' call.
- opened.close();
- opened = null;
+ if (reset()) break; // If we can reuse existing stream, nothing more to do.
+ if (input != null) {
+ stream = null; // Cleared first in case of error during 'close()' call.
+ input.close();
+ input = null;
}
- // Fall through
+ // Fall through for trying to create a new input stream.
}
case READING: {
/*
@@ -444,22 +433,60 @@ public abstract class StaxDataStore exte
* then we need to create a new input stream (except if the input was a DOM in memory, which we can
* share). The 'target' StaxStreamReader will be in charge of closing that stream.
*/
- if (type == InputType.NODE) break;
- final String name = getDisplayName();
- if (channelFactory == null) {
- throw new ForwardOnlyStorageException(getLocale(), name, StandardOpenOption.READ);
- }
- type = InputType.STREAM;
- input = opened = channelFactory.inputStream(name);
- if (stream == null) {
- stream = opened;
- mark();
+ if (type != InputType.NODE) {
+ final String name = getDisplayName();
+ if (channelFactory == null) {
+ throw new ForwardOnlyStorageException(getLocale(), name, StandardOpenOption.READ);
+ }
+ inputOrFile = input = channelFactory.inputStream(name);
+ type = InputType.STREAM;
+ if (stream == null) {
+ stream = input;
+ state = START;
+ mark();
+ }
}
break;
}
}
- final XMLStreamReader reader = type.create(this, input);
- target.stream = opened;
+ /*
+ * At this point we verified there is no write operation in progress and that the input stream (if not null)
+ * is available for our use. Now we need to build a XMLStreamReader from that input. This is InputType work,
+ * but that type may be null if the storage given by the user was not an InputStream, Reader or other types
+ * recognized by InputType. In such case there is two possibilities:
+ *
+ * - It may be an OutputStream, Writer or other types recognized by OutputType.
+ * - It may be a Path, File, URL or URI, which are intentionally not handled by Input/OutputType.
+ */
+ if (type == null) {
+ if (storageToWriter != null) {
+ final Closeable snapshot = storageToWriter.snapshot(inputOrFile);
+ if (snapshot != null) {
+ // Do not set state to READING since the input in this block is a copy of data.
+ final XMLStreamReader reader = storageToWriter.inputType.create(this, snapshot);
+ target.stream = snapshot;
+ return reader;
+ }
+ }
+ /*
+ * Maybe that storage was a Path, File or URL, in which case the constructor should have opened an
+ * InputStream for it. If not, then this was an unsupported storage type. However the input stream
+ * may have been converted to an output stream during a write operation, in which case we need to
+ * convert it back to an input stream.
+ */
+ type = InputType.STREAM;
+ input = IOUtilities.toInputStream(input);
+ if (input == null) {
+ throw new UnsupportedStorageException(getLocale(), getFormatName(), storage, StandardOpenOption.READ);
+ }
+ inputOrFile = input;
+ if (input != stream) {
+ stream = input;
+ mark();
+ }
+ }
+ final XMLStreamReader reader = type.create(this, inputOrFile);
+ target.stream = input;
state = READING;
return reader;
}
@@ -479,16 +506,16 @@ public abstract class StaxDataStore exte
final synchronized XMLStreamWriter createWriter(final StaxStreamWriter target)
throws DataStoreException, XMLStreamException, IOException
{
- Object output = storage;
- if (output == null) {
+ Object outputOrFile = storage;
+ if (outputOrFile == null) {
throw new DataStoreClosedException(getLocale(), getFormatName(), StandardOpenOption.WRITE);
}
switch (state) {
- default: throw new AssertionError(state);
- case READING: throw new ConcurrentReadException (getLocale(), getDisplayName());
- case WRITING: throw new ConcurrentWriteException(getLocale(), getDisplayName());
- case START: break; // Stream already at the data start; nothing to do.
- case FINISHED: {
+ default: throw new AssertionError(state);
+ case READING: throw new ConcurrentReadException (getLocale(), getDisplayName());
+ case WRITING: throw new ConcurrentWriteException(getLocale(), getDisplayName());
+ case START: break; // Stream already at the data start; nothing to do.
+ case FINISHED: {
if (reset()) break;
throw new ForwardOnlyStorageException(getLocale(), getDisplayName(), StandardOpenOption.WRITE);
}
@@ -496,22 +523,25 @@ public abstract class StaxDataStore exte
/*
* If the storage given by the user was not one of OutputStream, Writer or other type recognized
* by OutputType, then maybe that storage was a Path, File or URL, in which case the constructor
- * should have opened an InputStream (not an OutputStream) for it.
+ * should have opened an InputStream (not an OutputStream) for it. In some cases (e.g. reading a
+ * channel opened on a file), the input stream can be converted to an output stream.
*/
- AutoCloseable opened = stream;
+ AutoCloseable output = stream;
OutputType type = storageToWriter;
if (type == null) {
- opened = OutputType.fromInput(opened);
- if (opened == null) {
- throw new UnsupportedStorageException(getLocale(), getFormatName(), output, StandardOpenOption.WRITE);
+ type = OutputType.STREAM;
+ output = IOUtilities.toOutputStream(output);
+ if (output == null) {
+ throw new UnsupportedStorageException(getLocale(), getFormatName(), storage, StandardOpenOption.WRITE);
+ }
+ outputOrFile = output;
+ if (output != stream) {
+ stream = output;
+ mark();
}
- type = OutputType.STREAM;
- output = opened;
- stream = opened;
- mark();
}
- final XMLStreamWriter writer = type.create(this, output);
- target.stream = opened;
+ final XMLStreamWriter writer = type.create(this, outputOrFile);
+ target.stream = output;
state = WRITING;
return writer;
}
Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamReader.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamReader.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamReader.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamReader.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -257,7 +257,7 @@ public abstract class StaxStreamReader e
/**
* Skips all remaining elements until we reach the end of the given tag.
* Nested tags of the same name, if any, are also skipped.
- * After this method invocation, the current event is {@link #END_DOCUMENT}.
+ * After this method invocation, the current event is {@link #END_ELEMENT}.
*
* @param tagName name of the tag to close.
* @throws EOFException if end tag could not be found.
@@ -447,6 +447,9 @@ parse: switch (value.length()) {
/**
* Delegates to JAXB the unmarshalling of a part of XML document, starting from the current element (inclusive).
+ * This method assumes that the reader is on {@link #START_ELEMENT}. After this method invocation, the reader
+ * will be on the event <strong>after</strong> {@link #END_ELEMENT}; this implies that the caller will need to
+ * invoke {@link XMLStreamReader#getEventType()} instead of {@link XMLStreamReader#next()}.
*
* @param <T> compile-time value of the {@code type} argument.
* @param type expected type of the object to unmarshal.
Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamWriter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamWriter.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamWriter.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/xml/StaxStreamWriter.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -27,6 +27,7 @@ import javax.xml.bind.JAXBException;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.internal.storage.IOUtilities;
import org.apache.sis.util.collection.BackingStoreException;
import org.apache.sis.util.resources.Errors;
@@ -323,7 +324,8 @@ public abstract class StaxStreamWriter e
marshaller = null;
getMarshallerPool().recycle(m);
}
- writer.close();
+ writer.close(); // Implies a call to stream.flush().
+ IOUtilities.truncate(stream);
super.close();
}
}
Modified: sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/ReaderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/ReaderTest.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/ReaderTest.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/ReaderTest.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -166,7 +166,7 @@ public final strictfp class ReaderTest e
* This method verifies only the values that are common to both GPX 1.0 and GPX 1.1 test files.
*/
@SuppressWarnings("fallthrough")
- private static void verifyMetadata(final Metadata md, final int numLinks) {
+ static void verifyMetadata(final Metadata md, final int numLinks) {
assertEquals ("name", "Sample", md.name);
assertEquals ("description", "GPX test file", md.description);
assertEquals ("time", date("2010-03-01 00:00:00"), md.time);
@@ -298,7 +298,7 @@ public final strictfp class ReaderTest e
* Verifies the routes of GPX {@code "1.1/route.xml"} test file.
* This verification is shared by {@link #testRoute110()} and {@link #testSequentialReads()}.
*/
- private static void verifyRoute110(final Store reader) throws DataStoreException {
+ static void verifyRoute110(final Store reader) throws DataStoreException {
try (final Stream<Feature> features = reader.getFeatures()) {
final Iterator<Feature> it = features.iterator();
verifyRoute(it.next(), true, 3);
@@ -552,6 +552,20 @@ public final strictfp class ReaderTest e
}
/**
+ * Tests parsing of GPX version 1.1.0 route without reading the metadata before it.
+ * The reader is expected to skip metadata without unmarshalling them.
+ *
+ * @throws DataStoreException if reader failed to be created or failed at reading.
+ */
+ @Test
+ @DependsOnMethod("testRoute110")
+ public void testRouteSkipMetadata() throws DataStoreException {
+ try (final Store reader = create("1.1/route.xml")) {
+ verifyRoute110(reader);
+ }
+ }
+
+ /**
* Creates a data store for the {@code "1.1/route.xml"} test files using its URL instead than the input stream.
* Using the URL makes easier for the data store to read the same data more than once.
*/
@@ -567,7 +581,7 @@ public final strictfp class ReaderTest e
* @throws DataStoreException if reader failed to be created or failed at reading.
*/
@Test
- @DependsOnMethod("testRoute110")
+ @DependsOnMethod("testRouteSkipMetadata")
public void testSequentialReads() throws DataStoreException {
final Metadata md;
try (final Store reader = createFromURL()) {
Modified: sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/WriterTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/WriterTest.java?rev=1777933&r1=1777932&r2=1777933&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/WriterTest.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/WriterTest.java [UTF-8] Mon Jan 9 08:15:11 2017
@@ -29,6 +29,7 @@ import org.apache.sis.util.Version;
import org.apache.sis.util.Debug;
import org.apache.sis.test.DependsOn;
import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.TestUtilities;
import org.apache.sis.test.TestCase;
import org.junit.BeforeClass;
import org.junit.AfterClass;
@@ -105,7 +106,7 @@ public final strictfp class WriterTest e
/**
* Test writing GPX 1.0 metadata. This test creates programmatically the same metadata than the ones
- * found in {@code 1.0/metadata.xml} file, then compare the written XML file with the expected file.
+ * found in {@code 1.0/metadata.xml} file, then compares the written XML file with the expected file.
*
* @throws Exception if an error occurred while writing the XML data.
*/
@@ -116,7 +117,7 @@ public final strictfp class WriterTest e
/**
* Test writing GPX 1.1 metadata. This test creates programmatically the same metadata than the ones
- * found in {@code 1.1/metadata.xml} file, then compare the written XML file with the expected file.
+ * found in {@code 1.1/metadata.xml} file, then compares the written XML file with the expected file.
*
* @throws Exception if an error occurred while writing the XML data.
*/
@@ -144,7 +145,7 @@ public final strictfp class WriterTest e
/**
* Tests writing various GPX 1.0 way points. This test creates programmatically the same features than
- * the ones found in {@code 1.0/waypoint.xml}, then compare the written XML file with the expected file.
+ * the ones found in {@code 1.0/waypoint.xml}, then compares the written XML file with the expected file.
*
* @throws Exception if an error occurred while writing the XML data.
*/
@@ -156,7 +157,7 @@ public final strictfp class WriterTest e
/**
* Tests writing various GPX 1.1 way points. This test creates programmatically the same features than
- * the ones found in {@code 1.1/waypoint.xml}, then compare the written XML file with the expected file.
+ * the ones found in {@code 1.1/waypoint.xml}, then compares the written XML file with the expected file.
*
* @throws Exception if an error occurred while writing the XML data.
*/
@@ -168,7 +169,7 @@ public final strictfp class WriterTest e
/**
* Tests writing various GPX 1.0 routes. This test creates programmatically the same features than
- * the ones found in {@code 1.0/route.xml}, then compare the written XML file with the expected file.
+ * the ones found in {@code 1.0/route.xml}, then compares the written XML file with the expected file.
*
* @throws Exception if an error occurred while writing the XML data.
*/
@@ -180,7 +181,7 @@ public final strictfp class WriterTest e
/**
* Tests writing various GPX 1.1 routes. This test creates programmatically the same features than
- * the ones found in {@code 1.1/route.xml}, then compare the written XML file with the expected file.
+ * the ones found in {@code 1.1/route.xml}, then compares the written XML file with the expected file.
*
* @throws Exception if an error occurred while writing the XML data.
*/
@@ -192,7 +193,7 @@ public final strictfp class WriterTest e
/**
* Tests writing various GPX 1.0 tracks. This test creates programmatically the same features than
- * the ones found in {@code 1.0/track.xml}, then compare the written XML file with the expected file.
+ * the ones found in {@code 1.0/track.xml}, then compares the written XML file with the expected file.
*
* @throws Exception if an error occurred while writing the XML data.
*/
@@ -204,7 +205,7 @@ public final strictfp class WriterTest e
/**
* Tests writing various GPX 1.1 tracks. This test creates programmatically the same features than
- * the ones found in {@code 1.1/track.xml}, then compare the written XML file with the expected file.
+ * the ones found in {@code 1.1/track.xml}, then compares the written XML file with the expected file.
*
* @throws Exception if an error occurred while writing the XML data.
*/
@@ -229,6 +230,21 @@ public final strictfp class WriterTest e
* @param expected name of a test file containing the expected XML result.
*/
private void testFeatures(final Version version, final Type type, final String expected) throws Exception {
+ try (final Store store = create()) {
+ store.version = version;
+ testFeatures(store, type);
+ }
+ assertXmlEquals(WriterTest.class.getResourceAsStream(expected), toString(),
+ "xmlns:xsi", "xsi:schemaLocation", "xsi:type");
+ }
+
+ /**
+ * Writes way points, routes or tracks in the given store.
+ *
+ * @param store the store where to write.
+ * @param type the kind of feature to write: way point, route or track.
+ */
+ private void testFeatures(final Store store, final Type type) throws Exception {
final Types types = Types.DEFAULT;
/*
* Way Points as defined in "waypoint.xml" test file.
@@ -335,11 +351,37 @@ public final strictfp class WriterTest e
final Metadata metadata = new Metadata();
metadata.bounds = bounds;
metadata.creator = "DataProducer";
- try (final Store store = create()) {
- store.version = version;
- store.write(metadata, features.stream());
+ store.write(metadata, features.stream());
+ }
+
+ /**
+ * Tests coexistence of read and write operations by first reading part of a file,
+ * then switching in write mode.
+ *
+ * @throws Exception if an error occurred while creating the temporary test file,
+ * the test data or performing data store operation.
+ */
+ @Test
+ @DependsOnMethod("testRoutes110")
+ public void testInputReplacement() throws Exception {
+ try (final Store store = new Store(provider, new StorageConnector(
+ TestUtilities.createTemporaryFile(ReaderTest.class, "1.1/metadata.xml"))))
+ {
+ /*
+ * Read part of the file. We verify its content as a matter of principle,
+ * but the main purpose of following code is to advance in the stream.
+ */
+ ReaderTest.verifyMetadata((Metadata) store.getMetadata(), 3);
+ assertEquals("version", Store.V1_1, store.getVersion());
+ /*
+ * Replace the metadata content by route content. The data store should rewind
+ * to the begining of the file and replace the input stream by an output stream.
+ */
+ testFeatures(store, Type.ROUTE);
+ /*
+ * Following should revert the output stream back to an input stream and rewind again.
+ */
+ ReaderTest.verifyRoute110(store);
}
- assertXmlEquals(WriterTest.class.getResourceAsStream(expected), toString(),
- "xmlns:xsi", "xsi:schemaLocation", "xsi:type");
}
}