Author: jsorel
Date: Wed Feb 14 15:18:02 2018
New Revision: 1824245

URL: http://svn.apache.org/viewvc?rev=1824245&view=rev
Log:
Storage : improve folder store to support add and remove operations if a 
specific provider is given

Added:
    
sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/proj.prj
Modified:
    
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/URIDataStore.java
    
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/StoreProvider.java
    
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java
    
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
    
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
    
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/folder/StoreTest.java

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=1824245&r1=1824244&r2=1824245&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] Wed Feb 14 15:18:02 2018
@@ -158,6 +158,16 @@ public final class Resources extends Ind
         public static final short FeatureNotFound_2 = 17;
 
         /**
+         * Restricted store provider identifier to use for resource discovery 
and creation.
+         */
+        public static final short FolderStoreProviderParameter = 40;
+
+        /**
+         * Unknown provider identifier {0}.
+         */
+        public static final short FolderStoreProviderUnknown_1 = 41;
+
+        /**
          * Whether to assemble trajectory fragments (lines in CSV file) in a 
single feature instance.
          */
         public static final short FoliationRepresentation = 38;

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=1824245&r1=1824244&r2=1824245&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] Wed Feb 14 15:18:02 2018
@@ -16,7 +16,7 @@
 #
 
 #
-# Resources in this file are for "sis-netcdf" usage only and should not be 
used by any other module.
+# Resources in this file are for "sis-storage" usage only and should not be 
used by any other module.
 # 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.
@@ -37,6 +37,8 @@ DataStoreTimeZone                 = Time
 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.
+FolderStoreProviderParameter      = Restricted store provider identifier to 
use for resource discovery and creation.
+FolderStoreProviderUnknown_1      = Unknown provider identifier {0}.
 FoliationRepresentation           = Whether to assemble trajectory fragments 
(lines in CSV file) in a single feature instance.
 ExcessiveStringSize_3             = Character string in the \u201c{0}\u201d 
file is too long. The string has {2} characters while the limit is {1}.
 IllegalFeatureType_2              = The {0} data store does not accept 
features of type \u201c{1}\u201d.

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=1824245&r1=1824244&r2=1824245&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] Wed Feb 14 15:18:02 2018
@@ -43,6 +43,8 @@ DirectoryContent_1                = Cont
 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\u2019a pas \u00e9t\u00e9 trouv\u00e9e dans les 
donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb.
+FolderStoreProviderParameter      = Identifiant du fournisseur de donn\u00e9es 
unique \u00e0 utiliser pour la d\u00e9tection et la cr\u00e9ation.
+FolderStoreProviderUnknown_1      = L'identifiant de fournisseur de 
donn\u00e9es {0} est inconnu.
 FoliationRepresentation           = Indique s\u2019il faut assembler les 
fragments de trajectoires (lignes dans un fichier CSV) dans une entit\u00e9 
unique.
 IllegalFeatureType_2              = Le format {0} ne stocke pas de 
donn\u00e9es de type \u00ab\u202f{1}\u202f\u00bb.
 IllegalInputTypeForReader_2       = Le lecteur {0} n\u2019accepte pas des 
entr\u00e9s de type \u2018{1}\u2019.

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=1824245&r1=1824244&r2=1824245&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] Wed Feb 14 15:18:02 2018
@@ -18,6 +18,9 @@ package org.apache.sis.internal.storage;
 
 import java.net.URI;
 import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
