Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -45,6 +45,8 @@ import org.apache.sis.util.logging.Loggi
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.storage.Resources;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.ForwardOnlyStorageException;
 
 
 /**
@@ -244,6 +246,18 @@ public abstract class ChannelFactory {
     }
 
     /**
+     * Returns whether the streams or channels created by this factory is 
coupled with the {@code storage} argument
+     * given to the {@link #prepare prepare(…)} method. This is {@code true} 
if the storage is an {@link InputStream},
+     * {@link OutputStream} or {@link Channel}, and {@code false} if the 
storage is a {@link Path}, {@link File},
+     * {@link URL}, {@link URI} or equivalent.
+     *
+     * @return whether using the streams or channels will affect the original 
{@code storage} object.
+     */
+    public boolean isCoupled() {
+        return false;
+    }
+
+    /**
      * Returns {@code true} if this factory is capable to create another 
reader. This method returns {@code false}
      * if this factory is capable to create only one channel and {@link 
#reader(String)} has already been invoked.
      *
@@ -259,9 +273,10 @@ public abstract class ChannelFactory {
      *
      * @param  filename  data store name to report in case of failure.
      * @return the input stream.
+     * @throws DataStoreException if the channel is read-once.
      * @throws IOException if the input stream or its underlying byte channel 
can not be created.
      */
