sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1817597 [17/19] - in /sis/branches/ISO-19115-3: ./ application/ application/sis-console/ application/sis-console/src/main/artifact/ application/sis-console/src/main/artifact/lib/ application/sis-console/src/main/artifact/lib/darwin/ applic...
Date Sat, 09 Dec 2017 10:57:47 GMT
Modified: sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -21,6 +21,8 @@ import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.Collections;
 import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -43,21 +45,26 @@ import java.nio.channels.ReadableByteCha
 import java.nio.channels.WritableByteChannel;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.logging.WarningListeners;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.storage.Resources;
+import org.apache.sis.storage.DataStore;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.ForwardOnlyStorageException;
 
 
 /**
  * Opens a readable channel for a given input object (URL, input stream, <i>etc</i>).
  * The {@link #prepare prepare(…)} method analyzes the given input {@link Object} and tries to return a factory instance
  * capable to open at least one {@link ReadableByteChannel} for that input. For some kinds of input like {@link Path} or
- * {@link URL}, the {@link #reader(String)} method can be invoked an arbitrary amount of times for creating as many channels
- * as needed. But for other kinds of input like {@link InputStream}, only one channel can be returned. In such case,
- * only the first {@link #reader(String)} method invocation will succeed and all subsequent ones will throw an exception.
+ * {@link URL}, the {@link #readable readable(…)} method can be invoked an arbitrary amount of times for creating as many
+ * channels as needed. But for other kinds of input like {@link InputStream}, only one channel can be returned.
+ * In such case, only the first {@link #readable readable(…)} method invocation will succeed and all subsequent ones
+ * will throw an exception.
  *
  * <div class="section">Multi-threading</div>
  * This class is not thread-safe, except for the static {@link #prepare prepare(…)} method.
- * Callers are responsible for synchronizing their call to any member methods ({@link #reader(String)}, <i>etc</i>).
+ * Callers are responsible for synchronizing their call to any member methods ({@link #readable readable(…)}, <i>etc</i>).
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
@@ -209,7 +216,9 @@ public abstract class ChannelFactory {
                      * Unlikely to happen. But if it happens anyway, try to open the channel in a
                      * way less surprising for the user (closer to the object he has specified).
                      */
-                    return new Fallback(file, e);
+                    if (file.isFile()) {
+                        return new Fallback(file, e);
+                    }
                 }
             }
         }