@@ -29,6 +32,7 @@ import org.apache.sis.storage.DataStoreP
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.IllegalOpenParameterException;
 import org.apache.sis.internal.storage.io.IOUtilities;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
 
 
 /**
@@ -123,6 +127,12 @@ public abstract class URIDataStore exten
         private volatile ParameterDescriptorGroup openDescriptor;
 
         /**
+         * List of main file suffixes used.
+         */
+        protected final List<String> suffix = new ArrayList<>();
+        private final List<String> unSuffix = 
Collections.unmodifiableList(suffix);
+
+        /**
          * Creates a new provider.
          */
         protected Provider() {
@@ -145,6 +155,15 @@ public abstract class URIDataStore exten
         }
 
         /**
+         * Get the list of this format mainly used file suffixes.
+         *
+         * @return list of suffix, case insensitive, never null, can be empty.
+         */
+        public final List<String> getSuffix() {
+            return unSuffix;
+        }
+
+        /**
          * Invoked by {@link #getOpenParameters()} the first time that a 
parameter descriptor needs
          * to be created.  When invoked, the parameter group name is set to a 
name derived from the
          * {@link #getShortName()} value. The default implementation creates a 
group containing only

Modified: 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/StoreProvider.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/StoreProvider.java?rev=1824245&r1=1824244&r2=1824245&view=diff
==============================================================================
--- 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/StoreProvider.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/StoreProvider.java
 [UTF-8] Wed Feb 14 15:18:02 2018
@@ -126,6 +126,7 @@ public final class StoreProvider extends
      * Creates a new provider.
      */
     public StoreProvider() {
+        suffix.add("csv");
     }
 
     /**

Modified: 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java?rev=1824245&r1=1824244&r2=1824245&view=diff
==============================================================================
--- 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java
 [UTF-8] Wed Feb 14 15:18:02 2018
@@ -39,7 +39,7 @@ import org.apache.sis.util.logging.Loggi
 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;
+import org.apache.sis.internal.storage.io.IOUtilities;
 
 
 /**
@@ -60,33 +60,43 @@ public final class FolderStoreProvider e
     private static final String NAME = "folder";
 
     /**
+     * Description of the parameter for folder location.
+     */
+    static final ParameterDescriptor<Path> LOCATION;
+
+    /**
      * Description of the parameter for formating conventions of dates and 
numbers.
      */
-    private static final ParameterDescriptor<Locale> LOCALE;
+    static final ParameterDescriptor<Locale> LOCALE;
 
     /**
      * Description of the parameter for timezone of dates in the data store.
      */
-    private static final ParameterDescriptor<TimeZone> TIMEZONE;
+    static final ParameterDescriptor<TimeZone> TIMEZONE;
 
     /**
      * Description of the parameter for character encoding used by the data 
store.
      */
-    private static final ParameterDescriptor<Charset> ENCODING;
+    static final ParameterDescriptor<Charset> ENCODING;
+
+    /**
+     * Description of the parameter for restricting searched factories.
+     */
+    static final ParameterDescriptor<String> PROVIDER;
 
     /**
      * 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);
         ENCODING   = annotate(builder, URIDataStore.Provider.ENCODING, remark);
         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);
-        location   = new 
ParameterBuilder(URIDataStore.Provider.LOCATION_PARAM).create(Path.class, null);
-        PARAMETERS = builder.addName(NAME).createGroup(location, LOCALE, 
TIMEZONE, ENCODING);
+        PROVIDER   = 
builder.addName("provider").setDescription(Resources.formatInternational(Resources.Keys.FolderStoreProviderParameter)).create(String.class,
 null);
+        LOCATION   = new 
ParameterBuilder(URIDataStore.Provider.LOCATION_PARAM).create(Path.class, null);
+        PARAMETERS = builder.addName(NAME).createGroup(LOCATION, LOCALE, 
TIMEZONE, ENCODING, PROVIDER);
     }
 
     /**
@@ -178,12 +188,17 @@ public final class FolderStoreProvider e
     @Override
     public DataStore open(final ParameterValueGroup parameters) throws 
DataStoreException {
         ArgumentChecks.ensureNonNull("parameter", parameters);
-        final StorageConnector connector = 
URIDataStore.Provider.connector(this, parameters);
-        final Parameters pg = Parameters.castOrWrap(parameters);
-        connector.setOption(OptionKey.LOCALE,   pg.getValue(LOCALE));
-        connector.setOption(OptionKey.TIMEZONE, pg.getValue(TIMEZONE));
-        connector.setOption(OptionKey.ENCODING, pg.getValue(ENCODING));
-        return open(connector);
+        final Parameters params = Parameters.castOrWrap(parameters);
+        try {
+            if (params.getValue(PROVIDER) != null) {
+                return new Store.Writable(this, params);
+            } else {
+                return new Store(this, params);
+            }
+        } catch (IOException e) {
+            throw new 
DataStoreException(Resources.format(Resources.Keys.CanNotReadDirectory_1,
+                    IOUtilities.filename(params.getValue(LOCATION))), e);
+        }
     }
 
     /**

Modified: 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java?rev=1824245&r1=1824244&r2=1824245&view=diff
==============================================================================
--- 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
 [UTF-8] Wed Feb 14 15:18:02 2018
@@ -29,8 +29,15 @@ import java.nio.file.DirectoryStream;
 import java.nio.file.DirectoryIteratorException;
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Map.Entry;
 import java.util.logging.Level;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+import java.util.stream.Stream;
+import org.apache.sis.internal.storage.FileSystemResource;
 import org.opengis.metadata.Metadata;
 import org.opengis.metadata.maintenance.ScopeCode;
 import org.opengis.parameter.ParameterValueGroup;
@@ -47,6 +54,16 @@ import org.apache.sis.util.collection.Ba
 import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.internal.storage.MetadataBuilder;
 import org.apache.sis.internal.storage.Resources;
+import org.apache.sis.internal.storage.URIDataStore;
+import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.storage.FeatureSet;
+import org.apache.sis.storage.ProbeResult;
+import org.apache.sis.storage.ReadOnlyStorageException;
+import org.apache.sis.storage.WritableAggregate;
+import org.apache.sis.storage.WritableFeatureSet;
+import org.opengis.feature.Feature;
+import org.opengis.metadata.identification.Identification;
 
 
 /**
@@ -71,32 +88,55 @@ import org.apache.sis.internal.storage.R
  * @since   0.8
  * @module
  */