-    public InputStream inputStream(final String filename) throws IOException {
+    public InputStream inputStream(final String filename) throws 
DataStoreException, IOException {
         return Channels.newInputStream(reader(filename));
     }
 
@@ -271,9 +286,10 @@ public abstract class ChannelFactory {
      *
      * @param  filename  data store name to report in case of failure.
      * @return the output stream.
+     * @throws DataStoreException if the channel is write-once.
      * @throws IOException if the output stream or its underlying byte channel 
can not be created.
      */
-    public OutputStream outputStream(final String filename) throws IOException 
{
+    public OutputStream outputStream(final String filename) throws 
DataStoreException, IOException {
         return Channels.newOutputStream(writer(filename));
     }
 
@@ -284,9 +300,10 @@ public abstract class ChannelFactory {
      *
      * @param  filename  data store name to report in case of failure.
      * @return the channel for the given input.
+     * @throws DataStoreException if the channel is read-once.
      * @throws IOException if an error occurred while opening the channel.
      */
-    public abstract ReadableByteChannel reader(String filename) throws 
IOException;
+    public abstract ReadableByteChannel reader(String filename) throws 
DataStoreException, IOException;
 
     /**
      * Returns a byte channel from the output given to the {@link #prepare 
prepare(…)} method.
@@ -295,9 +312,10 @@ public abstract class ChannelFactory {
      *
      * @param  filename  data store name to report in case of failure.
      * @return the channel for the given output.
+     * @throws DataStoreException if the channel is write-once.
      * @throws IOException if an error occurred while opening the channel.
      */
-    public abstract WritableByteChannel writer(String filename) throws 
IOException;
+    public abstract WritableByteChannel writer(String filename) throws 
DataStoreException, IOException;
 
     /**
      * A factory that returns an existing channel <cite>as-is</cite>.
@@ -317,6 +335,14 @@ public abstract class ChannelFactory {
         }
 
         /**
+         * Returns {@code true} since use of channels or streams will affect 
the original storage object.
+         */
+        @Override
+        public boolean isCoupled() {
+            return true;
+        }
+
+        /**
          * Returns whether {@link #reader(String)} or {@link #writer(String)} 
can be invoked.
          */
         @Override
@@ -329,13 +355,19 @@ public abstract class ChannelFactory {
          * throws an exception on all subsequent invocations.
          */
         @Override
-        public ReadableByteChannel reader(final String filename) throws 
IOException {
+        public ReadableByteChannel reader(final String filename) throws 
DataStoreException, IOException {
             final Channel in = channel;
             if (in instanceof ReadableByteChannel) {
                 channel = null;
                 return (ReadableByteChannel) in;
             }
-            throw new 
IOException(Resources.format(Resources.Keys.StreamIsReadOnce_1, filename));
+            String message = Resources.format(in != null ? 
Resources.Keys.StreamIsNotReadable_1
+                                                         : 
Resources.Keys.StreamIsReadOnce_1, filename);
+            if (in != null) {
+                throw new IOException(message);                     // Stream 
is not readable.
+            } else {
+                throw new ForwardOnlyStorageException(message);     // Stream 
has already been read.
+            }
         }
 
         /**
@@ -343,13 +375,19 @@ public abstract class ChannelFactory {
          * throws an exception on all subsequent invocations.
          */
         @Override
-        public WritableByteChannel writer(final String filename) throws 
IOException {
-            final Channel in = channel;
-            if (in instanceof WritableByteChannel) {
+        public WritableByteChannel writer(final String filename) throws 
DataStoreException, IOException {
+            final Channel out = channel;
+            if (out instanceof WritableByteChannel) {
                 channel = null;
-                return (WritableByteChannel) in;
+                return (WritableByteChannel) out;
+            }
+            String message = Resources.format(out != null ? 
Resources.Keys.StreamIsNotWritable_1
+                                                          : 
Resources.Keys.StreamIsWriteOnce_1, filename);
+            if (out != null) {
+                throw new IOException(message);                     // Stream 
is not writable.
+            } else {
+                throw new ForwardOnlyStorageException(message);     // Stream 
has already been written.
             }
-            throw new 
IOException(Resources.format(Resources.Keys.StreamIsWriteOnce_1, filename));
         }
     }
 
@@ -357,7 +395,7 @@ public abstract class ChannelFactory {
      * A factory used as a fallback when we failed to convert a {@link File} 
to a {@link Path}.
      * This is used only if the conversion attempt threw an {@link 
InvalidPathException}. Such
      * failure is unlikely to happen, but if it happens anyway we try to open 
the channel in a
-     * way less surprising for the user (closer to the object he has 
specified).
+     * less surprising way for the user (i.e. closer to the object (s)he has 
specified).
      */
     private static final class Fallback extends ChannelFactory {
         /**

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -344,6 +344,14 @@ loop:   while ((c = read()) >= 0) {
 
     /**
      * Closes the {@linkplain #channel}.
+     * If the channel is backed by an {@link java.io.InputStream}, that stream 
will be closed too.
+     *
+     * <div class="section">Departure from Image I/O standard 
implementation</div>
+     * Java Image I/O wrappers around input/output streams do not close the 
underlying stream (see for example
+     * {@link javax.imageio.stream.FileCacheImageInputStream#close()} 
specification). But Apache SIS needs the
+     * underlying stream to be closed because we do not keep reference to the 
original input stream. Note that
+     * channels created by {@link 
java.nio.channels.Channels#newChannel(java.io.InputStream)} close the stream,
+     * which is the desired behavior for this method.
      *
      * @throws IOException if an error occurred while closing the channel.
      */

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -20,6 +20,7 @@ import java.util.Locale;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.LineNumberReader;
+import java.io.Reader;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.IOException;
@@ -236,7 +237,7 @@ public final class IOUtilities extends S
             return null;
         }
         /*
-         * Convert the URL to an URI, taking in account the encoding if any.
+         * Convert the URL to a URI, taking in account the encoding if any.
          *
          * Note: URL.toURI() is implemented as new URI(URL.toString()) where 
toString()
          * delegates to toExternalForm(), and all those methods are final. So 
we really
@@ -528,6 +529,30 @@ public final class IOUtilities extends S
     }
 
     /**
+     * Reads the next character as an Unicode code point. Unless end-of-file 
has been reached, the returned value is
+     * between {@value java.lang.Character#MIN_CODE_POINT} and {@value 
java.lang.Character#MAX_CODE_POINT} inclusive.
+     *
+     * @param  in  the reader from which to read code point.
+     * @return the next code point, or -1 on end of file.
+     * @throws IOException if an error occurred while reading characters.
+     *
+     * @since 0.8
+     */
+    public static int readCodePoint(final Reader in) throws IOException {
+        int c = in.read();
+        while (c >= Character.MIN_HIGH_SURROGATE && c <= 
Character.MAX_HIGH_SURROGATE) {
+            final int low = in.read();
+            if (low >= Character.MIN_LOW_SURROGATE && low <= 
Character.MAX_LOW_SURROGATE) {
+                c = Character.toCodePoint((char) c, (char) low);
+                break;
+            } else {
+                c = low;        // Discard orphan high surrogate and take the 
next character.
+            }
+        }
+        return c;
+    }
+
+    /**
      * Returns the error message for a file that can not be parsed.
      * The error message will contain the line number if available.
      *

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -20,11 +20,14 @@ import java.io.InputStream;
 import java.io.IOException;
 import javax.imageio.stream.ImageInputStream;
 
+// Branch-dependent imports
+import java.io.UncheckedIOException;
+
 
 /**
  * Wraps an {@link ImageInputStream} as a standard {@link InputStream}.
  *
- * @author  Martin Desruisseaux (IRD)
+ * @author  Martin Desruisseaux (IRD, Geomatys)
  * @version 0.8
  *
  * @see OutputStreamAdapter
@@ -41,13 +44,27 @@ public final class InputStreamAdapter ex
     public final ImageInputStream input;
 
     /**
+     * Position of the last mark created by {@link #mark(int)}, or the file 
beginning if there is no mark.
+     */
+    private long markPosition;
+
+    /**
+     * Count of marks created by {@link #mark()}, not counting the mark 
created by {@link #mark(int)}.
+     * We have to keep this count ourselves because {@link 
ImageInputStream#reset()} does nothing if
+     * there is no mark, and provides no API for letting us know if {@code 
reset()} worked.
+     */
+    private int nestedMarks;
+
+    /**
      * Constructs a new input stream.
      *
-     * @param input  the stream to wrap.
+     * @param  input  the stream to wrap.
+     * @throws IOException  if an error occurred while creating the adapter.
      */
-    public InputStreamAdapter(final ImageInputStream input) {
+    public InputStreamAdapter(final ImageInputStream input) throws IOException 
{
         assert !(input instanceof InputStream);
         this.input = input;
+        markPosition = input.getStreamPosition();
     }
 
     /**
@@ -105,31 +122,60 @@ public final class InputStreamAdapter ex
     }
 
     /**
-     * Marks the current position in this input stream.
+     * Discards all previous marks and marks the current position in this 
input stream.
+     * This method is part of {@link InputStream} API, where only one mark can 
be set and multiple
+     * calls to {@code reset()} move to the same position until {@code 
mark(int)} is invoked again.
      *
      * @param  readlimit  ignored.
+     * @throws UncheckedIOException if the mark can not be set.
      */
     @Override
     public void mark(final int readlimit) {
-        input.mark();
+        try {
+            markPosition = input.getStreamPosition();
+            input.flushBefore(markPosition);
+            nestedMarks = 0;
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);      // InputStream.mark() does 
not allow us to throw IOException.
+        }
     }
 
     /**
      * Marks the current position in this input stream.
+     * This method is part of {@link Markable} API, where marks can be nested.
+     * It is okay to invoke this method after {@link #mark(int)} (but not 
before).
      */
     @Override
     public void mark() {
         input.mark();
+        nestedMarks++;
     }
 
     /**
      * Repositions this stream to the position at the time the {@code mark} 
method was last called.
+     * This method has to comply with both {@link InputStream#reset()} and 
{@link Markable#reset()}
+     * contracts. It does that by choosing the first option in following list:
+     *
+     * <ul>
+     *   <li>If there is nested {@link #mark()} calls, then this {@code 
reset()} method sets the stream
+     *       position to the most recent unmatched call to {@code mark()}.</li>
+     *   <li>Otherwise if the {@link #mark(int)} method has been invoked, then 
this method sets the stream
+     *       position to the mark created by the most recent call to {@code 
mark(int)}. The {@code reset()}
+     *       method can be invoked many time; it will always set the position 
to the same mark
+     *       (this behavior is required by {@link InputStream} contract).</li>
+     *   <li>Otherwise this method sets the stream position to the position it 
had when this
+     *       {@code InputStreamAdapter} has been created.</li>
+     * </ul>
      *
      * @throws IOException if an I/O error occurs.
      */
     @Override
     public void reset() throws IOException {
-        input.reset();
+        if (--nestedMarks >= 0) {
+            input.reset();
+        } else {
+            input.seek(markPosition);
+        }
     }
 
     /**

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -17,7 +17,6 @@
 package org.apache.sis.internal.storage.io;
 
 import java.io.IOException;
-import java.nio.InvalidMarkException;
 
 
 /**
@@ -56,6 +55,8 @@ public interface Markable {
      * Calls to {@code mark()} and {@code reset()} can be nested arbitrarily.
      *
      * @throws IOException if this stream can not mark the current position.
+     *
+     * @see javax.imageio.stream.ImageInputStream#mark()
      */
     void mark() throws IOException;
 
@@ -64,8 +65,14 @@ public interface Markable {
      * An {@code IOException} may be be thrown if the previous marked position 
lies in the
      * discarded portion of the stream.
      *
-     * @throws InvalidMarkException if there is no mark.
+     * <p>If there is no mark, then the behavior is undefined.
+     * {@link java.io.InputStream#reset()} specifies that we shall move to the 
file beginning.
+     * {@link javax.imageio.stream.ImageInputStream#reset()} specifies that we 
shall do nothing.
+     * {@link ChannelDataInput#reset()} throws {@link 
java.nio.InvalidMarkException}.</p>
+     *
      * @throws IOException if a mark was defined but this stream can not move 
to that position.
+     *
+     * @see javax.imageio.stream.ImageInputStream#reset()
      */
     void reset() throws IOException;
 }

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -19,6 +19,7 @@ package org.apache.sis.internal.storage.
 import java.io.Reader;
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import org.apache.sis.internal.storage.io.IOUtilities;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.ProbeResult;
@@ -41,6 +42,7 @@ public abstract class FirstKeywordPeek {
 
     /**
      * The read-ahead limit when reading a text from a {@link Reader}.
+     * Should be no more than {@code StorageConnector.DEFAULT_BUFFER_SIZE / 2}.
      */
     static final int READ_AHEAD_LIMIT = 2048;
 
@@ -78,7 +80,7 @@ public abstract class FirstKeywordPeek {
             return -1;
         }
         int c;
-        while ((c = reader.read()) >= 0) {
+        while ((c = IOUtilities.readCodePoint(reader)) >= 0) {
             if (!Character.isWhitespace(c)) break;
         }
         return c;
@@ -94,7 +96,7 @@ public abstract class FirstKeywordPeek {
                 if (!buffer.hasRemaining()) break;
                 c = (char) buffer.get();
             } else {
-                c = reader.read();
+                c = IOUtilities.readCodePoint(reader);
                 if (c < 0) break;
             }
         } while (!Characters.isLineOrParagraphSeparator(c));
@@ -159,7 +161,7 @@ public abstract class FirstKeywordPeek {
                         }
                         keyword[pos++] = (char) c;
                     }
-                    c = (buffer == null) ? reader.read() : 
buffer.hasRemaining() ? (char) buffer.get() : -1;
+                    c = (buffer == null) ? IOUtilities.readCodePoint(reader) : 
buffer.hasRemaining() ? (char) buffer.get() : -1;
                 } while ((s = isKeywordChar(c)) >= ACCEPT);
                 /*
                  * At this point we finished to read and store the keyword.
@@ -187,7 +189,7 @@ public abstract class FirstKeywordPeek {
     }
 
     /**
-     * Returns {@code true} if the given first non-white character after the 
keywordis one of the expected characters.
+     * Returns {@code true} if the given first non-white character after the 
keyword is one of the expected characters.
      *
      * @param  c  the first non-white character after the keyword, or -1 if we 
reached the end of stream.
      * @return {@code true} if the given character is one of the expected 
post-keyword characters.

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -33,14 +33,14 @@ import org.apache.sis.internal.storage.R
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.io.wkt.WKTFormat;
 import org.apache.sis.io.wkt.Warnings;
-import org.apache.sis.storage.Resource;
-import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
+import org.apache.sis.storage.UnsupportedStorageException;
 import org.apache.sis.internal.referencing.DefinitionVerifier;
 import org.apache.sis.internal.storage.MetadataBuilder;
-import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.storage.URIDataStore;
+import org.apache.sis.setup.OptionKey;
 import org.apache.sis.util.CharSequences;
 
 
@@ -52,7 +52,7 @@ import org.apache.sis.util.CharSequences
  * @since   0.7
  * @module
  */
-final class Store extends DataStore {
+final class Store extends URIDataStore {
     /**
      * Arbitrary size limit. Files that big are likely to be something else 
than WKT,
      * so this limit allows earlier error reporting than loading huge amount 
of data
@@ -89,7 +89,8 @@ final class Store extends DataStore {
         source  = connector.getStorageAs(Reader.class);
         connector.closeAllExcept(source);
         if (source == null) {
-            throw new 
DataStoreException(Errors.format(Errors.Keys.CanNotOpen_1, 
super.getDisplayName()));
+            throw new UnsupportedStorageException(super.getLocale(), 
StoreProvider.NAME,
+                    connector.getStorage(), 
connector.getOption(OptionKey.OPEN_OPTIONS));
         }
     }
 
@@ -194,15 +195,6 @@ final class Store extends DataStore {
     }
 
     /**
-     * There is currently no resource associated to Well Known Text format 
since we parse only CRS.
-     * Future versions may return resources if we parse also geometries.
-     */
-    @Override
-    public Resource getRootResource() throws DataStoreException {
-        return null;
-    }
-
-    /**
      * Closes this data store and releases any underlying resources.
      *
      * @throws DataStoreException if an error occurred while closing this data 
store.

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -20,12 +20,12 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 import org.apache.sis.storage.DataStore;
-import org.apache.sis.storage.DataStoreProvider;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.ProbeResult;
 import org.apache.sis.internal.storage.Capability;
 import org.apache.sis.internal.storage.Capabilities;
+import org.apache.sis.internal.storage.URIDataStore;
 import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.util.Version;
 
@@ -39,7 +39,12 @@ import org.apache.sis.util.Version;
  * @module
  */
 @Capabilities(Capability.READ)
-public final class StoreProvider extends DataStoreProvider {
+public final class StoreProvider extends URIDataStore.Provider {
+    /**
+     * The format name.
+     */
+    static final String NAME = "WKT";
+
     /**
      * The {@value} MIME type.
      */
@@ -152,7 +157,7 @@ public final class StoreProvider extends
      */
     @Override
     public String getShortName() {
-        return "WKT";
+        return NAME;
     }
 
     /**

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -25,6 +25,7 @@ import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.ProbeResult;
+import org.apache.sis.internal.storage.io.IOUtilities;
 import org.apache.sis.internal.storage.DocumentedStoreProvider;
 
 
@@ -138,7 +139,7 @@ public abstract class AbstractProvider e
             final ProbeResult result = new MimeTypeDetector(types) {
                 private int remaining = READ_AHEAD_LIMIT;
                 @Override int read() throws IOException {
-                    return (--remaining >= 0) ? reader.read() : -1;
+                    return (--remaining >= 0) ? 
IOUtilities.readCodePoint(reader) : -1;
                 }
             }.probeContent();
             reader.reset();

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -98,7 +98,7 @@ abstract class MimeTypeDetector {
     /**
      * Reads a single byte or character, or -1 if we reached the end of the 
stream portion that we are allowed
      * to read. We are typically not allowed to read the full stream because 
only a limited amount of bytes is
-     * cached.
+     * cached. This method may return a Unicode code point (i.e. the returned 
value may not fit in {@code char}).
      *
      * @return the character, or -1 on EOF.
      * @throws IOException if an error occurred while reading the byte or 
character.

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -30,16 +30,17 @@ import org.opengis.util.FactoryException
 import org.opengis.referencing.ReferenceSystem;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.xml.XML;
-import org.apache.sis.storage.Resource;
-import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.UnsupportedStorageException;
 import org.apache.sis.metadata.iso.DefaultMetadata;
 import org.apache.sis.util.logging.WarningListener;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.internal.storage.URIDataStore;
 import org.apache.sis.internal.storage.MetadataBuilder;
 import org.apache.sis.internal.referencing.DefinitionVerifier;
+import org.apache.sis.setup.OptionKey;
 
 
 /**
@@ -59,7 +60,7 @@ import org.apache.sis.internal.referenci
  * @since   0.4
  * @module
  */
-final class Store extends DataStore {
+final class Store extends URIDataStore {
     /**
      * The input stream or reader, set by the constructor and cleared when no 
longer needed.
      */
@@ -97,7 +98,8 @@ final class Store extends DataStore {
         final Closeable c = input(source);
         connector.closeAllExcept(c);
         if (c == null) {
-            throw new 
DataStoreException(Errors.format(Errors.Keys.CanNotOpen_1, 
super.getDisplayName()));
+            throw new UnsupportedStorageException(super.getLocale(), 
StoreProvider.NAME,
+                    connector.getStorage(), 
connector.getOption(OptionKey.OPEN_OPTIONS));
         }
     }
 
@@ -209,15 +211,6 @@ final class Store extends DataStore {
     }
 
     /**
-     * Current implementation does not provide any resource since it is only 
about metadata.
-     * Futures versions may return resources if Apache SIS provides a wider 
GML support.
-     */
-    @Override
-    public Resource getRootResource() throws DataStoreException {
-        return null;
-    }
-
-    /**
      * Closes this data store and releases any underlying resources.
      *
      * @throws DataStoreException if an error occurred while closing this data 
store.

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -35,6 +35,11 @@ import org.apache.sis.internal.storage.C
 @Capabilities(Capability.READ)
 public final class StoreProvider extends AbstractProvider {
     /**
+     * The format name.
+     */
+    static final String NAME = "XML";
+
+    /**
      * Creates a new provider.
      */
     public StoreProvider() {
@@ -52,7 +57,7 @@ public final class StoreProvider extends
      */
     @Override
     public String getShortName() {
-        return "XML";
+        return NAME;
     }
 
     /**

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -17,6 +17,9 @@
 package org.apache.sis.storage;
 
 import java.util.Collection;
+import org.opengis.metadata.Metadata;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.internal.storage.Resources;
 
 
 /**
@@ -42,7 +45,7 @@ import java.util.Collection;
  *
  * The same resource may be part of more than one aggregate. For example the 
same resource could be part of
  * a <cite>production series</cite> and a <cite>transfer aggregate</cite>. In 
Apache SIS implementation,
- * those two kinds of aggregate will usually be created by different {@link 
DataStore} instances.
+ * those two kinds of aggregate will usually be implemented by different 
{@link DataStore} instances.
  *
  * <div class="section">Metadata</div>
  * Aggregates should have {@link #getMetadata() metadata} /
@@ -82,4 +85,47 @@ public interface Aggregate extends Resou
      * @throws DataStoreException if an error occurred while fetching the 
components.
      */
     Collection<Resource> components() throws DataStoreException;
+
+    /**
+     * Adds a new {@code Resource} in this {@code Aggregate}.
+     * The given {@link Resource} will be copied, and the <cite>effectively 
added</cite> resource returned.
+     * The effectively added resource may differ from the given resource in 
many aspects.
+     * The possible changes may include the followings but not only:
+     * <ul>
+     *  <li>types and properties names</li>
+     *  <li>{@link CoordinateReferenceSystem}</li>
+     *  <li>{@link Metadata}</li>
+     * </ul>
+     *
+     * <div class="note"><b>Warning:</b>
+     * copying informations between stores may produce differences in many 
aspects.
+     * The range of changes depends both on the original {@link Resource} 
structure
+     * and the target {@code Resource} structure. If the differences are too 
large,
+     * then this {@code Aggregate} may throw an exception.
+     * </div>
+     *
+     * <p>The default implementation throws {@link 
ReadOnlyStorageException}.</p>
+     *
+     * @param  resource  the resource to copy in this {@code Aggregate}.
+     * @return the effectively added resource. May be {@code resource} itself 
if it has been added verbatim.
+     * @throws ReadOnlyStorageException if this instance does not support 
write operations.
+     * @throws DataStoreException if the given resource can not be stored in 
this {@code Aggregate} for another reason.
+     */
+    default Resource add(Resource resource) throws ReadOnlyStorageException, 
DataStoreException {
+        throw new ReadOnlyStorageException(this, 
Resources.Keys.StoreIsReadOnly);
+    }
+
+    /**
+     * Removes a {@code Resource} from this {@code Aggregate}.
+     * This operation is destructive: the {@link Resource} and it's related 
data will be removed.
+     *
+     * <p>The default implementation throws {@link 
ReadOnlyStorageException}.</p>
+     *
+     * @param  resource  child resource to remove, should not be null.
+     * @throws ReadOnlyStorageException if this instance does not support 
write operations.
+     * @throws DataStoreException if the given resource could not be removed 
for another reason.
+     */
+    default void remove(Resource resource) throws ReadOnlyStorageException, 
DataStoreException {
+        throw new ReadOnlyStorageException(this, 
Resources.Keys.StoreIsReadOnly);
+    }
 }

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.storage;
 
+import org.opengis.geometry.Envelope;
+
 
 /**
  * Collection of features that share a common set of attributes or properties.
@@ -45,4 +47,28 @@ package org.apache.sis.storage;
  * @module
  */
 public interface DataSet extends Resource {
+    /**
+     * Returns the spatio-temporal extent of this resource in its most natural 
coordinate reference system.
+     * The following relationship to {@linkplain #getMetadata()} should hold:
+     *
+     * <ul>
+     *   <li>The envelope should be contained in the union of all geographic, 
vertical or temporal extents
+     *       described by {@code metadata} /
+     *       {@link 
org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() 
identificationInfo} /
+     *       {@link 
org.apache.sis.metadata.iso.identification.AbstractIdentification#getExtents() 
extent}.</li>
+     *   <li>The coordinate reference system should be one of the instances 
returned by
+     *       {@link 
org.apache.sis.metadata.iso.DefaultMetadata#getReferenceSystemInfo() 
referenceSystemInfo}.</li>
+     * </ul>
+     *
+     * The envelope should use the coordinate reference system (CRS)
+     * that most closely matches the geometry of the resource storage. It is 
often a
+     * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS 
projected CRS}, but other types like
+     * {@linkplain org.apache.sis.referencing.crs.DefaultEngineeringCRS 
engineering CRS} are also allowed.
+     * If this resource uses many different CRS with none of them covering all 
data, then the envelope should use a
+     * global system (typically a {@linkplain 
org.apache.sis.referencing.crs.DefaultGeocentricCRS geographic CRS}).
+     *
+     * @return the spatio-temporal resource extent. Should not be {@code null}.
+     * @throws DataStoreException if an error occurred while reading or 
computing the envelope.
+     */
+    Envelope getEnvelope() throws DataStoreException;
 }

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -22,6 +22,7 @@ import java.util.IdentityHashMap;
 import java.util.NoSuchElementException;
 import org.opengis.metadata.Metadata;
 import org.opengis.metadata.identification.Identification;
+import org.opengis.parameter.ParameterValueGroup;
 import org.apache.sis.util.Localized;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.logging.WarningListener;
@@ -32,6 +33,13 @@ import org.apache.sis.internal.util.Cita
 
 /**
  * Manages a series of features, coverages or sensor data.
+ * Different {@code DataStore} subclasses exist for different formats (netCDF, 
GeoTIFF, <i>etc.</i>).
+ * The supported format can be identifier by the {@linkplain #getProvider() 
provider}.
+ *
+ * <p>Each data store is itself a {@link Resource}. The data store subclasses 
should implement
+ * a more specialized {@code Resource} interface depending on the format 
characteristics.
+ * For example a {@code DataStore} for ShapeFiles will implement the {@link 
FeatureSet} interface,
+ * while a {@code DataStore} for netCDF files will implement the {@link 
Aggregate} interface.</p>
  *
  * <div class="section">Thread safety policy</div>
  * This {@code DataStore} base class is thread-safe. However subclasses do not 
need to be thread-safe.
@@ -46,13 +54,15 @@ import org.apache.sis.internal.util.Cita
  * @since 0.3
  * @module
  */
-public abstract class DataStore implements Localized, AutoCloseable {
+public abstract class DataStore implements Resource, Localized, AutoCloseable {
     /**
      * The factory that created this {@code DataStore} instance, or {@code 
null} if unspecified.
      * This information can be useful for fetching information common to all 
{@code DataStore}
      * instances of the same class.
      *
      * @since 0.8
+     *
+     * @see #getProvider()
      */
     protected final DataStoreProvider provider;
 
@@ -112,6 +122,21 @@ public abstract class DataStore implemen
     }
 
     /**
+     * Returns the factory that created this {@code DataStore} instance.
+     * The provider gives additional information on this {@code DataStore} 
such as a format description
+     * and a list of parameters that can be used for opening data stores of 
the same class.
+     *
+     * @return the factory that created this {@code DataStore} instance, or 
{@code null} if unspecified.
+     *
+     * @see #provider
+     *
+     * @since 0.8
+     */
+    public DataStoreProvider getProvider() {
+        return provider;
+    }
+
+    /**
      * Returns a short name or label for this data store.
      * The returned name can be used in user interfaces or in error messages.
      * It may be a title in natural language, but should be relatively short.
@@ -169,6 +194,29 @@ public abstract class DataStore implemen
     }
 
     /**
+     * Returns the parameters used to open this data store.
+     * The collection of legal parameters is implementation-dependent
+     * ({@linkplain 
org.apache.sis.parameter.DefaultParameterValue#getDescriptor() their 
description}
+     * is given by {@link DataStoreProvider#getOpenParameters()}),
+     * but should contain at least a parameter named {@value 
DataStoreProvider#LOCATION}
+     * with a {@link java.net.URI}, {@link java.nio.file.Path} or {@link 
javax.sql.DataSource} value.
+     *
+     * <p>In the event a data store must be closed and reopened later, those 
parameters can be stored in a file or
+     * database and used for {@linkplain 
DataStoreProvider#open(ParameterValueGroup) creating a new store} later.</p>
+     *
+     * <p>In some cases, for stores reading in-memory data or other inputs 
that can not fit with
+     * {@code ParameterDescriptorGroup} requirements (for example an {@link 
java.io.InputStream}
+     * connected to unknown or no {@link java.net.URL}), this method may 
return null.</p>
+     *
+     * @return parameters used for opening this {@code DataStore}, or {@code 
null} if not available.
+     *
+     * @see DataStoreProvider#getOpenParameters()
+     *
+     * @since 0.8
+     */
+    public abstract ParameterValueGroup getOpenParameters();
+
+    /**
      * Returns information about the data store as a whole. The returned 
metadata object can contain
      * information such as the spatiotemporal extent of all contained 
{@linkplain Resource resources},
      * contact information about the creator or distributor, data quality, 
update frequency, usage constraints,
@@ -179,22 +227,10 @@ public abstract class DataStore implemen
      *
      * @see Resource#getMetadata()
      */
+    @Override
     public abstract Metadata getMetadata() throws DataStoreException;
 
     /**
-     * Returns the starting point from which all resources in this data store 
can be accessed.
-     * A resource can be for example a air temperature map or the set of all 
bridges in a city.
-     * If this data store contains only one resource, then that resource is 
returned directly.
-     * Otherwise if this data store contains more than one resource, then this 
method returns
-     * an {@link Aggregate} from which other resources can be accessed.
-     *
-     * @return the starting point of all resources in this data store,
-     *         or {@code null} if this data store does not contain any 
resources.
-     * @throws DataStoreException if an error occurred while reading the data.
-     */
-    public abstract Resource getRootResource() throws DataStoreException;
-
-    /**
      * Searches for a resource identified by the given identifier.
      * The given identifier should match the following metadata element of a 
resource:
      *
@@ -207,8 +243,10 @@ public abstract class DataStore implemen
      * is {@code "foo:bar"}, then this method may accept {@code "bar"} as a 
synonymous of {@code "foo:bar"}
      * provided that it does not introduce ambiguity.
      *
-     * <p>The default implementation verifies the {@linkplain 
#getRootResource() root resource}, then iterates over
-     * components of {@link Aggregate}s. If a match is found without 
ambiguity, the associated resource is returned.
+     * <p>The default implementation verifies if above criterion matches to 
this {@code DataStore}
+     * (which is itself a resource), then iterates recursively over {@link 
Aggregate} components
+     * if this data store is an aggregate.
+     * If a match is found without ambiguity, the associated resource is 
returned.
      * Otherwise an exception is thrown. Subclasses are encouraged to override 
this method with a more efficient
      * implementation.</p>
      *
@@ -219,7 +257,7 @@ public abstract class DataStore implemen
      */
     public Resource findResource(final String identifier) throws 
DataStoreException {
         ArgumentChecks.ensureNonEmpty("identifier", identifier);
-        final Resource resource = findResource(identifier, getRootResource(), 
new IdentityHashMap<>());
+        final Resource resource = findResource(identifier, this, new 
IdentityHashMap<>());
         if (resource != null) {
             return resource;
         }

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -16,11 +16,16 @@
  */
 package org.apache.sis.storage;
 
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.ParameterNotFoundException;
 import org.opengis.metadata.distribution.Format;
 import org.apache.sis.internal.simple.SimpleFormat;
+import org.apache.sis.internal.storage.Resources;
 import org.apache.sis.metadata.iso.citation.DefaultCitation;
 import org.apache.sis.metadata.iso.distribution.DefaultFormat;
 import org.apache.sis.measure.Range;
+import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Version;
 
 
@@ -52,12 +57,29 @@ import org.apache.sis.util.Version;
  * However the {@code DataStore} instances created by the providers do not 
need to be thread-safe.
  *
  * @author  Martin Desruisseaux (Geomatys)
+ * @author  Johann Sorel (Geomatys)
  * @version 0.8
  * @since   0.3
  * @module
  */
 public abstract class DataStoreProvider {
     /**
+     * Name of the parameter that specifies the data store location.
+     * A parameter named {@value} should be included in the group of 
parameters returned by {@link #getOpenParameters()}.
+     * The parameter value is often a {@link java.net.URI} or a {@link 
java.nio.file.Path}, but other types are allowed.
+     *
+     * <p>Implementors are encouraged to define a parameter with this name
+     * to ensure a common and consistent definition among providers.
+     * The parameter should be defined as mandatory and declared with a 
well-known Java class such as
+     * {@link java.net.URI}, {@link java.nio.file.Path}, JDBC {@linkplain 
javax.sql.DataSource}, <i>etc</i>.
+     * The type should have a compact textual representation, for 
serialization in XML or configuration files.
+     * Consequently {@link java.io.InputStream} and {@link 
java.nio.channels.Channel} should be avoided.</p>
+     *
+     * @see #getOpenParameters()
+     */
+    public static final String LOCATION = "location";
+
+    /**
      * Creates a new provider.
      */
     protected DataStoreProvider() {
@@ -130,6 +152,37 @@ public abstract class DataStoreProvider
     }
 
     /**
+     * Returns a description of all parameters accepted by this provider for 
opening a data store.
+     * Those parameters provide an alternative to {@link StorageConnector} for 
opening a {@link DataStore}
+     * from a path or URL, together with additional information like character 
encoding.
+     *
+     * <p>Implementors are responsible for declaring all parameters and 
whether they are mandatory or optional.
+     * It is recommended to define at least a parameter named {@value 
#LOCATION}.
+     * That parameter will be recognized by the default {@code 
DataStoreProvider} methods and used whenever a
+     * {@link StorageConnector} is required.</p>
+     *
+     * <div class="note"><b>Alternative:</b>
+     * the main differences between the use of {@code StorageConnector} and 
parameters are:
+     * <ul class="verbose">
+     *   <li>{@code StorageConnector} is designed for use with file or stream 
of unknown format;
+     *       the format is automatically detected. By contrast, the use of 
parameters require to
+     *       determine the format first (i.e. select a {@code 
DataStoreProvider}).</li>
+     *   <li>Parameters can be used to dynamically generate user configuration 
interfaces
+     *       and provide fine grain control over the store general behavior 
such as caching,
+     *       time-outs, encoding, <i>etc</i>.</li>
+     *   <li>Parameters can more easily be serialized in XML or configuration 
files.</li>
+     * </ul></div>
+     *
+     * @return description of the parameters required for opening a {@link 
DataStore}.
+     *
+     * @see #open(ParameterValueGroup)
+     * @see DataStore#getOpenParameters()
+     *
+     * @since 0.8
+     */
+    public abstract ParameterDescriptorGroup getOpenParameters();
+
+    /**
      * Indicates if the given storage appears to be supported by the {@code 
DataStore}s created by this provider.
      * The most typical return values are:
      *
@@ -187,6 +240,10 @@ public abstract class DataStoreProvider
 
     /**
      * Returns a data store implementation associated with this provider.
+     * This method is typically invoked when the format is not known in advance
+     * (the {@link #probeContent(StorageConnector)} method can be tested on 
many providers)
+     * or when the input is not a type accepted by {@link 
#open(ParameterValueGroup)}
+     * (for example an {@link java.io.InputStream}).
      *
      * <div class="section">Implementation note</div>
      * Implementors shall invoke {@link 
StorageConnector#closeAllExcept(Object)} after {@code DataStore}
@@ -199,4 +256,50 @@ public abstract class DataStoreProvider
      * @see DataStores#open(Object)
      */
     public abstract DataStore open(StorageConnector connector) throws 
DataStoreException;
+
+    /**
+     * Returns a data store implementation associated with this provider for 
the given parameters.
+     * The {@code DataStoreProvider} instance needs to be known before 
parameters are initialized,
+     * since the parameters are implementation-dependent. Example:
+     *
+     * {@preformat java
+     *     DataStoreProvider provider = ...;
+     *     ParameterValueGroup pg = provider.getOpenParameters().createValue();
+     *     pg.parameter(DataStoreProvider.LOCATION, myURL);
+     *     // Set any other parameters if desired.
+     *     try (DataStore ds = provider.open(pg)) {
+     *         // Use the data store.
+     *     }
+     * }
+     *
+     * <div class="section">Implementation note</div>
+     * The default implementation gets the value of a parameter named {@value 
#LOCATION}.
+     * That value (typically a path or URL) is given to {@link 
StorageConnector} constructor,
+     * which is then passed to {@link #open(StorageConnector)}.
+     *
+     * @param  parameters  opening parameters as defined by {@link 
#getOpenParameters()}.
+     * @return a data store implementation associated with this provider for 
the given parameters.
+     * @throws DataStoreException if an error occurred while creating the data 
store instance.
+     *
+     * @see #getOpenParameters()
+     *
+     * @since 0.8
+     */
+    public DataStore open(final ParameterValueGroup parameters) throws 
DataStoreException {
+        ArgumentChecks.ensureNonNull("parameter", parameters);
+        ParameterNotFoundException cause = null;
+        Object location;
+        try {
+            location = parameters.parameter(LOCATION).getValue();
+        } catch (ParameterNotFoundException e) {
+            location = null;
+            cause = e;
+        }
+        if (location == null) {
+            throw new 
IllegalOpenParameterException(Resources.format(Resources.Keys.UndefinedParameter_2,
+                    getShortName(), LOCATION), cause);
+        }
+        final StorageConnector connector = new StorageConnector(location);
+        return open(connector);
+    }
 }

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -16,7 +16,12 @@
  */
 package org.apache.sis.storage;
 
+import java.util.Iterator;
+import org.apache.sis.internal.storage.Resources;
+
 // Branch-dependent imports
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
 import java.util.stream.Stream;
 import org.opengis.feature.Feature;
 import org.opengis.feature.FeatureType;
@@ -101,4 +106,63 @@ public interface FeatureSet extends Data
      * @throws DataStoreException if an error occurred while creating the 
stream.
      */
     Stream<Feature> features(boolean parallel) throws DataStoreException;
+
+    /**
+     * Inserts new features in this {@code FeatureSet}.
+     * Any feature already present in the {@link FeatureSet} will remain 
unmodified.
+     *
+     * <div class="note"><b>API note:</b>
+     * this method expects an {@link Iterator} rather then a {@link 
java.util.stream.Stream} for easing
+     * inter-operability with various API. Implementing a custom {@link 
Iterator} requires less effort
+     * than implementing a {@link Stream}. On the other side if the user has a 
{@link Stream},
+     * obtaining an {@link Iterator} can be done by a call to {@link 
Stream#iterator()}.</div>
+     *
+     * <p>The default implementation throws {@link 
ReadOnlyStorageException}.</p>
+     *
+     * @param  features features to insert in this {@code FeatureSet}.
+     * @throws ReadOnlyStorageException if this instance does not support 
write operations.
+     * @throws DataStoreException if another error occurred while storing new 
features.
+     */
+    default void add(Iterator<? extends Feature> features) throws 
ReadOnlyStorageException, DataStoreException {
+        throw new ReadOnlyStorageException(this, 
Resources.Keys.StoreIsReadOnly);
+    }
+
+    /**
+     * Removes all features from this {@code FeatureSet} which matches the 
given predicate.
+     *
+     * <p>The default implementation throws {@link 
ReadOnlyStorageException}.</p>
+     *
+     * @param  filter  a predicate which returns true for resources to be 
removed.
+     * @return {@code true} if any elements were removed.
+     * @throws ReadOnlyStorageException if this instance does not support 
write operations.
+     * @throws DataStoreException if another error occurred while removing 
features.
+     */
+    default boolean removeIf(Predicate<? super Feature> filter) throws 
ReadOnlyStorageException, DataStoreException {
+        throw new ReadOnlyStorageException(this, 
Resources.Keys.StoreIsReadOnly);
+    }
+
+    /**
+     * Updates all features from this {@code FeatureSet} which matches the 
given predicate.
+     * For each {@link Feature} instance matching the given {@link Predicate},
+     * the <code>{@linkplain UnaryOperator#apply 
UnaryOperator.apply(Feature)}</code> method will be invoked.
+     * {@code UnaryOperator}s are free to modify the given {@code Feature} 
<i>in-place</i> or to return a
+     * different feature instance. Two behaviors are possible:
+     * <ul>
+     *   <li>If the operator returns a non-null {@link Feature}, then the 
modified feature is stored
+     *       in replacement of the previous feature (not necessarily at the 
same location).</li>
+     *   <li>If the operator returns {@code null}, then the feature will be 
removed from the {@code FeatureSet}.</li>
+     * </ul>
+     *
+     * <p>The default implementation throws {@link 
ReadOnlyStorageException}.</p>
+     *
+     * @param  filter   a predicate which returns true for resources to be 
updated.
+     * @param  updater  operation called for each matching {@link Feature}.
+     * @throws ReadOnlyStorageException if this instance does not support 
write operations.
+     * @throws DataStoreException if another error occurred while replacing 
features.
+     */
+    default void replaceIf(Predicate<? super Feature> filter, 
UnaryOperator<Feature> updater)
+            throws ReadOnlyStorageException, DataStoreException
+    {
+        throw new ReadOnlyStorageException(this, 
Resources.Keys.StoreIsReadOnly);
+    }
 }

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -35,6 +35,8 @@ import org.apache.sis.internal.storage.i
  * @version 0.8
  * @since   0.8
  * @module
+ *
+ * @see ReadOnlyStorageException
  */
 public class ForwardOnlyStorageException extends DataStoreException {
     /**
@@ -58,7 +60,17 @@ public class ForwardOnlyStorageException
     }
 
     /**
-     * Creates a localized exception with a default message saying that the 
stream is read-once.
+     * Creates an exception with the specified details message and cause.
+     *
+     * @param message  the detail message in the default locale.
+     * @param cause    the cause for this exception.
+     */
+    public ForwardOnlyStorageException(final String message, final Throwable 
cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Creates a localized exception with a default message saying that the 
stream is read-once or write-once.
      *
      * @param locale    the locale of the message to be returned by {@link 
#getLocalizedMessage()}, or {@code null}.
      * @param filename  name of the file or data store where the error 
occurred.

Modified: 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java?rev=1807624&r1=1807623&r2=1807624&view=diff
==============================================================================
--- 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java
 [UTF-8] (original)
+++ 
sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java
 [UTF-8] Thu Sep  7 15:47:24 2017
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.storage;
 
-import org.opengis.geometry.Envelope;
 import org.opengis.metadata.Metadata;
 
 
@@ -28,8 +27,8 @@ import org.opengis.metadata.Metadata;
  * organization, in which case only metadata are provided. If the resource is 
digital, then {@code Resource}s
  * should be instances of sub-types like {@link Aggregate} or {@link 
FeatureSet}.
  *
- * <p>The resources contained in a data store can be obtained by a call to 
{@link DataStore#getRootResource()}.
- * If the data store contains resources for many feature types or coverages, 
then the root resource will be an
+ * <p>{@code DataStore}s are themselves closeable resources.
+ * If the data store contains resources for many feature types or coverages, 
then the data store will be an
  * instance of {@link Aggregate}. The {@linkplain Aggregate#components() 
components} of an aggregate can be
  * themselves other aggregates, thus forming a tree.</p>
  *
@@ -37,14 +36,13 @@ import org.opengis.metadata.Metadata;
  * this type is closely related to the {@code DS_Resource} type defined by ISO 
19115.
  * The Apache SIS type differs from the ISO type by being more closely related 
to data extraction,
  * as can been seen from the checked {@link DataStoreException} thrown by most 
methods.
- * Convenience methods for frequently requested information – for example 
{@link #getEnvelope()} – were added.
+ * Convenience methods for frequently requested information – for example 
{@link DataSet#getEnvelope()} – were added.
  * The sub-types performing the actual data extraction – for example {@link 
FeatureSet} – are specific to Apache SIS.
  * </div>
  *
  * @author  Johann Sorel (Geomatys)
  * @version 0.8
  *
- * @see DataStore#getRootResource()
  * @see Aggregate#components()
  *
  * @since 0.8
@@ -53,8 +51,6 @@ import org.opengis.metadata.Metadata;
 public interface Resource {
     /**
      * Returns information about this resource.
-     * If this resource is the {@linkplain DataStore#getRootResource() data 
store root resource},
-     * then this method may return the same metadata instance than {@link 
DataStore#getMetadata()}.
      * If this resource is an {@link Aggregate}, then the metadata may 
enumerate characteristics
      * (spatio-temporal extents, feature types, range dimensions, <i>etc.</i>) 
of all
      * {@linkplain Aggregate#components() components} in the aggregate, or 
summarize them (for example by omitting
@@ -107,29 +103,4 @@ public interface Resource {
      * @see DataStore#getMetadata()
      */
     Metadata getMetadata() throws DataStoreException;
-
-    /**
-     * Returns the spatio-temporal extent of this resource in its most natural 
coordinate reference system.
-     * The following relationship to {@linkplain #getMetadata()} should hold:
-     *
-     * <ul>
-     *   <li>The envelope should be contained in the union of all geographic, 
vertical or temporal extents
-     *       described by {@code metadata} /
-     *       {@link 
org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() 
identificationInfo} /
-     *       {@link 
org.apache.sis.metadata.iso.identification.AbstractIdentification#getExtents() 
extent}.</li>
-     *   <li>The coordinate reference system should be one of the instances 
returned by
-     *       {@link 
org.apache.sis.metadata.iso.DefaultMetadata#getReferenceSystemInfo() 
referenceSystemInfo}.</li>
-     * </ul>
-     *
-     * The envelope should use the coordinate reference system (CRS)
-     * that most closely matches the geometry of the resource storage. It is 
often a
-     * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS 
projected CRS}, but other types like
-     * {@linkplain org.apache.sis.referencing.crs.DefaultEngineeringCRS 
engineering CRS} are also allowed.
-     * If this resource uses many different CRS with none of them covering all 
data, then the envelope should use a
-     * global system (typically a {@linkplain 
org.apache.sis.referencing.crs.DefaultGeocentricCRS geographic CRS}).
-     *
-     * @return the spatio-temporal resource extent. Should not be {@code null}.
-     * @throws DataStoreException if an error occurred while reading or 
computing the envelope.
-     */
-    Envelope getEnvelope() throws DataStoreException;
 }


Reply via email to