sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject 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/...
Date Mon, 09 Jan 2017 08:15:11 GMT
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:     // ︙
                         case Tags.DESCRIPTION: // ︙
                         case Tags.SOURCE:      // ︙
                         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:      // ︙
                         case Tags.DESCRIPTION:  // ︙
                         case Tags.SOURCE:       // ︙
                         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(…)' will fail.  Note that if that reader was in use by someone else, the 'reader'
+             * field would be null and the 'new Writer(…)' 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");
     }
 }



Mime
View raw message