-final class Store extends DataStore implements Aggregate, 
DirectoryStream.Filter<Path> {
+class Store extends DataStore implements Aggregate, 
DirectoryStream.Filter<Path> {
+
+    /**
+     * File walker to delete file and folder recursively.
+     */
+    private static final SimpleFileVisitor<Path> FILE_DELETE = new 
SimpleFileVisitor<Path>() {
+        @Override
+        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
throws IOException {
+            Files.delete(file);
+            return FileVisitResult.CONTINUE;
+        }
+
+        @Override
+        public FileVisitResult postVisitDirectory(Path dir, IOException exc) 
throws IOException {
+            Files.delete(dir);
+            return FileVisitResult.CONTINUE;
+        }
+    };
+
     /**
      * The {@link FolderStoreProvider#LOCATION} parameter value, or {@code 
null} if none.
      */
-    private final Path location;
+    protected final Path location;
 
     /**
      * Formating conventions of dates and numbers, or {@code null} if 
unspecified.
      */
-    private final Locale locale;
+    protected final Locale locale;
 
     /**
      * Timezone of dates in the data store, or {@code null} if unspecified.
      */
-    private final TimeZone timezone;
+    protected final TimeZone timezone;
 
     /**
      * Character encoding used by the data store, or {@code null} if 
unspecified.
      */
-    private final Charset encoding;
+    protected final Charset encoding;
+
+    /**
+     * Single provider to use in searches and creation operations, or {@code 
null} if unspecified.
+     */
+    protected final String providerName;
 
     /**
      * 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;
+    protected final Map<Path,DataStore> children;
 
     /**
      * Information about the data store as a whole, created when first needed.
@@ -110,7 +150,7 @@ final class Store extends DataStore impl
      *
      * @see #components()
      */
-    private transient Collection<Resource> components;
+    protected transient Collection<Resource> components;
 
     /**
      * {@code true} if {@link #sharedRepository(Path)} has already been 
invoked for {@link #location} path.
@@ -119,6 +159,11 @@ final class Store extends DataStore impl
     private transient boolean sharedRepositoryReported;
 
     /**
+     * Cached search and create provider to use.
+     */
+    private transient DataStoreProvider searchProvider;
+
+    /**
      * Creates a new folder store from the given file, path or URI.
      *
      * @param  provider   the factory that created this {@code DataStore} 
instance, or {@code null} if unspecified.
@@ -134,6 +179,18 @@ final class Store extends DataStore impl
         encoding = connector.getOption(OptionKey.ENCODING);
         children = new ConcurrentHashMap<>();
         children.put(location.toRealPath(), this);
+        providerName = null;
+    }
+
+    Store(final DataStoreProvider provider, final Parameters params) throws 
DataStoreException, IOException {
+        super(provider, new StorageConnector(params));
+        location     = params.getValue(FolderStoreProvider.LOCATION);
+        locale       = params.getValue(FolderStoreProvider.LOCALE);
+        timezone     = params.getValue(FolderStoreProvider.TIMEZONE);
+        encoding     = params.getValue(FolderStoreProvider.ENCODING);
+        providerName = params.getValue(FolderStoreProvider.PROVIDER);
+        children     = new ConcurrentHashMap<>();
+        children.put(location.toRealPath(), this);
     }
 
     /**
@@ -145,11 +202,13 @@ final class Store extends DataStore impl
      */
     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;
+        location       = connector.getStorageAs(Path.class);
+        locale         = connector.getOption(OptionKey.LOCALE);
+        timezone       = connector.getOption(OptionKey.TIMEZONE);
+        encoding       = connector.getOption(OptionKey.ENCODING);
+        children       = parent.children;
+        providerName   = parent.providerName;
+        searchProvider = parent.searchProvider;
     }
 
     /**
@@ -158,10 +217,11 @@ final class Store extends DataStore impl
     @Override
     public ParameterValueGroup getOpenParameters() {
         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);
+        pg.parameter(DataStoreProvider.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);
+        if (providerName != null) 
pg.parameter("provider").setValue(providerName);
         return pg;
     }
 
@@ -227,8 +287,20 @@ final class Store extends DataStore impl
                         connector.setOption(OptionKey.LOCALE,   locale);
                         connector.setOption(OptionKey.TIMEZONE, timezone);
                         connector.setOption(OptionKey.ENCODING, encoding);
+
+                        final DataStoreProvider provider = 
getSearchAndCreateProvider();
                         try {
-                            next = DataStores.open(connector);
+                            if (provider != null) {
+                                final ProbeResult result = 
provider.probeContent(connector);
+                                if (result.isSupported()) {
+                                    next = provider.open(connector);
+                                } else {
+                                    throw new UnsupportedStorageException();
+                                }
+                            } else {
+                                next = DataStores.open(connector);
+                            }
+
                         } catch (UnsupportedStorageException ex) {
                             if (!Files.isDirectory(candidate)) {
                                 connector.closeAllExcept(null);
@@ -273,6 +345,25 @@ final class Store extends DataStore impl
     }
 
     /**
+     *
+     * @return search and create provider, can be null
+     * @throws DataStoreException
+     */
+    protected DataStoreProvider getSearchAndCreateProvider() throws 
DataStoreException {
+        if (searchProvider == null && providerName != null) {
+            for (DataStoreProvider provider : DataStores.providers()) {
+                if (providerName.equals(provider.getShortName())) {
+                    searchProvider = provider;
+                    break;
+                }
+            }
+            if (searchProvider == null) {
+                throw new 
DataStoreException(message(Resources.Keys.FolderStoreProviderUnknown_1, 
providerName));
+            }
+        }
+        return searchProvider;
+    }
+    /**
      * Builds an error message for an error occurring while reading files in 
the directory.
      */
     private String canNotRead() {
@@ -327,4 +418,160 @@ final class Store extends DataStore impl
             }
         }
     }