@@ -221,33 +230,48 @@ public abstract class ChannelFactory {
         if (storage instanceof URL) {
             final URL file = (URL) storage;
             return new ChannelFactory() {
-                @Override public ReadableByteChannel reader(String filename) throws IOException {
+                @Override public ReadableByteChannel readable(String filename, WarningListeners<DataStore> listeners) throws IOException {
                     return Channels.newChannel(file.openStream());
                 }
-                @Override public WritableByteChannel writer(String filename) throws IOException {
+                @Override public WritableByteChannel writable(String filename, WarningListeners<DataStore> listeners) throws IOException {
                     return Channels.newChannel(file.openConnection().getOutputStream());
                 }
             };
         }
         if (storage instanceof Path) {
             final Path path = (Path) storage;
-            return new ChannelFactory() {
-                @Override public ReadableByteChannel reader(String filename) throws IOException {
-                    return Files.newByteChannel(path, optionSet);
-                }
-                @Override public WritableByteChannel writer(String filename) throws IOException {
-                    return Files.newByteChannel(path, optionSet);
-                }
-            };
+            if (Files.isRegularFile(path)) {
+                return new ChannelFactory() {
+                    @Override public ReadableByteChannel readable(String filename, WarningListeners<DataStore> listeners) throws IOException {
+                        return Files.newByteChannel(path, optionSet);
+                    }
+                    @Override public WritableByteChannel writable(String filename, WarningListeners<DataStore> listeners) throws IOException {
+                        return Files.newByteChannel(path, optionSet);
+                    }
+                };
+            }
         }
         return null;
     }
 
     /**
-     * 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.
+     * 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 {@link #reader(String)} can be invoked.
+     * @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 readable byte channel.
+     * This method returns {@code false} if this factory is capable to create only one channel
+     * and {@link #readable readable(…)} has already been invoked.
+     *
+     * @return whether {@link #readable readable(…)} or {@link #writable writable(…)} can be invoked.
      */
     public boolean canOpen() {
         return true;
@@ -258,11 +282,15 @@ public abstract class ChannelFactory {
      * it is caller's responsibility to wrap the stream in a {@link java.io.BufferedInputStream} if desired.
      *
      * @param  filename  data store name to report in case of failure.
+     * @param  listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none.
      * @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 {
-        return Channels.newInputStream(reader(filename));
+    public InputStream inputStream(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException
+    {
+        return Channels.newInputStream(readable(filename, listeners));
     }
 
     /**
@@ -270,11 +298,15 @@ public abstract class ChannelFactory {
      * it is caller's responsibility to wrap the stream in a {@link java.io.BufferedOutputStream} if desired.
      *
      * @param  filename  data store name to report in case of failure.
+     * @param  listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none.
      * @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 {
-        return Channels.newOutputStream(writer(filename));
+    public OutputStream outputStream(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException
+    {
+        return Channels.newOutputStream(writable(filename, listeners));
     }
 
     /**
@@ -283,10 +315,13 @@ public abstract class ChannelFactory {
      * this method throws an exception.
      *
      * @param  filename  data store name to report in case of failure.
+     * @param  listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none.
      * @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 readable(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException;
 
     /**
      * Returns a byte channel from the output given to the {@link #prepare prepare(…)} method.
@@ -294,10 +329,13 @@ public abstract class ChannelFactory {
      * this method throws an exception.
      *
      * @param  filename  data store name to report in case of failure.
+     * @param  listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none.
      * @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 writable(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException;
 
     /**
      * A factory that returns an existing channel <cite>as-is</cite>.
@@ -317,7 +355,15 @@ public abstract class ChannelFactory {
         }
 
         /**
-         * Returns whether {@link #reader(String)} or {@link #writer(String)} can be invoked.
+         * Returns {@code true} since use of channels or streams will affect the original storage object.
+         */
+        @Override
+        public boolean isCoupled() {
+            return true;
+        }
+
+        /**
+         * Returns whether {@link #readable readable(…)} or {@link #writable writable(…)} can be invoked.
          */
         @Override
         public boolean canOpen() {
@@ -329,13 +375,21 @@ public abstract class ChannelFactory {
          * throws an exception on all subsequent invocations.
          */
         @Override
-        public ReadableByteChannel reader(final String filename) throws IOException {
+        public ReadableByteChannel readable(final String filename, final WarningListeners<DataStore> listeners)
+                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 +397,21 @@ 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 writable(final String filename, final WarningListeners<DataStore> listeners)
+                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 +419,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 {
         /**
@@ -387,7 +449,9 @@ public abstract class ChannelFactory {
          * {@link File} to a {@link Path}. On all subsequent invocations, the file is opened silently.</p>
          */
         @Override
-        public FileInputStream inputStream(String filename) throws IOException {
+        public FileInputStream inputStream(final String filename, final WarningListeners<DataStore> listeners)
+                throws IOException
+        {
             final FileInputStream in;
             try {
                 in = new FileInputStream(file);
@@ -403,7 +467,7 @@ public abstract class ChannelFactory {
              * But the exception was nevertheless unexpected, so log its stack trace in order
              * to allow the developer to check if there is something wrong.
              */
-            warning();
+            warning("inputStream", listeners);
             return in;
         }
 
@@ -415,7 +479,9 @@ public abstract class ChannelFactory {
          * {@link File} to a {@link Path}. On all subsequent invocations, the file is opened silently.</p>
          */
         @Override
-        public FileOutputStream outputStream(String filename) throws IOException {
+        public FileOutputStream outputStream(final String filename, final WarningListeners<DataStore> listeners)
+                throws IOException
+        {
             final FileOutputStream out;
             try {
                 out = new FileOutputStream(file);
@@ -426,7 +492,7 @@ public abstract class ChannelFactory {
                 }
                 throw ioe;
             }
-            warning();
+            warning("outputStream", listeners);
             return out;
         }
 
@@ -435,10 +501,19 @@ public abstract class ChannelFactory {
          * Since the exception was nevertheless unexpected, log its stack trace in order to allow the developer
          * to check if there is something wrong.
          */
-        private void warning() {
+        private void warning(final String method, final WarningListeners<DataStore> listeners) {
             if (cause != null) {
-                Logging.unexpectedException(Logging.getLogger(Modules.STORAGE), ChannelFactory.class, "prepare", cause);
+                final LogRecord record = new LogRecord(Level.WARNING, cause.toString());
+                record.setLoggerName(Modules.STORAGE);
+                record.setSourceMethodName(method);
+                record.setSourceClassName(ChannelFactory.class.getName());
+                record.setThrown(cause);
                 cause = null;
+                if (listeners != null) {
+                    listeners.warning(record);
+                } else {
+                    Logging.getLogger(Modules.STORAGE).log(record);
+                }
             }
         }
 
@@ -446,16 +521,16 @@ public abstract class ChannelFactory {
          * Opens a new channel for the file given at construction time.
          */
         @Override
-        public ReadableByteChannel reader(String filename) throws IOException {
-            return inputStream(filename).getChannel();
+        public ReadableByteChannel readable(String filename, WarningListeners<DataStore> listeners) throws IOException {
+            return inputStream(filename, listeners).getChannel();
         }
 
         /**
          * Opens a new channel for the file given at construction time.
          */
         @Override
-        public WritableByteChannel writer(String filename) throws IOException {
-            return outputStream(filename).getChannel();
+        public WritableByteChannel writable(String filename, WarningListeners<DataStore> listeners) throws IOException {
+            return outputStream(filename, listeners).getChannel();
         }
     }
 

Modified: sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java [UTF-8] Sat Dec  9 10:57:44 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;
@@ -160,6 +161,29 @@ public final class IOUtilities extends S
     }
 
     /**
+     * Returns the given path without the directories and without the extension.
+     * For example if the given path is {@code "/Users/name/Map.png"}, then this
+     * method returns {@code "Map"}.
+     *
+     * @param  path  the path from which to get the filename without extension, or {@code null}.
+     * @return the filename without extension, or {@code null} if none.
+     */
+    public static String filenameWithoutExtension(String path) {
+        if (path != null) {
+            int s = path.lastIndexOf(File.separatorChar);
+            if (s < 0 && File.separatorChar != '/') {
+                s = path.lastIndexOf('/');
+            }
+            int e = path.lastIndexOf('.');
+            if (e <= ++s) {
+                e = path.length();
+            }
+            path = path.substring(s, e);
+        }
+        return path;
+    }
+
+    /**
      * Encodes the characters that are not legal for the {@link URI#URI(String)} constructor.
      * Note that in addition to unreserved characters ("{@code _-!.~'()*}"), the reserved
      * characters ("{@code ?/[]@}") and the punctuation characters ("{@code ,;:$&+=}")
@@ -236,7 +260,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 +552,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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -20,11 +20,22 @@ 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)
+ * <div class="section">Thread-safety</div>
+ * This class is thread-safe only if the underlying {@link ImageInputStream} is itself thread-safe.
+ * For performance reasons, this class does not synchronize the frequently invoked {@code read(…)}
+ * methods since they do nothing else than delegating to {@code ImageInputStream}. This means that
+ * if the wrapped input is {@link ChannelImageInputStream}, then this class is <strong>not</strong>
+ * thread-safe. This is not necessarily a contradiction with Java API since input streams define no
+ * explicit synchronization lock (contrarily to {@link java.io.Reader}.
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
  * @version 0.8
  *
  * @see OutputStreamAdapter
@@ -41,13 +52,34 @@ 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;
+
+    /**
+     * Temporarily set to {@code true} if a call to {@link #close()} should not be propagated to the {@link #input}.
+     *
+     * @see RewindableLineReader#rewind()
+     */
+    boolean keepOpen;
+
+    /**
      * 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 +137,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();
+    public synchronized void mark(final int readlimit) {
+        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() {
+    public synchronized 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();
+    public synchronized void reset() throws IOException {
+        if (--nestedMarks >= 0) {
+            input.reset();
+        } else {
+            input.seek(markPosition);
+        }
     }
 
     /**
@@ -149,7 +210,7 @@ public final class InputStreamAdapter ex
      * @throws IOException if an I/O error occurs.
      */
     @Override
-    public void close() throws IOException {
-        input.close();
+    public synchronized void close() throws IOException {
+        if (!keepOpen) input.close();
     }
 }

Modified: sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -17,7 +17,6 @@
 package org.apache.sis.internal.storage.io;
 
 import java.io.IOException;
-import java.nio.InvalidMarkException;
 
 
 /**
@@ -37,7 +36,10 @@ import java.nio.InvalidMarkException;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
- * @since   0.8
+ *
+ * @see Readable
+ *
+ * @since 0.8
  * @module
  */
 public interface Markable {
@@ -56,6 +58,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 +68,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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/package-info.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/package-info.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/package-info.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -24,7 +24,7 @@
  * may change in incompatible ways in any future version without notice.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.3
  * @module
  */

Modified: sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -19,28 +19,23 @@ package org.apache.sis.internal.storage.
 import java.util.List;
 import java.util.Arrays;
 import java.util.ArrayList;
-import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import java.io.Reader;
 import java.io.IOException;
 import java.text.ParsePosition;
 import java.text.ParseException;
 import org.opengis.metadata.Metadata;
-import org.opengis.util.FactoryException;
 import org.opengis.referencing.ReferenceSystem;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.internal.storage.Resources;
 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.internal.referencing.DefinitionVerifier;
+import org.apache.sis.storage.UnsupportedStorageException;
 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.GeometryLibrary;
+import org.apache.sis.setup.OptionKey;
 import org.apache.sis.util.CharSequences;
 
 
@@ -52,7 +47,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
@@ -66,6 +61,11 @@ final class Store extends DataStore {
     private Reader source;
 
     /**
+     * The geometry library, or {@code null} for the default.
+     */
+    private final GeometryLibrary library;
+
+    /**
      * The parsed objects, filled only when first needed.
      * May still be empty if the parsing failed.
      */
@@ -89,8 +89,10 @@ 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));
         }
+        library = connector.getOption(OptionKey.GEOMETRY_LIBRARY);
     }
 
     /**
@@ -127,30 +129,12 @@ final class Store extends DataStore {
              * definitions.
              */
             final ParsePosition pos = new ParsePosition(0);
-            final WKTFormat parser = new WKTFormat(null, null);
+            final StoreFormat parser = new StoreFormat(library, listeners);
             do {
                 final Object obj = parser.parse(wkt, pos);
                 objects.add(obj);
                 pos.setIndex(CharSequences.skipLeadingWhitespaces(wkt, pos.getIndex(), wkt.length()));
-                final Warnings warnings = parser.getWarnings();
-                if (warnings != null) {
-                    log(new LogRecord(Level.WARNING, warnings.toString()));
-                }
-                /*
-                 * The WKT has been parsed. Below is a verification of whether the parsed WKT is conform with
-                 * the authority definition (if an authority code has been specified). This verification is not
-                 * really necessary since we will use the WKT definition anyway even if we find discrepancies.
-                 * But non-conform WKT definitions happen so often in practice that we are better to check.
-                 */
-                if (obj instanceof CoordinateReferenceSystem) try {
-                    final DefinitionVerifier v = DefinitionVerifier.withAuthority((CoordinateReferenceSystem) obj, null, false);
-                    if (v != null) {
-                        final LogRecord warning = v.warning(false);
-                        if (warning != null) log(warning);
-                    }
-                } catch (FactoryException e) {
-                    listeners.warning(null, e);
-                }
+                parser.validate(obj);
             } while (pos.getIndex() < wkt.length());
         } catch (ParseException e) {
             throw new DataStoreContentException(getLocale(), "WKT", getDisplayName(), in).initCause(e);
@@ -188,21 +172,13 @@ final class Store extends DataStore {
                     builder.addReferenceSystem((ReferenceSystem) object);
                 }
             }
+            addTitleOrIdentifier(builder);
             metadata = builder.build(true);
         }
         return metadata;
     }
 
     /**
-     * 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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java [UTF-8] Sat Dec  9 10:57:44 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} /
@@ -61,7 +64,7 @@ import java.util.Collection;
  */
 public interface Aggregate extends Resource {
     /**
-     * Returns the children resources of this aggregate. The returned collection contains at least all
+     * Returns the children resources of this aggregate. The returned collection contains
      * the resources listed by their name in the following {@linkplain #getMetadata() metadata} elements.
      * The returned collection may contain more resources if the metadata are incomplete,
      * and the resources do not need to be in the same order:
@@ -78,8 +81,56 @@ public interface Aggregate extends Resou
      * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getCitation() citation} /
      * {@link org.apache.sis.metadata.iso.citation.DefaultCitation#getTitle() title}</blockquote>
      *
+     * <div class="section">Lazy resource instantiation</div>
+     * If the collection instantiates components only when first needed, and if a checked exception occurs
+     * during invocation of a {@link Collection} or {@link java.util.Iterator} method, then the collection
+     * or the iterator should wrap the exception in a {@link org.apache.sis.util.collection.BackingStoreException}.
+     *
      * @return all children resources that are components of this aggregate. Never {@code null}.
      * @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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java [UTF-8] Sat Dec  9 10:57:44 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,47 @@ public abstract class DataStore implemen
     }
 
     /**
+     * Creates a new instance as a child of another data store instance.
+     * The new instance inherits the parent {@linkplain #getProvider() provider}.
+     * The parent and the child share the same listeners: adding or removing a listener to a parent
+     * adds or removes the same listeners to all children, and conversely.
+     *
+     * @param  parent     the parent data store, or {@code null} if none.
+     * @param  connector  information about the storage (URL, stream, reader instance, <i>etc</i>).
+     * @throws DataStoreException if an error occurred while creating the data store for the given storage.
+     *
+     * @since 0.8
+     */
+    protected DataStore(final DataStore parent, final StorageConnector connector) throws DataStoreException {
+        ArgumentChecks.ensureNonNull("connector", connector);
+        if (parent != null) {
+            provider  = parent.provider;
+            locale    = parent.locale;
+            listeners = parent.listeners;
+        } else {
+            provider  = null;
+            locale    = Locale.getDefault(Locale.Category.DISPLAY);
+            listeners = new WarningListeners<>(this);
+        }
+        name = connector.getStorageName();
+    }
+
+    /**
+     * 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 +220,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 org.apache.sis.storage.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 +253,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 +269,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 +283,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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -16,11 +16,15 @@
  */
 package org.apache.sis.storage;
 
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.metadata.distribution.Format;
 import org.apache.sis.internal.simple.SimpleFormat;
+import org.apache.sis.internal.storage.URIDataStore;
 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 +56,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 +151,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 or accepted 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 +239,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 +255,37 @@ 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);
+        return open(URIDataStore.Provider.connector(this, parameters));
+    }
 }

Modified: sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -23,7 +23,7 @@ import java.util.Iterator;
 import java.util.ServiceLoader;
 import org.apache.sis.internal.storage.Resources;
 import org.apache.sis.internal.system.DefaultFactories;
-import org.apache.sis.internal.util.LazySet;
+import org.apache.sis.internal.referencing.LazySet;
 import org.apache.sis.util.ArgumentChecks;
 
 
@@ -241,7 +241,9 @@ search:         while (!deferred.isEmpty
             }
         }
         if (open && selected == null) {
-            throw new UnsupportedStorageException(null, Resources.Keys.UnknownFormatFor_1, connector.getStorageName());
+            @SuppressWarnings("null")
+            final String name = connector.getStorageName();
+            throw new UnsupportedStorageException(null, Resources.Keys.UnknownFormatFor_1, name);
         }
         return selected;
     }

Modified: sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java [UTF-8] Sat Dec  9 10:57:44 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;
@@ -70,6 +75,40 @@ public interface FeatureSet extends Data
     FeatureType getType() throws DataStoreException;
 
     /**
+     * Requests a subset of features and feature properties from this resource.
+     * The filtering can be applied in two domains:
+     *
+     * <ul>
+     *   <li>The returned {@code FeatureSet} may contain a smaller number of {@link Feature} instances.</li>
+     *   <li>In each {@code Feature} instance of the returned set, the number of
+     *       {@linkplain org.apache.sis.feature.DefaultFeatureType#getProperty properties} may be smaller.</li>
+     * </ul>
+     *
+     * While it is technically possible to return a <em>transformed</em> feature set (i.e. containing feature
+     * properties not found in this original {@code FeatureSet}, for example as a result of some computation),
+     * such usages should be rare. Transformations should be the topic of a separated processing package.
+     * This {@code subset(Query)} method is rather for allowing {@link DataStore} implementations to optimize
+     * the overall filtering by using the tools available with their format (for example an R-tree).
+     * {@code BoundingBox} filters are the most common case of optimization implemented by {@link DataStore}.
+     *
+     * <p>The returned subset may be a <em>view</em> of this set, i.e. changes in this {@code FeatureSet}
+     * may be reflected immediately on the returned subset (and conversely), but not necessarily.
+     * However the returned subset may not have the same capabilities as this {@link FeatureSet}.
+     * In particular, write operations may become unsupported after complex queries.</p>
+     *
+     * <p>The default implementation throws {@link UnsupportedQueryException}.</p>
+     *
+     * @param  query  definition of feature and feature properties filtering applied at reading time.
+     * @return resulting subset of features (never {@code null}).
+     * @throws UnsupportedQueryException if this {@code FeatureSet} can not execute the given query.
+     *         This includes query validation errors.
+     * @throws DataStoreException if another error occurred while processing the query.
+     */
+    default FeatureSet subset(Query query) throws UnsupportedQueryException, DataStoreException {
+        throw new UnsupportedQueryException();
+    }
+
+    /**
      * Returns a stream of all features contained in this dataset.
      * For all features, the following condition shall be true:
      *
@@ -101,4 +140,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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java [UTF-8] Sat Dec  9 10:57:44 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/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java [UTF-8] Sat Dec  9 10:57:44 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