sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1808720 [1/2] - in /sis/branches/JDK8: application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ core/sis-utility/src/main/java/org/apache/sis/setup/ core/sis-utility/src/main/java/org/apache/sis/util/ core/sis-utility/src/main/java...
Date Mon, 18 Sep 2017 14:13:23 GMT
Author: desruisseaux
Date: Mon Sep 18 14:13:22 2017
New Revision: 1808720

URL: http://svn.apache.org/viewvc?rev=1808720&view=rev
Log:
Update FolderStore in order to take in account symbolic links, warn about possible cyclic links, propagate encoding information (locale, timezone, charset),
handle the location as Path instead of URI, more extended exception handling. We omit the sort for now since it forces loading all resources before we can sort
(something that we may want to avoid in the future), and we omit declaration in META-INF since it would handle any folder, even if more specialized DataStore exists.

Added:
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java
      - copied, changed from r1808719, sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderProvider.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
      - copied, changed from r1808719, sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStore.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/package-info.java   (with props)
    sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/folder/StoreTest.java
      - copied, changed from r1808719, sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/folder/FolderStoreTest.java
    sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/
    sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/
    sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/
    sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/
    sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/README.txt   (with props)
    sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/data1.xml
    sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/data2.xml
    sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/data3.xml
    sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/data4/
    sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/data4/data5.xml
Removed:
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderAggregate.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderProvider.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStore.java
    sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/folder/FolderStoreTest.java
Modified:
    sis/branches/JDK8/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListeners.java
    sis/branches/JDK8/ide-project/NetBeans/build.xml
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java
    sis/branches/JDK8/storage/sis-storage/src/main/resources/META-INF/services/org.apache.sis.storage.DataStoreProvider
    sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/ChannelDataInputTest.java
    sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
    sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStore.java

Modified: sis/branches/JDK8/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java [UTF-8] (original)
+++ sis/branches/JDK8/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -18,7 +18,6 @@ package org.apache.sis.gui.dataset;
 
 import java.awt.Color;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import javafx.beans.property.SimpleObjectProperty;
@@ -36,15 +35,18 @@ import javafx.scene.image.ImageView;
 import javafx.scene.text.TextAlignment;
 import javafx.util.Callback;
 import org.apache.sis.internal.gui.FontGlyphs;
+import org.apache.sis.internal.util.Citations;
+import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.storage.Aggregate;
 import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.FeatureSet;
 import org.apache.sis.storage.Resource;
-import org.opengis.metadata.Identifier;
 import org.opengis.metadata.Metadata;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.metadata.identification.Identification;
+import org.opengis.util.InternationalString;
+
 
 /**
  * Tree viewer displaying a {@link Resource} hierarchy.
@@ -108,17 +110,44 @@ public class ResourceTree extends TreeTa
         }
     }
 
-    private static String getIdentifier(Metadata metadata) {
-        final Collection<? extends Identification> identifications = metadata.getIdentificationInfo();
-        for (Identification identification : identifications) {
-            final Citation citation = identification.getCitation();
-            if (citation != null) {
-                for (Identifier identifier : citation.getIdentifiers()) {
-                    return identifier.getCode();
+    /**
+     * Returns a label for a resource. Current implementation builds a string containing the resource title
+     * if non-ambiguous, followed by filename in order to resolve ambiguity that may be caused by different
+     * files having the same resource identification in their metadata.
+     *
+     * @param  name      the result of {@link DataStore#getDisplayName()}, or {@code null} if unknown.
+     * @param  metadata  the result of {@link DataStore#getMetadata()} (may be {@code null}).
+     */
+    private static String getTitle(final String name, final Metadata metadata) {
+        if (metadata != null) {
+            String title = null;
+            for (final Identification identification : CollectionsExt.nonNull(metadata.getIdentificationInfo())) {
+                final Citation citation = identification.getCitation();
+                if (citation != null) {
+                    final InternationalString i18n = citation.getTitle();
+                    String id;
+                    if (i18n != null) {
+                        id = i18n.toString();                   // TODO: use display locale.
+                    } else {
+                        id = Citations.getIdentifier(identification.getCitation(), false);
+                    }
+                    if (id != null && !(id = id.trim()).isEmpty()) {
+                        if (title == null) {
+                            title = id;
+                        } else if (!title.equals(id)) {
+                            return name;                        // Ambiguity - will use the filename instead.
+                        }
+                    }
+                }
+            }
+            if (title != null) {
+                if (name != null) {
+                    title += " (" + name + ')';
                 }
+                return title;
             }
         }
-        return null;
+        return name;
     }
 
 
@@ -132,6 +161,7 @@ public class ResourceTree extends TreeTa
             this.resource = res;
         }
 