+
+    /**
+     * Writable version of the store which rely on given datastore provider to 
create new types.
+     *
+     * Note 1 : this implementation is experimental.
+     * Note 2 : it has not been tested since we do not have writable feature 
sets yet.
+     */
+    static class Writable extends Store implements WritableAggregate {
+
+        public Writable(DataStoreProvider provider, Parameters params) throws 
DataStoreException, IOException {
+            super(provider, params);
+        }
+
+        /**
+         * Create a new resource.
+         * This implementation uses the provider given in store creation 
parameters.
+         *
+         * @param resource
+         * @return
+         * @throws DataStoreException
+         */
+        @Override
+        public synchronized Resource add(Resource resource) throws 
DataStoreException {
+            if (!(resource instanceof FeatureSet)) {
+                throw new DataStoreException("Only FeatureSet resources can be 
imported in this store.");
+            }
+
+            if (components().contains(resource)) {
+                throw new DataStoreException("Resource is already in this 
aggregate.");
+            }
+
+            //we know it is not null in this instance
+            final DataStoreProvider provider = getSearchAndCreateProvider();
+            if (!(provider instanceof URIDataStore.Provider)) {
+                throw new DataStoreException("Resource creation is possible 
only with URIProviders");
+            }
+
+            final URIDataStore.Provider p = (URIDataStore.Provider) provider;
+
+            //build location
+            String fileName = null;
+            for (Identification id : 
resource.getMetadata().getIdentificationInfo()) {
+                fileName = Citations.getIdentifier(id.getCitation());
+                if (fileName!=null && !fileName.isEmpty()) break;
+            }
+            if (fileName == null || fileName.isEmpty()) {
+                throw new DataStoreException("Resource does not have an 
identifier.");
+            }
+
+            //some format may have no suffix at all
+            if (!p.getSuffix().isEmpty()) {
+                fileName += "."+ p.getSuffix().get(0);
+            }
+
+            //create new store/resource
+            final Path location = this.location.resolve(fileName);
+            final StorageConnector connector = new StorageConnector(location);
+            connector.setOption(OptionKey.LOCALE,   locale);
+            connector.setOption(OptionKey.TIMEZONE, timezone);
+            connector.setOption(OptionKey.ENCODING, encoding);
+            final DataStore store = p.open(connector);
+
+            //check we can write datas
+            if (!(store instanceof WritableFeatureSet)) {
+                try {
+                    //remove any created file
+                    if (resource instanceof FileSystemResource) {
+                        //delete resource files
+                        final Path[] resourcePaths = ((FileSystemResource) 
resource).getResourcePaths();
+                        for (Path path : resourcePaths) {
+                            Files.walkFileTree(path, FILE_DELETE);
+                        }
+                    }
+                    Files.deleteIfExists(location);
+                } catch(IOException ex) {
+                    //do nothing
+                } finally {
+                    store.close();
+                }
+                throw new DataStoreException("Created resource is not a 
WritableFeatureSet.");
+            }
+
+            //copy datas between resources
+            children.put(location, store);
+            final FeatureSet source = (FeatureSet) resource;
+            final WritableFeatureSet target = (WritableFeatureSet) store;
+            target.updateType(source.getType());
+            try (Stream<Feature> stream = source.features(false)) {
+                target.add(stream.iterator());
+            }
+
+
+            //clear cache
+            components = null;
+
+            return store;
+        }
+
+        /**
+         * Note : in this implementation we clear the cache after closing the 
stores and before deleting the files.
+         * This ensure in the worse case scenario a new store will be created 
on the possible remaining files.
+         *
+         * @param resource
+         * @throws ReadOnlyStorageException
+         * @throws DataStoreException
+         */
+        @Override
+        public synchronized void remove(Resource resource) throws 
ReadOnlyStorageException, DataStoreException {
+            if (!(components().contains(resource))) {
+                throw new DataStoreException("Unknown resource, verify it is 
part of this aggregate.");
+            }
+
+            //clear cache
+            components = null;
+
+            if (resource instanceof Store) {
+                final Store store = (Store) resource;
+                store.close();
+                //clear cache
+                children.remove(store.location);
+
+                try {
+                    Files.walkFileTree(store.location, FILE_DELETE);
+                } catch (IOException ex) {
+                    throw new DataStoreException(ex.getMessage(), ex);
+                }
+            } else {
+                //resource is a datastore, we are sure of it
+                final DataStore store = (DataStore) resource;
+                store.close();
+
+                //clear cache, we need to do this loop in case the resource is
+                //not a FileSystemResource or wrongly declares the used files
+                for (Entry<Path,DataStore> entry : children.entrySet()) {
+                    if (entry.getValue() == store) {
+                        children.remove(entry.getKey());
+                        break;
+                    }
+                }
+
+                if (resource instanceof FileSystemResource) {
+                    //delete resource files
+                    final Path[] resourcePaths = ((FileSystemResource) 
resource).getResourcePaths();
+                    for (Path path : resourcePaths) {
+                        try {
+                            Files.walkFileTree(path, FILE_DELETE);
+                        } catch (IOException ex) {
+                            throw new DataStoreException(ex.getMessage(), ex);
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
 }

Modified: 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java?rev=1824245&r1=1824244&r2=1824245&view=diff
==============================================================================
--- 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java
 [UTF-8] Wed Feb 14 15:18:02 2018
@@ -80,6 +80,7 @@ public abstract class AbstractProvider e
     protected AbstractProvider(final String name, final int initialCapacity) {
         super(name);
         types = new HashMap<>(initialCapacity);
+        suffix.add("xml");
     }
 
     /**

Modified: 
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/folder/StoreTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/folder/StoreTest.java?rev=1824245&r1=1824244&r2=1824245&view=diff
==============================================================================
--- 
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/folder/StoreTest.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/folder/StoreTest.java
 [UTF-8] Wed Feb 14 15:18:02 2018
@@ -26,6 +26,7 @@ import java.util.HashSet;
 import java.util.Set;
 import org.opengis.metadata.identification.Identification;
 import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.parameter.Parameters;
 import org.apache.sis.storage.Aggregate;
 import org.apache.sis.storage.Resource;
 import org.apache.sis.storage.DataStoreException;
@@ -68,9 +69,9 @@ public final strictfp class StoreTest ex
      */
     @Test
     public void testComponents() throws URISyntaxException, 
DataStoreException, IOException {
-        final Set<String> identifiers = new HashSet<>(Arrays.asList("Sample 
1", "Sample 2", "Sample 3", "data4"));
+        final Set<String> identifiers = new HashSet<>(Arrays.asList("proj", 
"Sample 1", "Sample 2", "Sample 3", "data4"));
         try (Store store = new Store(null, new 
StorageConnector(testDirectory()))) {
-            assertEquals("Expected three data stores.", 3, 
store.components().size());
+            assertEquals("Expected three data stores.", 4, 
store.components().size());
             verifyContent(store, identifiers);
         }
         if (!identifiers.isEmpty()) {
@@ -78,6 +79,31 @@ public final strictfp class StoreTest ex
         }
     }
 
+    /**
+     * Verify that the restricting provider parameter is used.
+     *
+     * @throws URISyntaxException if the URL to test data can not be converted 
to a path of the file system.
+     * @throws DataStoreException if an error occurred while reading the 
resources.
+     * @throws IOException if an I/O error occurs.
+     */
+    @Test
+    public void testSearchProviderParameter() throws URISyntaxException, 
DataStoreException, IOException {
+
+        {
+            final Set<String> identifiers = new 
HashSet<>(Arrays.asList("Sample 1", "Sample 2", "Sample 3", "data4"));
+            final Parameters params = 
Parameters.castOrWrap(FolderStoreProvider.PARAMETERS.createValue());
+            params.parameter("location").setValue(testDirectory());
+            params.parameter("provider").setValue("XML");
+            try (Store store = new Store(null, params)) {
+                assertEquals("Expected three data stores.", 3, 
store.components().size());
+                verifyContent(store, identifiers);
+            }
+            if (!identifiers.isEmpty()) {
+                fail("Missing resources: " + identifiers);
+            }
+        }
+    }
+
     /**
      * Verifies that the given metadata contains one of the given identifiers.
      * The identifiers that are found are removed from the given set.

Added: 
sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/proj.prj
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/proj.prj?rev=1824245&view=auto
==============================================================================
--- 
sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/proj.prj
 (added)
+++ 
sis/branches/JDK8/storage/sis-storage/src/test/resources/org/apache/sis/internal/storage/folder/test-data/proj.prj
 Wed Feb 14 15:18:02 2018
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.0174532925199433]]
\ No newline at end of file


Reply via email to