sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1807624 [2/4] - in /sis/branches/JDK9: ./ core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ core/sis-metadata/src/main/java/org/apache/sis/metadata/ core/sis-metadata/src/test/java/org/apache/sis/internal/metadata/ core/sis...
Date Thu, 07 Sep 2017 15:47:24 GMT
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;
 }



Mime
View raw message