+        @Override
         public ObservableList<TreeItem<Resource>> getChildren() {
             if (isFirstTimeChildren) {
                 isFirstTimeChildren = false;
@@ -169,8 +199,10 @@ public class ResourceTree extends TreeTa
             setCellValueFactory(new Callback<CellDataFeatures<Resource, String>, javafx.beans.value.ObservableValue<java.lang.String>>() {
                 @Override
                 public ObservableValue<String> call(CellDataFeatures<Resource, String> param) {
+                    final Resource res = param.getValue().getValue();
+                    final String name = (res instanceof DataStore) ? ((DataStore) res).getDisplayName() : null;
                     try {
-                        return new SimpleObjectProperty<>(getIdentifier(param.getValue().getValue().getMetadata()));
+                        return new SimpleObjectProperty<>(getTitle(name, res.getMetadata()));
                     } catch (DataStoreException ex) {
                        return new SimpleObjectProperty<>(ex.getMessage());
                     }
@@ -211,5 +243,4 @@ public class ResourceTree extends TreeTa
             setGraphic(new ImageView(getTypeIcon(resource)));
         }
     }
-
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -36,7 +36,7 @@ import org.apache.sis.internal.system.Mo
  * ({@link org.apache.sis.storage.DataStore}, <i>etc</i>).
  * {@code OptionKey}s are used for aspects that usually do not need to be configured, except in a few specialized cases.
  * For example most data file formats read by SIS do not require the user to specify the character encoding, since the
- * encoding it is often given in the file header or in the format specification. However if SIS may have to read plain
+ * encoding it is often given in the file header or in the format specification. However if SIS needs to read plain
  * text files <em>and</em> the default platform encoding is not suitable, then the user can specify the desired encoding
  * explicitely using the {@link #ENCODING} option.
  *
@@ -77,8 +77,8 @@ public class OptionKey<T> implements Ser
     /**
      * The library to use for creating geometric objects at reading time.
      * Some libraries are the Java Topology Suite (JTS), ESRI geometry API and Java2D.
-     * If this option is not specified, then a library will be selected automatically
-     * among the libraries available in the runtime environment.
+     * If this option is not specified, then a default library will be selected among
+     * the libraries available in the runtime environment.
      *
      * @since 0.8
      */

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.ArrayList;
 import java.util.Locale;
 import java.sql.SQLException;
+import java.nio.file.DirectoryIteratorException;
 import java.lang.reflect.InvocationTargetException;
 import org.opengis.util.InternationalString;
 import org.apache.sis.util.resources.Vocabulary;
@@ -211,6 +212,7 @@ public final class Exceptions extends St
      *   <li>It is an instance of {@link InvocationTargetException} (could be wrapping anything).</li>
      *   <li>It is an instance of {@link BackingStoreException} (typically wrapping a checked exception).</li>
      *   <li>It is an instance of {@link UncheckedIOException} (wrapping a {@link java.io.IOException}).</li>
+     *   <li>It is an instance of {@link DirectoryIteratorException} (wrapping a {@link java.io.IOException}).</li>
      *   <li>It is a parent type of the cause. For example some JDBC drivers wrap {@link SQLException}
      *       in other {@code SQLException} without additional information.</li>
      * </ul>
@@ -227,7 +229,8 @@ public final class Exceptions extends St
         if (exception != null) {
             while (exception instanceof InvocationTargetException ||
                    exception instanceof BackingStoreException ||
-                   exception instanceof UncheckedIOException)
+                   exception instanceof UncheckedIOException ||
+                   exception instanceof DirectoryIteratorException)
             {
                 final Throwable cause = exception.getCause();
                 if (!(cause instanceof Exception)) break;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -360,7 +360,7 @@ public final class Logging extends Stati
         }
         /*
          * Now prepare the log message. If we have been unable to figure out a source class and
-         * method name, we will fallback on JDK logging default mechanism, which may returns a
+         * method name, we will fallback on JDK logging default mechanism, which may return a
          * less relevant name than our attempt to use the logger name as the package name.
          *
          * The message is fetched using Exception.getMessage() instead than getLocalizedMessage()

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -24,7 +24,7 @@ import java.util.logging.LogRecord;
  * A log record to be logged without stack trace, unless the user specified it explicitely.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.3
+ * @version 0.8
  * @since   0.3
  * @module
  */
@@ -43,8 +43,8 @@ final class QuietLogRecord extends LogRe
     /**
      * Creates a new log record for the given message and exception.
      */
-    QuietLogRecord(final String message, final Exception exception) {
-        super(Level.WARNING, message);
+    QuietLogRecord(final Level level, final String message, final Exception exception) {
+        super(level, message);
         super.setThrown(exception);
     }
 

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListeners.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListeners.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListeners.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListeners.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -203,6 +203,24 @@ public class WarningListeners<S> impleme
      * Reports a warning represented by the given message and exception.
      * At least one of {@code message} and {@code exception} shall be non-null.
      * If both are non-null, then the exception message will be concatenated after the given message.
+     * If the exception is non-null, its stack trace will be omitted at logging time for avoiding to
+     * pollute console output (keeping in mind that this method should be invoked only for non-fatal
+     * warnings). See {@linkplain #warning(Level, String, Exception) below} for more explanation.
+     *
+     * <p>This method is a shortcut for <code>{@linkplain #warning(Level, String, Exception)
+     * warning}({@linkplain Level#WARNING}, message, exception)</code>.
+     *
+     * @param message    the message to log, or {@code null} if none.
+     * @param exception  the exception to log, or {@code null} if none.
+     */
+    public void warning(String message, Exception exception) {
+        warning(Level.WARNING, message, exception);
+    }
+
+    /**
+     * Reports a warning at the given level represented by the given message and exception.
+     * At least one of {@code message} and {@code exception} shall be non-null.
+     * If both are non-null, then the exception message will be concatenated after the given message.
      *
      * <div class="section">Stack trace omission</div>
      * If there is no registered listener, then the {@link #warning(LogRecord)} method will send the record to the
@@ -216,10 +234,12 @@ public class WarningListeners<S> impleme
      *   <li>register a listener which will log the record itself.</li>
      * </ul>
      *
+     * @param level      the warning level.
      * @param message    the message to log, or {@code null} if none.
      * @param exception  the exception to log, or {@code null} if none.
      */
-    public void warning(String message, final Exception exception) {
+    public void warning(final Level level, String message, final Exception exception) {
+        ArgumentChecks.ensureNonNull("level", level);
         final LogRecord record;
         final StackTraceElement[] trace;
         if (exception != null) {
@@ -228,11 +248,11 @@ public class WarningListeners<S> impleme
             if (message == null) {
                 message = exception.toString();
             }
-            record = new QuietLogRecord(message, exception);
+            record = new QuietLogRecord(level, message, exception);
         } else {
             ArgumentChecks.ensureNonEmpty("message", message);
             trace = Thread.currentThread().getStackTrace();
-            record = new LogRecord(Level.WARNING, message);
+            record = new LogRecord(level, message);
         }
         for (final StackTraceElement e : trace) {
             if (isPublic(e)) {

Modified: sis/branches/JDK8/ide-project/NetBeans/build.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/ide-project/NetBeans/build.xml?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/ide-project/NetBeans/build.xml (original)
+++ sis/branches/JDK8/ide-project/NetBeans/build.xml Mon Sep 18 14:13:22 2017
@@ -235,6 +235,7 @@
       </fileset>
       <fileset dir="${project.root}/storage/sis-storage/src/test/resources">
         <include name="**/*.txt"/>
+        <include name="**/*.xml"/>
       </fileset>
       <fileset dir="${project.root}/storage/sis-xmlstore/src/test/resources">
         <include name="**/*.gpx"/>

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -67,6 +67,11 @@ public final class Resources extends Ind
         public static final short AmbiguousName_4 = 15;
 
         /**
+         * Can not read “{0}” directory.
+         */
+        public static final short CanNotReadDirectory_1 = 34;
+
+        /**
          * Can not read “{1}” as a file in the {0} format.
          */
         public static final short CanNotReadFile_2 = 1;
@@ -102,6 +107,31 @@ public final class Resources extends Ind
         public static final short ConcurrentWrite_1 = 20;
 
         /**
+         * Character encoding used by the data store.
+         */
+        public static final short DataStoreEncoding = 29;
+
+        /**
+         * Formating conventions of dates and numbers.
+         */
+        public static final short DataStoreLocale = 30;
+
+        /**
+         * Data store location as a file or URL.
+         */
+        public static final short DataStoreLocation = 31;
+
+        /**
+         * Timezone of dates in the data store.
+         */
+        public static final short DataStoreTimeZone = 32;
+
+        /**
+         * Content of “{0}” directory.
+         */
+        public static final short DirectoryContent_1 = 35;
+
+        /**
          * Character string in the “{0}” file is too long. The string has {2} characters while the
          * limit is {1}.
          */
@@ -164,6 +194,11 @@ public final class Resources extends Ind
         public static final short ShallBeDeclaredBefore_2 = 22;
 
         /**
+         * The “{0}” directory is used more than once because of symbolic links.
+         */
+        public static final short SharedDirectory_1 = 36;
+
+        /**
          * Write operations are not supported.
          */
         public static final short StoreIsReadOnly = 28;
@@ -202,6 +237,11 @@ public final class Resources extends Ind
          * Format of “{0}” is not recognized.
          */
         public static final short UnknownFormatFor_1 = 14;
+
+        /**
+         * Used only if this information is not encoded with the data.
+         */
+        public static final short UsedOnlyIfNotEncoded = 33;
     }
 
     /**

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties [ISO-8859-1] Mon Sep 18 14:13:22 2017
@@ -20,6 +20,7 @@
 # For resources shared by all modules in the Apache SIS project, see "org.apache.sis.util.resources" package.
 #
 AmbiguousName_4                   = Name \u201c{3}\u201d is ambiguous because it can be understood as either \u201c{1}\u201d or \u201c{2}\u201d in the context of \u201c{0}\u201d data.
+CanNotReadDirectory_1             = Can not read \u201c{0}\u201d directory.
 CanNotReadFile_2                  = Can not read \u201c{1}\u201d as a file in the {0} format.
 CanNotReadFile_3                  = Can not read line {2} of \u201c{1}\u201d as part of a file in the {0} format.
 CanNotReadFile_4                  = Can not read line {2} (after column {3}) of \u201c{1}\u201d as part of a file in the {0} format.
@@ -27,6 +28,11 @@ ClosedReader_1                    = This
 ClosedWriter_1                    = This {0} writer is closed.
 ConcurrentRead_1                  = One or more read operations are in progress in the \u201c{0}\u201d data store.
 ConcurrentWrite_1                 = A write operation is in progress in the \u201c{0}\u201d data store.
+DataStoreEncoding                 = Character encoding used by the data store.
+DataStoreLocale                   = Formating conventions of dates and numbers.
+DataStoreLocation                 = Data store location as a file or URL.
+DataStoreTimeZone                 = Timezone of dates in the data store.
+DirectoryContent_1                = Content of \u201c{0}\u201d directory.
 FeatureAlreadyPresent_2           = A feature named \u201c{1}\u201d is already present in the \u201c{0}\u201d data store.
 FeatureNotFound_2                 = Feature \u201c{1}\u201d has not been found in the \u201c{0}\u201d data store.
 ExcessiveStringSize_3             = Character string in the \u201c{0}\u201d file is too long. The string has {2} characters while the limit is {1}.
@@ -39,6 +45,7 @@ ProcessingExecutedOn_1            = Proc
 ResourceIdentifierCollision_2     = More than one resource have the \u201c{1}\u201d identifier in the \u201c{0}\u201d data store.
 ResourceNotFound_2                = No resource found for the \u201c{1}\u201d identifier in the \u201c{0}\u201d data store.
 ShallBeDeclaredBefore_2           = The \u201c{1}\u201d element must be declared before \u201c{0}\u201d.
+SharedDirectory_1                 = The \u201c{0}\u201d directory is used more than once because of symbolic links.
 StoreIsReadOnly                   = Write operations are not supported.
 StreamIsForwardOnly_1             = Can not move backward in the \u201c{0}\u201d stream.
 StreamIsNotReadable_1             = Stream \u201c{0}\u201d is not readable.
@@ -47,3 +54,4 @@ StreamIsReadOnce_1                = The
 StreamIsWriteOnce_1               = Can not modify previously written data in \u201c{0}\u201d.
 UndefinedParameter_2              = Can not open {0} data store without \u201c{1}\u201d parameter.
 UnknownFormatFor_1                = Format of \u201c{0}\u201d is not recognized.
+UsedOnlyIfNotEncoded              = Used only if this information is not encoded with the data.

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties [ISO-8859-1] Mon Sep 18 14:13:22 2017
@@ -25,6 +25,7 @@
 #   U+00A0 NO-BREAK SPACE         before  :
 #
 AmbiguousName_4                   = Le nom \u00ab\u202f{3}\u202f\u00bb est ambigu\u00eb car il peut \u00eatre interpr\u00e9t\u00e9 aussi bien comme \u00ab\u202f{1}\u202f\u00bb ou \u00ab\u202f{2}\u202f\u00bb dans le contexte des donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb.
+CanNotReadDirectory_1             = Ne peut pas lire le r\u00e9pertoire \u00ab\u202f{0}\u202f\u00bb.
 CanNotReadFile_2                  = Ne peut pas lire \u00ab\u202f{1}\u202f\u00bb comme un fichier au format {0}.
 CanNotReadFile_3                  = Ne peut pas lire la ligne {2} de \u00ab\u202f{1}\u202f\u00bb comme une partie d\u2019un fichier au format {0}.
 CanNotReadFile_4                  = Ne peut pas lire la ligne {2} (apr\u00e8s la colonne {3}) de \u00ab\u202f{1}\u202f\u00bb comme une partie d\u2019un fichier au format {0}.
@@ -32,6 +33,11 @@ ClosedReader_1                    = Ce l
 ClosedWriter_1                    = Cet encodeur {0} est ferm\u00e9.
 ConcurrentRead_1                  = Une ou plusieurs op\u00e9rations de lecture sont en cours sur les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb.
 ConcurrentWrite_1                 = Une op\u00e9ration d\u2019\u00e9criture est en cours sur les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb.
+DataStoreEncoding                 = Encodage des caract\u00e8res utilis\u00e9 par la source de donn\u00e9es.
+DataStoreLocale                   = Conventions d\u2019\u00e9criture des dates et des nombres.
+DataStoreLocation                 = Chemin (fichier ou URL) vers la source de donn\u00e9es.
+DataStoreTimeZone                 = Fuseau horaire des dates dans les donn\u00e9es.
+DirectoryContent_1                = Contenu du r\u00e9pertoire \u00ab\u202f{0}\u202f\u00bb.
 ExcessiveStringSize_3             = La cha\u00eene de caract\u00e8res dans le fichier \u00ab\u202f{0}\u202f\u00bb est trop longue. La cha\u00eene fait {2} caract\u00e8res alors que la limite est {1}.
 FeatureAlreadyPresent_2           = Une entit\u00e9 nomm\u00e9e \u00ab\u202f{1}\u202f\u00bb est d\u00e9j\u00e0 pr\u00e9sente dans les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb.
 FeatureNotFound_2                 = L\u2019entit\u00e9 \u00ab\u202f{1}\u202f\u00bb n\u2019est pas \u00e9t\u00e9 trouv\u00e9e dans les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb.
@@ -44,6 +50,7 @@ ProcessingExecutedOn_1            = Trai
 ResourceIdentifierCollision_2     = Plusieurs ressources utilisent l\u2019identifiant \u00ab\u202f{1}\u202f\u00bb dans les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb.
 ResourceNotFound_2                = Aucune ressource n\u2019a \u00e9t\u00e9 trouv\u00e9e pour l\u2019identifiant \u00ab\u202f{1}\u202f\u00bb dans les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb.
 ShallBeDeclaredBefore_2           = L\u2019\u00e9l\u00e9ment \u00ab\u202f{1}\u202f\u00bb doit \u00eatre d\u00e9clar\u00e9 avant \u00ab\u202f{0}\u202f\u00bb.
+SharedDirectory_1                 = Le r\u00e9pertoire \u00ab\u202f{0}\u202f\u00bb est utilis\u00e9 plus d\u2019une fois \u00e0 cause des liens symboliques.
 StoreIsReadOnly                   = Les op\u00e9rations d\u2019\u00e9criture ne sont pas support\u00e9es.
 StreamIsForwardOnly_1             = Ne peut pas reculer dans le flux de donn\u00e9es \u00ab\u202f{0}\u202f\u00bb.
 StreamIsNotReadable_1             = Les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb ne sont pas accessibles en lecture.
@@ -52,3 +59,4 @@ StreamIsReadOnce_1                = Les
 StreamIsWriteOnce_1               = Ne peut pas revenir sur les donn\u00e9es d\u00e9j\u00e0 \u00e9crites dans \u00ab\u202f{0}\u202f\u00bb.
 UndefinedParameter_2              = Ne peut pas ouvrir une source de donn\u00e9es {0} sans le param\u00e8tre \u00ab\u202f{1}\u202f\u00bb.
 UnknownFormatFor_1                = Le format de \u00ab\u202f{0}\u202f\u00bb n\u2019est pas reconnu.
+UsedOnlyIfNotEncoded              = Utilis\u00e9 seulement si cette information n\u2019est pas encod\u00e9e avec les donn\u00e9es.

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -33,7 +33,7 @@ import org.apache.sis.storage.DataStoreE
  * @since   0.4
  * @module
  */
-public class StoreTypeDetector extends FileTypeDetector {
+public final class StoreTypeDetector extends FileTypeDetector {
     /**
      * Constructor for {@link java.util.ServiceLoader}.
      */

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -99,7 +99,8 @@ public abstract class URIDataStore exten
         /**
          * Description of the location parameter.
          */
-        protected static final ParameterDescriptor<URI> LOCATION_PARAM = new ParameterBuilder()
+        public static final ParameterDescriptor<URI> LOCATION_PARAM = new ParameterBuilder()
+                .setDescription(Resources.formatInternational(Resources.Keys.DataStoreLocation))
                 .addName(LOCATION)
                 .setRequired(true)
                 .create(URI.class, null);
@@ -121,7 +122,7 @@ public abstract class URIDataStore exten
          * This method creates the descriptor only when first needed. Subclasses can override the
          * {@link #build(ParameterBuilder)} method if they need to modify the descriptor to create.
          *
-         * @return description of the parameters required for opening a {@link DataStore}.
+         * @return description of the parameters required or accepted for opening a {@link DataStore}.
          */
         @Override
         public final ParameterDescriptorGroup getOpenParameters() {

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -86,7 +86,7 @@ import org.opengis.feature.AttributeType
  * @since   0.7
  * @module
  */
-public final class Store extends URIDataStore implements FeatureSet {
+final class Store extends URIDataStore implements FeatureSet {
     /**
      * The character at the beginning of lines to ignore in the header.
      * Note that this is not part of OGC Moving Feature Specification.

Copied: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java (from r1808719, sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderProvider.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java?p2=sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java&p1=sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderProvider.java&r1=1808719&r2=1808720&rev=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderProvider.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -16,68 +16,189 @@
  */
 package org.apache.sis.internal.storage.folder;
 
-import java.net.URI;
-import java.nio.file.FileSystemNotFoundException;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
-import org.apache.sis.internal.storage.URIDataStore;
+import java.nio.file.FileSystemNotFoundException;
+import org.opengis.util.InternationalString;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.ParameterNotFoundException;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.parameter.Parameters;
 import org.apache.sis.storage.DataStore;
-import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreProvider;
-import org.apache.sis.storage.ProbeResult;
+import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.StorageConnector;
-import org.opengis.parameter.ParameterDescriptorGroup;
-import org.opengis.parameter.ParameterValueGroup;
+import org.apache.sis.storage.ProbeResult;
+import org.apache.sis.storage.IllegalOpenParameterException;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.internal.system.Modules;
+import org.apache.sis.internal.storage.Resources;
+import org.apache.sis.internal.storage.URIDataStore;
+import org.apache.sis.setup.OptionKey;
+
 
 /**
- * The provider of {@link FolderStore} instances.
+ * The provider of {@link Store} instances. This provider is intentionally <strong>not</strong> registered
+ * in {@code META-INF/services/org.apache.sis.storage.DataStoreProvider} because is will open any directory,
+ * which may conflict with other providers opening only directory with some specific content.
  *
- * @author Johann Sorel (Geomatys)
+ * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
  * @since   0.8
  * @module
  */
-public class FolderProvider extends DataStoreProvider {
-
-    public static final String NAME = "folder";
+public final class FolderStoreProvider extends DataStoreProvider {
+    /**
+     * A short name or abbreviation for the data format.
+     */
+    private static final String NAME = "folder";
+
+    /**
+     * Description of the parameter for formating conventions of dates and numbers.
+     */
+    private static final ParameterDescriptor<Locale> LOCALE;
+
+    /**
+     * Description of the parameter for timezone of dates in the data store.
+     */
+    private static final ParameterDescriptor<TimeZone> TIMEZONE;
+
+    /**
+     * Description of the parameter for character encoding used by the data store.
+     */
+    private static final ParameterDescriptor<Charset> ENCODING;
+
+    /**
+     * The group of parameter descriptors to be returned by {@link #getOpenParameters()}.
+     */
+    static final ParameterDescriptorGroup PARAMETERS;
+    static {
+        final ParameterDescriptor<Path> location;
+        final ParameterBuilder builder = new ParameterBuilder();
+        final InternationalString remark = Resources.formatInternational(Resources.Keys.UsedOnlyIfNotEncoded);
+        LOCALE     = builder.addName("locale"  ).setDescription(Resources.formatInternational(Resources.Keys.DataStoreLocale  )).setRemarks(remark).create(Locale.class,   null);
+        TIMEZONE   = builder.addName("timezone").setDescription(Resources.formatInternational(Resources.Keys.DataStoreTimeZone)).setRemarks(remark).create(TimeZone.class, null);
+        ENCODING   = builder.addName("encoding").setDescription(Resources.formatInternational(Resources.Keys.DataStoreEncoding)).setRemarks(remark).create(Charset.class,  null);
+        location   = builder.addName( LOCATION ).setDescription(URIDataStore.Provider.LOCATION_PARAM.getDescription())          .setRequired(true) .create(Path.class,     null);
+        PARAMETERS = builder.addName( NAME     ).createGroup(location, LOCALE, TIMEZONE, ENCODING);
+    }
 
-    public static final ParameterDescriptorGroup PARAMETERS_DESCRIPTOR = URIDataStore.Provider.descriptor(NAME);
+    /**
+     * The unique instance of this provider.
+     *
+     * @see #open(Path)
+     */
+    public static final FolderStoreProvider INSTANCE = new FolderStoreProvider();
+
+    /**
+     * Creates a new provider.
+     */
+    private FolderStoreProvider() {
+    }
 
+    /**
+     * Returns a short name or abbreviation for the data format.
+     *
+     * @return a short name or abbreviation for the data format.
+     */
     @Override
     public String getShortName() {
         return NAME;
     }
 
+    /**
+     * Returns a description of all parameters accepted by this provider for opening a data store.
+     *
+     * @return description of the parameters for opening a {@link DataStore}.
+     */
     @Override
     public ParameterDescriptorGroup getOpenParameters() {
-        return PARAMETERS_DESCRIPTOR;
+        return PARAMETERS;
     }
 
+    /**
+     * Returns {@link ProbeResult#SUPPORTED} if the given storage appears to be a folder.
+     * Returning {@code SUPPORTED} from this method does not guarantee that reading or writing will succeed,
+     * only that there appears to be a reasonable chance of success based on a brief inspection of the storage
+     * header.
+     *
+     * @return {@link ProbeResult#SUPPORTED} if the given storage seems to be readable as a folder.
+     * @throws DataStoreException if an I/O or SQL error occurred.
+     */
     @Override
     public ProbeResult probeContent(StorageConnector connector) throws DataStoreException {
-        final URI uri = connector.getStorageAs(URI.class);
         try {
-            //check path is valid
-            final Path path = Paths.get(uri);
-            if (Files.isDirectory(path)) {
-                return new ProbeResult(true, null, null);
+            final Path path = connector.getStorageAs(Path.class);
+            if (path != null && Files.isDirectory(path)) {
+                return ProbeResult.SUPPORTED;
             }
-        } catch (FileSystemNotFoundException ex) {
-            //nothing we can do, may happen a lot
+        } catch (FileSystemNotFoundException e) {
+            Logging.recoverableException(Logging.getLogger(Modules.STORAGE), FolderStoreProvider.class, "probeContent", e);
+            // Nothing we can do, may happen often.
         }
-       return new ProbeResult(false, null, null);
+        return ProbeResult.UNSUPPORTED_STORAGE;
     }
 
+    /**
+     * Returns a data store implementation associated with this provider.
+     *
+     * @param  connector  information about the storage (URL, path, <i>etc</i>).
+     * @return a data store implementation associated with this provider for the given storage.
+     * @throws DataStoreException if an error occurred while creating the data store instance.
+     */
     @Override
-    public DataStore open(StorageConnector connector) throws DataStoreException {
-        final URI uri = connector.getStorageAs(URI.class);
-        return new FolderStore(uri);
+    public DataStore open(final StorageConnector connector) throws DataStoreException {
+        try {
+            return new Store(this, connector);
+        } catch (IOException e) {
+            throw new DataStoreException(Resources.format(Resources.Keys.CanNotReadDirectory_1,
+                    connector.getStorageName()), e);
+        }
     }
 
+    /**
+     * Returns a data store implementation associated with this provider for the given parameters.
+     *
+     * @return a folder data store implementation for the given parameters.
+     * @throws DataStoreException if an error occurred while creating the data store instance.
+     */
     @Override
-    public DataStore open(ParameterValueGroup parameters) throws DataStoreException {
-        return new FolderStore(parameters);
+    public DataStore open(final ParameterValueGroup parameters) throws DataStoreException {
+        ArgumentChecks.ensureNonNull("parameter", parameters);
+        ParameterNotFoundException cause = null;
+        try {
+            final Object location = parameters.parameter(LOCATION).getValue();
+            if (location != null) {
+                final Parameters pg = Parameters.castOrWrap(parameters);
+                final StorageConnector connector = new StorageConnector(location);
+                connector.setOption(OptionKey.LOCALE,   pg.getValue(LOCALE));
+                connector.setOption(OptionKey.TIMEZONE, pg.getValue(TIMEZONE));
+                connector.setOption(OptionKey.ENCODING, pg.getValue(ENCODING));
+                return open(connector);
+            }
+        } catch (ParameterNotFoundException e) {
+            cause = e;
+        }
+        throw new IllegalOpenParameterException(Resources.format(Resources.Keys.UndefinedParameter_2,
+                getShortName(), LOCATION), cause);
     }
 
+    /**
+     * Returns a folder data store for the given path.
+     *
+     * @param  path  the directory for which to create a data store.
+     * @return a data store for the given directory.
+     * @throws DataStoreException if an error occurred while creating the data store instance.
+     */
+    public static DataStore open(final Path path) throws DataStoreException {
+        return INSTANCE.open(new StorageConnector(path));
+    }
 }

Copied: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java (from r1808719, sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStore.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java?p2=sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java&p1=sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStore.java&r1=1808719&r2=1808720&rev=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStore.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -16,132 +16,315 @@
  */
 package org.apache.sis.internal.storage.folder;
 
-import java.net.URI;
-import java.nio.file.Paths;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
 import java.util.Collection;
-import org.apache.sis.parameter.Parameters;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.nio.charset.Charset;
+import java.nio.file.Path;
+import java.nio.file.Files;
+import java.nio.file.DirectoryStream;
+import java.nio.file.DirectoryIteratorException;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.logging.Level;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opengis.metadata.Metadata;
+import org.opengis.metadata.maintenance.ScopeCode;
+import org.opengis.parameter.ParameterValueGroup;
+import org.apache.sis.setup.OptionKey;
+import org.apache.sis.storage.Resource;
 import org.apache.sis.storage.Aggregate;
 import org.apache.sis.storage.DataStore;
-import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.DataStoreProvider;
 import org.apache.sis.storage.DataStores;
-import org.apache.sis.storage.ReadOnlyStorageException;
-import org.apache.sis.storage.Resource;
-import org.opengis.metadata.Metadata;
-import org.opengis.parameter.ParameterValueGroup;
+import org.apache.sis.storage.DataStoreProvider;
+import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.UnsupportedStorageException;
+import org.apache.sis.util.collection.BackingStoreException;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
+import org.apache.sis.internal.storage.MetadataBuilder;
+import org.apache.sis.internal.storage.Resources;
+
 
 /**
- * A Folder store acts as an aggregate of multiple files in a single store.
- * each file will be tested and possibly opened by another store.
+ * A folder store acts as an aggregate of multiple files in a single store.
+ * Only visible files are considered; all hidden files are excluded.
+ * Each visible file will be tested and eventually opened by another store.
  * This approach allows to discover the content of a folder or archive without
  * testing each file one by one.
  *
- * @author Johann Sorel (Geomatys)
+ * <p><b>Limitations:</b></p>
+ * <ul>
+ *   <li>Current version is read-only.</li>
+ *   <li>Current version does not watch for external modifications in directory content.</li>
+ *   <li>Current version open all files in the directory and keep those files open.
+ *       If the directory is large, it will be a problem.</li>
+ *   <li>We could open data stores concurrently. This is not yet done.</li>
+ * </ul>
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
  * @since   0.8
  * @module
  */
-public class FolderStore extends DataStore implements Aggregate {
+final class Store extends DataStore implements Aggregate, DirectoryStream.Filter<Path> {
+    /**
+     * The {@link FolderStoreProvider#LOCATION} parameter value, or {@code null} if none.
+     */
+    private final Path location;
+
+    /**
+     * Formating conventions of dates and numbers, or {@code null} if unspecified.
+     */
+    private final Locale locale;
+
+    /**
+     * Timezone of dates in the data store, or {@code null} if unspecified.
+     */
+    private final TimeZone timezone;
+
+    /**
+     * Character encoding used by the data store, or {@code null} if unspecified.
+     */
+    private final Charset encoding;
 
-    private final ParameterValueGroup params;
-    private final FolderAggregate root;
+    /**
+     * All data stores (including sub-folders) found in the directory structure, including the root directory.
+     * This is used for avoiding never-ending loop with symbolic links.
+     */
+    private final Map<Path,DataStore> children;
 
     /**
-     * Construct a new FolderStore from an URI.
+     * Information about the data store as a whole, created when first needed.
      *
-     * @param uri root folder path
+     * @see #getMetadata()
      */
-    public FolderStore(URI uri) {
-        this(toParameters(uri));
-    }
+    private transient Metadata metadata;
 
     /**
-     * Construct a new FolderStore from parameters.
+     * Resources in the folder given at construction time, created when first needed.
      *
-     * @param params opening parameters, those must comply to the provider parameter
-     *        definition {@link FolderProvider#PARAMETERS_DESCRIPTOR}
+     * @see #components()
      */
-    public FolderStore(ParameterValueGroup params) {
-        this.params = params;
-        final URI uri = URI.class.cast(params.parameter(DataStoreProvider.LOCATION).getValue());
-        root = new FolderAggregate(listeners, null, Paths.get(uri));
-    }
+    private transient Collection<Resource> components;
 
-    private static ParameterValueGroup toParameters(final URI uri) {
-        final Parameters params = Parameters.castOrWrap(FolderProvider.PARAMETERS_DESCRIPTOR.createValue());
-        params.parameter(DataStoreProvider.LOCATION).setValue(uri);
-        return params;
-    }
+    /**
+     * {@code true} if {@link #sharedRepository(Path)} has already been invoked for {@link #location} path.
+     * This is used for avoiding to report the same message many times.
+     */
+    private transient boolean sharedRepositoryReported;
 
     /**
-     * {@inheritDoc }
+     * Creates a new folder store from the given file, path or URI.
      *
-     * @return the factory that created this {@code DataStore} instance
+     * @param  provider   the factory that created this {@code DataStore} instance, or {@code null} if unspecified.
+     * @param  connector  information about the storage (URL, stream, <i>etc</i>).
+     * @throws DataStoreException if an error occurred while opening the stream.
      */
-    @Override
-    public DataStoreProvider getProvider() {
-        return DataStores.providers().stream().filter(
-                (DataStoreProvider t) -> t.getShortName().equals(FolderProvider.NAME)).findFirst().get();
+    @SuppressWarnings("ThisEscapedInObjectConstruction")    // Okay because 'folders' does not escape.
+    Store(final DataStoreProvider provider, final StorageConnector connector) throws DataStoreException, IOException {
+        super(provider, connector);
+        location = connector.getStorageAs(Path.class);
+        locale   = connector.getOption(OptionKey.LOCALE);
+        timezone = connector.getOption(OptionKey.TIMEZONE);
+        encoding = connector.getOption(OptionKey.ENCODING);
+        children = new ConcurrentHashMap<>();
+        children.put(location.toRealPath(), this);
     }
 
     /**
-     * {@inheritDoc }
+     * Creates a new sub-folder store as a child of the given folder store.
      *
-     * @return parameters used for opening this {@code DataStore}, not null.
+     * @param  parent     the parent folder store.
+     * @param  connector  information about the storage (URL, stream, <i>etc</i>).
+     * @throws DataStoreException if an error occurred while opening the stream.
+     */
+    private Store(final Store parent, final StorageConnector connector) throws DataStoreException {
+        super(parent, connector);
+        location = connector.getStorageAs(Path.class);
+        locale   = connector.getOption(OptionKey.LOCALE);
+        timezone = connector.getOption(OptionKey.TIMEZONE);
+        encoding = connector.getOption(OptionKey.ENCODING);
+        children = parent.children;
+    }
+
+    /**
+     * Returns the parameters used to open this data store.
      */
     @Override
     public ParameterValueGroup getOpenParameters() {
-        return params;
+        final ParameterValueGroup pg = (provider != null ? provider.getOpenParameters() : FolderStoreProvider.PARAMETERS).createValue();
+        pg.parameter(FolderStoreProvider.LOCATION).setValue(location);
+        if (locale   != null) pg.parameter("locale"  ).setValue(locale  );
+        if (timezone != null) pg.parameter("timezone").setValue(timezone);
+        if (encoding != null) pg.parameter("encoding").setValue(encoding);
+        return pg;
     }
 
     /**
-     * @see FolderAggregate#getMetadata()
-     *
-     * @throws org.apache.sis.storage.DataStoreException
+     * Invoked during iteration for omitting hidden files.
      */
     @Override
-    public Metadata getMetadata() throws DataStoreException {
-        return root.getMetadata();
+    public boolean accept(final Path entry) throws IOException {
+        return !Files.isHidden(entry);
     }
 
     /**
-     * @see FolderAggregate#components()
+     * Returns information about the data store as a whole.
+     * Those metadata contains the directory name in the resource title.
      *
-     * @throws org.apache.sis.storage.DataStoreException
+     * @return information about resources in the data store.
      */
     @Override
-    public Collection<Resource> components() throws DataStoreException {
-        return root.components();
+    public synchronized Metadata getMetadata() {
+        if (metadata == null) {
+            final MetadataBuilder mb = new MetadataBuilder();
+            final String name = getDisplayName();
+            mb.addResourceScope(ScopeCode.COLLECTION, Resources.formatInternational(Resources.Keys.DirectoryContent_1, name));
+            mb.addLanguage(locale,   MetadataBuilder.Scope.RESOURCE);
+            mb.addEncoding(encoding, MetadataBuilder.Scope.RESOURCE);
+            mb.addTitleOrIdentifier(name, MetadataBuilder.Scope.ALL);
+            metadata = mb.build(true);
+        }
+        return metadata;
     }
 
     /**
-     * @see FolderAggregate#add(org.apache.sis.storage.Resource)
-     *
-     * @throws org.apache.sis.storage.DataStoreException
+     * Returns all resources found in the folder given at construction time.
+     * Only the resources recognized by a {@link DataStore} will be included.
+     * This includes sub-folders. Resources are in no particular order.
      */
     @Override
-    public org.apache.sis.storage.Resource add(org.apache.sis.storage.Resource resource) throws DataStoreException, ReadOnlyStorageException {
-        return root.add(resource);
+    @SuppressWarnings("ReturnOfCollectionOrArrayField")
+    public synchronized Collection<Resource> components() throws DataStoreException {
+        if (components == null) {
+            try (DirectoryStream<Path> stream = Files.newDirectoryStream(location, this)) {
+                final List<DataStore> resources = new ArrayList<>();
+                for (final Path candidate : stream) {
+                    /*
+                     * The candidate path may be a symbolic link to a file that we have previously read.
+                     * In such case, use the existing data store.   A use case is a directory containing
+                     * hundred of GeoTIFF files all accompanied by ".prj" files having identical content.
+                     * (Note: those ".prj" files should be invisible since they should be identified as
+                     * GeoTIFF auxiliary files, but current Store implementation does not know that).
+                     */
+                    final Path real = candidate.toRealPath();
+                    DataStore next = children.get(real);
+                    if (next instanceof Store) {
+                        ((Store) next).sharedRepository(real);          // Warn about directories only.
+                    }
+                    if (next == null) {
+                        /*
+                         * The candidate file has never been read before. Try to read it now.
+                         * If the file format is unknown (UnsupportedStorageException), we will
+                         * check if we can open it as a child folder store before to skip it.
+                         */
+                        final StorageConnector connector = new StorageConnector(candidate);
+                        connector.setOption(OptionKey.LOCALE,   locale);
+                        connector.setOption(OptionKey.TIMEZONE, timezone);
+                        connector.setOption(OptionKey.ENCODING, encoding);
+                        try {
+                            next = DataStores.open(connector);
+                        } catch (UnsupportedStorageException ex) {
+                            if (!Files.isDirectory(candidate)) {
+                                connector.closeAllExcept(null);
+                                listeners.warning(Level.FINE, null, ex);
+                                continue;
+                            }
+                            next = new Store(this, connector);
+                        } catch (DataStoreException ex) {
+                            try {
+                                connector.closeAllExcept(null);
+                            } catch (DataStoreException s) {
+                                ex.addSuppressed(s);
+                            }
+                            throw ex;
+                        }
+                        /*
+                         * At this point we got the data store. It could happen that a store for
+                         * the same file has been added concurrently, so we need to check again.
+                         */
+                        final DataStore existing = children.putIfAbsent(real, next);
+                        if (existing != null) {
+                            next.close();
+                            next = existing;
+                            if (next instanceof Store) {
+                                ((Store) next).sharedRepository(real);      // Warn about directories only.
+                            }
+                        }
+                    }
+                    resources.add(next);
+                }
+                components = UnmodifiableArrayList.wrap(resources.toArray(new Resource[resources.size()]));
+            } catch (DirectoryIteratorException | UncheckedIOException ex) {
+                // The cause is an IOException (no other type allowed).
+                throw new DataStoreException(canNotRead(), ex.getCause());
+            } catch (IOException ex) {
+                throw new DataStoreException(canNotRead(), ex);
+            } catch (BackingStoreException ex) {
+                throw ex.unwrapOrRethrow(DataStoreException.class);
+            }
+        }
+        return components;              // Safe because unmodifiable list.
     }
 
     /**
-     * @see FolderAggregate#remove(org.apache.sis.storage.Resource)
-     *
-     * @throws org.apache.sis.storage.DataStoreException
+     * Builds an error message for an error occurring while reading files in the directory.
      */
-    @Override
-    public void remove(org.apache.sis.storage.Resource resource) throws DataStoreException, ReadOnlyStorageException {
-        root.remove(resource);
+    private String canNotRead() {
+        return message(Resources.Keys.CanNotReadDirectory_1, getDisplayName());
+    }
+
+    /**
+     * Logs a warning about a file that could be read, but happen to be a directory that we have read previously.
+     * We could add the existing {@link Aggregate} instance in the parent {@code Aggregate} that we are building,
+     * but doing so may create a cycle. Current version logs a warning instead because users may not be prepared
+     * to handle cycles. Not that we have no guarantee that a cycle really exists at this stage, only that it may
+     * exist.
+     */
+    private void sharedRepository(final Path candidate) {
+        if (!sharedRepositoryReported) {
+            sharedRepositoryReported = true;
+            listeners.warning(message(Resources.Keys.SharedDirectory_1, candidate), null);
+        }
     }
 
     /**
-     * @see FolderAggregate#close()
+     * Returns a localized string for the given key and value.
      *
-     * @throws org.apache.sis.storage.DataStoreException
+     * @param  key  one of the {@link Resources.Keys} constants ending with {@code _1} suffix.
      */
-    @Override
-    public void close() throws DataStoreException {
-        root.close();
+    private String message(final short key, final Object value) {
+        return Resources.forLocale(getLocale()).getString(key, value);
     }
 
+    /**
+     * Closes all children resources.
+     */
+    @Override
+    public synchronized void close() throws DataStoreException {
+        final Collection<Resource> resources = components;
+        if (resources != null) {
+            components = null;                                      // Clear first in case of failure.
+            DataStoreException failure = null;
+            for (final Resource r : resources) {
+                if (r instanceof DataStore) try {
+                    ((DataStore) r).close();
+                } catch (DataStoreException ex) {
+                    if (failure == null) {
+                        failure = ex;
+                    } else {
+                        failure.addSuppressed(ex);
+                    }
+                }
+            }
+            if (failure != null) {
+                throw failure;
+            }
+        }
+    }
 }

Added: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/package-info.java?rev=1808720&view=auto
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/package-info.java (added)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/package-info.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * {@link org.apache.sis.storage.DataStore} implementation for a folder containing an arbitrary amount of data files.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @version 0.8
+ * @since   0.8
+ * @module
+ */
+package org.apache.sis.internal.storage.folder;

Propchange: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/package-info.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/package-info.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java [UTF-8] Mon Sep 18 14:13:22 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,8 +45,10 @@ 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;
 
@@ -53,13 +57,14 @@ import org.apache.sis.storage.ForwardOnl
  * 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 #reader reader(…)} 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 reader(…)} 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 #reader reader(…)}, <i>etc</i>).
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
@@ -211,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);
+                    }
                 }
             }
         }
@@ -223,24 +230,26 @@ 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 reader(String filename, WarningListeners<DataStore> listeners) throws IOException {
                     return Channels.newChannel(file.openStream());
                 }
-                @Override public WritableByteChannel writer(String filename) throws IOException {
+                @Override public WritableByteChannel writer(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 reader(String filename, WarningListeners<DataStore> listeners) throws IOException {
+                        return Files.newByteChannel(path, optionSet);
+                    }
+                    @Override public WritableByteChannel writer(String filename, WarningListeners<DataStore> listeners) throws IOException {
+                        return Files.newByteChannel(path, optionSet);
+                    }
+                };
+            }
         }
         return null;
     }
@@ -259,9 +268,9 @@ public abstract class ChannelFactory {
 
     /**
      * 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.
+     * if this factory is capable to create only one channel and {@link #reader reader(…)} has already been invoked.
      *
-     * @return whether {@link #reader(String)} can be invoked.
+     * @return whether {@link #reader reader(…)} or {@link #writer writer(…)} can be invoked.
      */
     public boolean canOpen() {
         return true;
@@ -272,12 +281,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 DataStoreException, IOException {
-        return Channels.newInputStream(reader(filename));
+    public InputStream inputStream(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException
+    {
+        return Channels.newInputStream(reader(filename, listeners));
     }
 
     /**
@@ -285,12 +297,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 DataStoreException, IOException {
-        return Channels.newOutputStream(writer(filename));
+    public OutputStream outputStream(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException
+    {
+        return Channels.newOutputStream(writer(filename, listeners));
     }
 
     /**
@@ -299,11 +314,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 DataStoreException, IOException;
+    public abstract ReadableByteChannel reader(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException;
 
     /**
      * Returns a byte channel from the output given to the {@link #prepare prepare(…)} method.
@@ -311,11 +328,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 DataStoreException, IOException;
+    public abstract WritableByteChannel writer(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException;
 
     /**
      * A factory that returns an existing channel <cite>as-is</cite>.
@@ -343,7 +362,7 @@ public abstract class ChannelFactory {
         }
 
         /**
-         * Returns whether {@link #reader(String)} or {@link #writer(String)} can be invoked.
+         * Returns whether {@link #reader reader(…)} or {@link #writer writer(…)} can be invoked.
          */
         @Override
         public boolean canOpen() {
@@ -355,7 +374,9 @@ public abstract class ChannelFactory {
          * throws an exception on all subsequent invocations.
          */
         @Override
-        public ReadableByteChannel reader(final String filename) throws DataStoreException, IOException {
+        public ReadableByteChannel reader(final String filename, final WarningListeners<DataStore> listeners)
+                throws DataStoreException, IOException
+        {
             final Channel in = channel;
             if (in instanceof ReadableByteChannel) {
                 channel = null;
@@ -375,7 +396,9 @@ public abstract class ChannelFactory {
          * throws an exception on all subsequent invocations.
          */
         @Override
-        public WritableByteChannel writer(final String filename) throws DataStoreException, IOException {
+        public WritableByteChannel writer(final String filename, final WarningListeners<DataStore> listeners)
+                throws DataStoreException, IOException
+        {
             final Channel out = channel;
             if (out instanceof WritableByteChannel) {
                 channel = null;
@@ -425,7 +448,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);
@@ -441,7 +466,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;
         }
 
@@ -453,7 +478,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);
@@ -464,7 +491,7 @@ public abstract class ChannelFactory {
                 }
                 throw ioe;
             }
-            warning();
+            warning("outputStream", listeners);
             return out;
         }
 
@@ -473,10 +500,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);
+                }
             }
         }
 
@@ -484,16 +520,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 reader(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 writer(String filename, WarningListeners<DataStore> listeners) throws IOException {
+            return outputStream(filename, listeners).getChannel();
         }
     }
 

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -81,6 +81,11 @@ 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.
      */

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -122,6 +122,32 @@ 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.

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -173,7 +173,7 @@ public abstract class DataStoreProvider
      *   <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}.
+     * @return description of the parameters required or accepted for opening a {@link DataStore}.
      *
      * @see #open(ParameterValueGroup)
      * @see DataStore#getOpenParameters()
@@ -299,7 +299,6 @@ public abstract class DataStoreProvider
             throw new IllegalOpenParameterException(Resources.format(Resources.Keys.UndefinedParameter_2,
                     getShortName(), LOCATION), cause);
         }
-        final StorageConnector connector = new StorageConnector(location);
-        return open(connector);
+        return open(new StorageConnector(location));
     }
 }

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java?rev=1808720&r1=1808719&r2=1808720&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java [UTF-8] Mon Sep 18 14:13:22 2017
@@ -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;
     }



Mime
View raw message