sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1813747 [12/13] - in /sis/branches/JDK9: ./ application/ application/sis-console/ application/sis-console/src/main/artifact/ application/sis-console/src/main/artifact/lib/ application/sis-console/src/main/artifact/lib/darwin/ application/s...
Date Mon, 30 Oct 2017 10:25:10 GMT
Modified: sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -25,6 +25,7 @@ import org.apache.sis.internal.netcdf.De
 import org.apache.sis.internal.netcdf.DecoderTest;
 import org.apache.sis.internal.storage.io.ChannelDataInput;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.setup.GeometryLibrary;
 import org.apache.sis.test.DependsOn;
 
 import static org.junit.Assert.*;
@@ -60,9 +61,9 @@ public final strictfp class ChannelDecod
      *
      * <ul>
      *   <li>{@link #THREDDS} for a NcML file.</li>
-     *   <li>{@link #NCEP}    for a NetCDF binary file.</li>
-     *   <li>{@link #CIP}     for a NetCDF binary file.</li>
-     *   <li>{@link #LANDSAT} for a NetCDF binary file.</li>
+     *   <li>{@link #NCEP}    for a netCDF binary file.</li>
+     *   <li>{@link #CIP}     for a netCDF binary file.</li>
+     *   <li>{@link #LANDSAT} for a netCDF binary file.</li>
      * </ul>
      *
      * @param  name  the file name as one of the above-cited constants.
@@ -75,12 +76,12 @@ public final strictfp class ChannelDecod
         assertNotNull(name, in);
         final ChannelDataInput input = new ChannelDataInput(name,
                 Channels.newChannel(in), ByteBuffer.allocate(4096), false);
-        return new ChannelDecoder(LISTENERS, input);
+        return new ChannelDecoder(input, null, GeometryLibrary.JAVA2D, LISTENERS);
     }
 
     /**
      * Unconditionally returns {@code false} since {@link ChannelDecoder}
-     * supports only the classic and 64 bits NetCDF formats.
+     * supports only the classic and 64 bits netCDF formats.
      *
      * @return {@code false}.
      */

Modified: sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfoTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfoTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfoTest.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfoTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -51,7 +51,7 @@ public final strictfp class GridGeometry
 
     /**
      * Unconditionally returns {@code false} since {@link ChannelDecoder}
-     * supports only the classic and 64 bits NetCDF formats.
+     * supports only the classic and 64 bits netCDF formats.
      *
      * @return {@code false}.
      */

Modified: sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/VariableInfoTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/VariableInfoTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/VariableInfoTest.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/VariableInfoTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -52,7 +52,7 @@ public final strictfp class VariableInfo
 
     /**
      * Unconditionally returns {@code false} since {@link ChannelDecoder}
-     * supports only the classic and 64 bits NetCDF formats.
+     * supports only the classic and 64 bits netCDF formats.
      *
      * @return {@code false}.
      */

Modified: sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -31,6 +31,7 @@ import org.apache.sis.internal.netcdf.De
 import org.apache.sis.internal.netcdf.ucar.DecoderWrapper;
 import org.apache.sis.internal.netcdf.TestCase;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.setup.GeometryLibrary;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestUtilities;
 import org.junit.Test;
@@ -43,7 +44,7 @@ import static org.junit.Assert.*;
  * The tests are overridden in order to add some additional assertions for attributes not parsed by
  * the GeoAPI demo code.
  *
- * <p>This tests uses the UCAR implementation for reading NetCDF attributes.
+ * <p>This tests uses the UCAR implementation for reading netCDF attributes.
  * For a test using the SIS embedded implementation, see {@link MetadataReaderTest}.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
@@ -54,7 +55,7 @@ import static org.junit.Assert.*;
 @DependsOn(MetadataReaderTest.class)
 public final strictfp class ConformanceTest extends NetcdfMetadataTest {
     /**
-     * Reads a metadata object from the given NetCDF file.
+     * Reads a metadata object from the given netCDF file.
      * This method is invoked by the tests inherited from the {@code geoapi-test} module.
      *
      * <div class="note"><b>Note:</b>
@@ -62,13 +63,13 @@ public final strictfp class ConformanceT
      * {@code NetcdfFile.findAttribute(String)} method calls. However in SIS implementation, the metadata
      * object is fully created right at this method invocation time.</div>
      *
-     * @param  file the NetCDF file to wrap.
+     * @param  file  the netCDF file to wrap.
      * @return a metadata implementation created from the attributes found in the given file.
-     * @throws IOException if an error occurred while reading the given NetCDF file.
+     * @throws IOException if an error occurred while reading the given netCDF file.
      */
     @Override
     protected Metadata wrap(final NetcdfFile file) throws IOException {
-        final Decoder decoder = new DecoderWrapper(TestCase.LISTENERS, file);
+        final Decoder decoder = new DecoderWrapper(file, GeometryLibrary.JAVA2D, TestCase.LISTENERS);
         final MetadataReader ncISO = new MetadataReader(decoder);
         try {
             return ncISO.read();
@@ -139,7 +140,7 @@ public final strictfp class ConformanceT
     }
 
     /**
-     * Tests a NetCDF binary file. This method inherits the tests defined in GeoAPI,
+     * Tests a netCDF binary file. This method inherits the tests defined in GeoAPI,
      * and adds some additional tests for attributes parsed by SIS but not GeoAPI.
      *
      * @throws IOException if the test file can not be read.
@@ -175,7 +176,10 @@ public final strictfp class ConformanceT
     @Test
     @Override
     public void testLandsat() throws IOException {
-        addCommonProperties(expectedProperties, false);
+        final Map<String,Object> expected = expectedProperties;
+        addCommonProperties(expected, false);
+        assertNull(expected.put("identificationInfo.citation.title", "Landsat-GDAL"));
+        assertNull(expected.put("metadataIdentifier.code", "Landsat-GDAL"));
         super.testLandsat();
         assertSame("metadataScope", ScopeCode.DATASET, getSingleton(metadata.getMetadataScopes()).getResourceScope());
 
@@ -193,6 +197,8 @@ public final strictfp class ConformanceT
     public void testCIP() throws IOException {
         final Map<String,Object> expected = expectedProperties;
         addCommonProperties(expected, true);
+        assertNull(expected.put("identificationInfo.citation.title", "CIP"));
+        assertNull(expected.put("metadataIdentifier.code", "CIP"));
         super.testCIP();
         assertSame("metadataScope", ScopeCode.DATASET, getSingleton(metadata.getMetadataScopes()).getResourceScope());
         /*

Modified: sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -26,6 +26,7 @@ import org.apache.sis.internal.netcdf.uc
 import org.apache.sis.internal.netcdf.impl.ChannelDecoderTest;
 import org.apache.sis.metadata.iso.DefaultMetadata;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.setup.GeometryLibrary;
 import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 
@@ -35,7 +36,7 @@ import static org.apache.sis.test.TestUt
 
 /**
  * Tests {@link MetadataReader}. This tests uses the SIS embedded implementation and the UCAR library
- * for reading NetCDF attributes.
+ * for reading netCDF attributes.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
@@ -57,7 +58,7 @@ public final strictfp class MetadataRead
     }
 
     /**
-     * Reads the metadata using the NetCDF decoder embedded with SIS,
+     * Reads the metadata using the netCDF decoder embedded with SIS,
      * and compares its string representation with the expected one.
      *
      * @throws IOException if an I/O error occurred while opening the file.
@@ -82,7 +83,7 @@ public final strictfp class MetadataRead
     @Test
     public void testUCAR() throws IOException, DataStoreException {
         final Metadata metadata;
-        try (Decoder input = new DecoderWrapper(TestCase.LISTENERS, new NetcdfDataset(open(NCEP)))) {
+        try (Decoder input = new DecoderWrapper(new NetcdfDataset(open(NCEP)), GeometryLibrary.JAVA2D, TestCase.LISTENERS)) {
             metadata = new MetadataReader(input).read();
         }
         compareToExpected(metadata);

Modified: sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreProviderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreProviderTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreProviderTest.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreProviderTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -48,7 +48,7 @@ import static org.opengis.test.Assert.*;
 public final strictfp class NetcdfStoreProviderTest extends IOTestCase {
     /**
      * Tests {@link NetcdfStoreProvider#probeContent(StorageConnector)} for an input stream which shall
-     * be recognized as a classic NetCDF file.
+     * be recognized as a classic netCDF file.
      *
      * @throws DataStoreException if a logical error occurred.
      */
@@ -66,7 +66,7 @@ public final strictfp class NetcdfStoreP
     /**
      * Tests {@link NetcdfStoreProvider#probeContent(StorageConnector)} for a UCAR {@link NetcdfFile} object.
      *
-     * @throws IOException if an error occurred while opening the NetCDF file.
+     * @throws IOException if an error occurred while opening the netCDF file.
      * @throws DataStoreException if a logical error occurred.
      */
     @Test
@@ -83,9 +83,9 @@ public final strictfp class NetcdfStoreP
 
     /**
      * Tests {@link NetcdfStoreProvider#decoder(WarningListeners, StorageConnector)} for an input stream which
-     * shall be recognized as a classic NetCDF file. The provider shall instantiate a {@link ChannelDecoder}.
+     * shall be recognized as a classic netCDF file. The provider shall instantiate a {@link ChannelDecoder}.
      *
-     * @throws IOException if an error occurred while opening the NetCDF file.
+     * @throws IOException if an error occurred while opening the netCDF file.
      * @throws DataStoreException if a logical error occurred.
      */
     @Test
@@ -100,7 +100,7 @@ public final strictfp class NetcdfStoreP
      * Tests {@link NetcdfStoreProvider#decoder(WarningListeners, StorageConnector)} for a UCAR
      * {@link NetcdfFile} object. The provider shall instantiate a {@link DecoderWrapper}.
      *
-     * @throws IOException if an error occurred while opening the NetCDF file.
+     * @throws IOException if an error occurred while opening the netCDF file.
      * @throws DataStoreException if a logical error occurred.
      */
     @Test

Modified: sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreTest.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreTest.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreTest.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -41,10 +41,10 @@ import static org.opengis.test.Assert.*;
 })
 public final strictfp class NetcdfStoreTest extends IOTestCase {
     /**
-     * Returns a new NetCDF store to test.
+     * Returns a new netCDF store to test.
      *
      * @param  dataset the name of the datastore to load.
-     * @throws DataStoreException if an error occurred while reading the NetCDF file.
+     * @throws DataStoreException if an error occurred while reading the netCDF file.
      */
     private static NetcdfStore create(final String dataset) throws DataStoreException {
         return new NetcdfStore(null, new StorageConnector(IOTestCase.class.getResource(dataset)));
@@ -53,7 +53,7 @@ public final strictfp class NetcdfStoreT
     /**
      * Tests {@link NetcdfStore#getMetadata()}.
      *
-     * @throws DataStoreException if an error occurred while reading the NetCDF file.
+     * @throws DataStoreException if an error occurred while reading the netCDF file.
      */
     @Test
     public void testGetMetadata() throws DataStoreException {
@@ -68,7 +68,7 @@ public final strictfp class NetcdfStoreT
     /**
      * Tests {@link NetcdfStore#getConventionVersion()}.
      *
-     * @throws DataStoreException if an error occurred while reading the NetCDF file.
+     * @throws DataStoreException if an error occurred while reading the netCDF file.
      */
     @Test
     public void testGetConventionVersion() throws DataStoreException {

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -29,14 +29,13 @@ import org.apache.sis.util.logging.Warni
  * @since   0.8
  * @module
  */
-public abstract class AbstractFeatureSet extends AbstractDataSet implements FeatureSet {
+public abstract class AbstractFeatureSet extends AbstractResource implements FeatureSet {
     /**
      * Creates a new resource.
      *
-     * @param store      the data store which contains this resource.
      * @param listeners  the set of registered warning listeners for the data store.
      */
-    protected AbstractFeatureSet(final DataStore store, final WarningListeners<DataStore> listeners) {
-        super(store, listeners);
+    protected AbstractFeatureSet(final WarningListeners<DataStore> listeners) {
+        super(listeners);
     }
 }

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -16,9 +16,19 @@
  */
 package org.apache.sis.internal.storage;
 
+import java.util.Locale;
+import org.opengis.geometry.Envelope;
+import org.opengis.metadata.Metadata;
+import org.opengis.metadata.extent.Extent;
+import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.opengis.metadata.extent.GeographicExtent;
+import org.opengis.metadata.identification.Identification;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.util.Localized;
+import org.apache.sis.util.logging.WarningListeners;
 import org.apache.sis.storage.Resource;
 import org.apache.sis.storage.DataStore;
-import org.apache.sis.util.logging.WarningListeners;
+import org.apache.sis.storage.DataStoreException;
 
 
 /**
@@ -29,37 +39,86 @@ import org.apache.sis.util.logging.Warni
  * @since   0.8
  * @module
  */
-public abstract class AbstractResource implements Resource {
-    /**
-     * The data store which contains this resource.
-     */
-    protected final DataStore store;
-
+public abstract class AbstractResource implements Resource, Localized {
     /**
      * The set of registered warning listeners for the data store.
-     *
-     * @todo Remove this field if we move {@code AbstractResource} to the {@code org.apache.sis.storage} package,
-     *       since we would be able to access {@code DataStore.listeners} directly.
      */
-    private final WarningListeners<DataStore> listeners;
+    protected final WarningListeners<DataStore> listeners;
 
     /**
      * Creates a new resource.
      *
-     * @param store      the data store which contains this resource.
      * @param listeners  the set of registered warning listeners for the data store.
      */
-    protected AbstractResource(final DataStore store, final WarningListeners<DataStore> listeners) {
-        this.store     = store;
+    protected AbstractResource(final WarningListeners<DataStore> listeners) {
         this.listeners = listeners;
     }
 
     /**
-     * The set of registered warning listeners for the data store.
+     * Returns the locale for error messages or warnings.
+     * Returns {@code null} if no locale is explicitly defined.
+     *
+     * @return the locale, or {@code null} if not explicitly defined.
+     */
+    @Override
+    public final Locale getLocale() {
+        return (listeners != null) ? listeners.getLocale() : null;
+    }
+
+    /**
+     * Returns the display name of the data store, or {@code null} if none.
+     * This is a convenience method for formatting error messages in subclasses.
+     *
+     * @return the data store display name, or {@code null}.
+     *
+     * @see DataStore#getDisplayName()
+     */
+    protected final String getStoreName() {
+        return (listeners != null) ? listeners.getSource().getDisplayName() : null;
+    }
+
+    /**
+     * Returns the spatio-temporal envelope of this resource.
+     * The default implementation computes the union of all {@link GeographicBoundingBox} in the resource metadata,
+     * assuming the {@linkplain org.apache.sis.referencing.CommonCRS#defaultGeographic() default geographic CRS}
+     * (usually WGS 84).
+     *
+     * @return the spatio-temporal resource extent, or {@code null} if none.
+     * @throws DataStoreException if an error occurred while reading or computing the envelope.
+     */
+    public Envelope getEnvelope() throws DataStoreException {
+        return envelope(getMetadata());
+    }
+
+    /**
+     * Implementation of {@link #getEnvelope()}, provided as a separated method for
+     * {@link org.apache.sis.storage.DataSet} implementations that do not extend {@code AbstractResource}.
      *
-     * @return the registered warning listeners for the data store.
+     * @param  metadata  the metadata from which to compute the envelope, or {@code null}.
+     * @return the spatio-temporal resource extent, or {@code null} if none.
      */
-    protected final WarningListeners<DataStore> listeners() {
-        return listeners;
+    public static Envelope envelope(final Metadata metadata) {
+        GeneralEnvelope bounds = null;
+        if (metadata != null) {
+            for (final Identification identification : metadata.getIdentificationInfo()) {
+                if (identification != null) {                                               // Paranoiac check.
+                    for (final Extent extent : identification.getExtents()) {
+                        if (extent != null) {                                               // Paranoiac check.
+                            for (final GeographicExtent ge : extent.getGeographicElements()) {
+                                if (ge instanceof GeographicBoundingBox) {
+                                    final GeneralEnvelope env = new GeneralEnvelope((GeographicBoundingBox) ge);
+                                    if (bounds == null) {
+                                        bounds = env;
+                                    } else {
+                                        bounds.add(env);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return bounds;
     }
 }

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -24,16 +24,19 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.net.URI;
 import java.nio.charset.Charset;
 import javax.measure.Unit;
 import org.opengis.util.MemberName;
 import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
+import org.opengis.geometry.Geometry;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.citation.Role;
 import org.opengis.metadata.citation.DateType;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.metadata.citation.CitationDate;
+import org.opengis.metadata.citation.OnLineFunction;
 import org.opengis.metadata.spatial.GCP;
 import org.opengis.metadata.spatial.Dimension;
 import org.opengis.metadata.spatial.DimensionNameType;
@@ -42,6 +45,7 @@ import org.opengis.metadata.spatial.Pixe
 import org.opengis.metadata.spatial.GeolocationInformation;
 import org.opengis.metadata.spatial.SpatialRepresentationType;
 import org.opengis.metadata.constraint.Restriction;
+import org.opengis.metadata.content.CoverageContentType;
 import org.opengis.metadata.content.TransferFunctionType;
 import org.opengis.metadata.maintenance.ScopeCode;
 import org.opengis.metadata.acquisition.Context;
@@ -62,6 +66,7 @@ import org.apache.sis.metadata.iso.Defau
 import org.apache.sis.metadata.iso.extent.DefaultExtent;
 import org.apache.sis.metadata.iso.extent.DefaultVerticalExtent;
 import org.apache.sis.metadata.iso.extent.DefaultTemporalExtent;
+import org.apache.sis.metadata.iso.extent.DefaultBoundingPolygon;
 import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
 import org.apache.sis.metadata.iso.extent.DefaultGeographicDescription;
 import org.apache.sis.metadata.iso.spatial.DefaultGridSpatialRepresentation;
@@ -83,6 +88,7 @@ import org.apache.sis.metadata.iso.citat
 import org.apache.sis.metadata.iso.citation.DefaultResponsibility;
 import org.apache.sis.metadata.iso.citation.DefaultIndividual;
 import org.apache.sis.metadata.iso.citation.DefaultOrganisation;
+import org.apache.sis.metadata.iso.citation.DefaultOnlineResource;
 import org.apache.sis.metadata.iso.constraint.DefaultLegalConstraints;
 import org.apache.sis.metadata.iso.identification.DefaultKeywords;
 import org.apache.sis.metadata.iso.identification.DefaultResolution;
@@ -875,6 +881,7 @@ public class MetadataBuilder {
      * @param  scope      whether the date applies to data, to metadata or to both.
      *
      * @see #addTitle(CharSequence)
+     * @see #addTitleOrIdentifier(String, Scope)
      */
     public final void addIdentifier(final CharSequence authority, String code, final Scope scope) {
         ArgumentChecks.ensureNonNull("scope", scope);
@@ -1072,7 +1079,7 @@ public class MetadataBuilder {
      * Storage location is:
      *
      * <ul>
-     *   <li>{@code metadata/identificationInfo/citation/title} if available</li>
+     *   <li>{@code metadata/identificationInfo/citation/title} if not yet used</li>
      *   <li>{@code metadata/identificationInfo/citation/alternateTitle} otherwise</li>
      * </ul>
      *
@@ -1094,12 +1101,56 @@ public class MetadataBuilder {
     }
 
     /**
+     * Adds the given code as a title if the current citation has no title, or as an identifier otherwise.
+     * This method is invoked when adding an identifier to a metadata that may have no title. Because the
+     * title is mandatory, adding only an identifier would make an invalid metadata.
+     *
+     * @param  code   the identifier code, or {@code null} for no-operation.
+     * @param  scope  whether the date applies to data, to metadata or to both.
+     *
+     * @see #addTitle(CharSequence)
+     * @see #addIdentifier(CharSequence, String, Scope)
+     */
+    public final void addTitleOrIdentifier(final String code, Scope scope) {
+        ArgumentChecks.ensureNonNull("scope", scope);
+        if (scope != Scope.METADATA) {
+            if (citation == null || citation.getTitle() == null) {
+                addTitle(code);
+                if (scope == Scope.RESOURCE) {
+                    return;
+                }
+                scope = Scope.METADATA;
+            }
+        }
+        addIdentifier(null, code, scope);
+    }
+
+    /**
+     * Adds a version of the resource.
+     * If a version already exists, the new one will be appended after a new line.
+     * Storage location is:
+     *
+     * <ul>
+     *   <li>{@code metadata/identificationInfo/citation/edition}</li>
+     * </ul>
+     *
+     * @param version  the version of resource(s), or {@code null} for no-operation.
+     */
+    public final void addEdition(final CharSequence version) {
+        final InternationalString i18n = trim(version);
+        if (i18n != null) {
+            final DefaultCitation citation = citation();
+            citation.setEdition(append(citation.getEdition(), i18n));
+        }
+    }
+
+    /**
      * Adds a brief narrative summary of the resource(s).
-     * If a summary already existed, the new one will be appended after a new line.
+     * If a summary already exists, the new one will be appended after a new line.
      * Storage location is:
      *
      * <ul>
-     *   <li>{@code metadata/identificationInfo/abstract}
+     *   <li>{@code metadata/identificationInfo/abstract}</li>
      * </ul>
      *
      * @param description  the summary of resource(s), or {@code null} for no-operation.
@@ -1117,7 +1168,7 @@ public class MetadataBuilder {
 
     /**
      * Adds a summary of the intentions with which the resource(s) was developed.
-     * If a purpose already existed, the new one will be appended after a new line.
+     * If a purpose already exists, the new one will be appended after a new line.
      * Storage location is:
      *
      * <ul>
@@ -1155,7 +1206,7 @@ public class MetadataBuilder {
 
     /**
      * Adds any other descriptive information about the resource.
-     * If information already existed, the new one will be appended after a new line.
+     * If information already exists, the new one will be appended after a new line.
      * Storage location is:
      *
      * <ul>
@@ -1612,6 +1663,22 @@ parse:      for (int i = 0; i < length;)
     }
 
     /**
+     * Adds the given geometry as a bounding polygon.
+     * Storage locations is:
+     *
+     * <ul>
+     *   <li>{@code metadata/identificationInfo/extent/geographicElement/polygon}</li>
+     * </ul>
+     *
+     * @param  bounds  the bounding polygon, or {@code null} if none.
+     */
+    public final void addBoundingPolygon(final Geometry bounds) {
+        if (bounds != null) {
+            addIfNotPresent(extent().getGeographicElements(), new DefaultBoundingPolygon(bounds));
+        }
+    }
+
+    /**
      * Adds a geographic extent described by an identifier. The given identifier is stored as-is as
      * the natural language description, and possibly in a modified form as the geographic identifier.
      * See {@link DefaultGeographicDescription#DefaultGeographicDescription(CharSequence)} for details.
@@ -2000,6 +2067,22 @@ parse:      for (int i = 0; i < length;)
     }
 
     /**
+     * Adds type of information represented in the cell.
+     * Storage location is:
+     *
+     * <ul>
+     *   <li>{@code metadata/contentInfo/attributeGroup/contentType}</li>
+     * </ul>
+     *
+     * @param  type  type of information represented in the cell, or {@code null} for no-operation.
+     */
+    public final void addContentType(final CoverageContentType type) {
+        if (type != null) {
+            attributeGroup().getContentTypes().add(type);
+        }
+    }
+
+    /**
      * Sets the name or number that uniquely identifies instances of bands of wavelengths on which a sensor operates.
      * If a coverage contains more than one band, additional bands can be created by calling
      * {@link #newSampleDimension()} before to call this method.
@@ -2059,7 +2142,7 @@ parse:      for (int i = 0; i < length;)
      * @param  authority  identifies which controlled list of name is used, or {@code null} if none.
      * @param  name       the band name, or {@code null} for no-operation.
      */
-    public final void addBandIdentifier(final CharSequence authority, String name) {
+    public final void addBandName(final CharSequence authority, String name) {
         if (name != null && !(name = name.trim()).isEmpty()) {
             addIfNotPresent(sampleDimension().getNames(), sharedIdentifier(authority, name));
         }
@@ -2372,6 +2455,26 @@ parse:      for (int i = 0; i < length;)
     }
 
     /**
+     * Adds the identifier of the operation used to acquire the dataset.
+     * Examples: "GHRSST", "NOAA CDR", "NASA EOS", "JPSS", "GOES-R".
+     * Storage location is:
+     *
+     * <ul>
+     *   <li>{@code metadata/acquisitionInformation/operation/identifier}</li>
+     * </ul>
+     *
+     * @param  program     identification of the mission, or {@code null} if none.
+     * @param  identifier  unique identification of the operation, or {@code null} for no-operation.
+     */
+    public final void addAcquisitionOperation(final CharSequence program, String identifier) {
+        if (identifier != null && !(identifier = identifier.trim()).isEmpty()) {
+            final DefaultOperation r = new DefaultOperation();
+            r.setIdentifier(sharedIdentifier(program, identifier));
+            addIfNotPresent(acquisition().getOperations(), r);
+        }
+    }
+
+    /**
      * Adds the identifier of the requirement to be satisfied by data acquisition.
      * Storage location is:
      *
@@ -2500,6 +2603,25 @@ parse:      for (int i = 0; i < length;)
         }
     }
 
+    /**
+     * Adds a URL to a more complete description of the metadata.
+     *
+     * <ul>
+     *   <li>{@code metadata/metadataLinkage/linkage}
+     *     with {@code function} set to {@link OnLineFunction#COMPLETE_METADATA}</li>
+     * </ul>
+     *
+     * @param  link
+     */
+    public final void addCompleteMetadata(final URI link) {
+        if (link != null) {
+            final DefaultOnlineResource ln = new DefaultOnlineResource(link);
+            ln.setFunction(OnLineFunction.COMPLETE_METADATA);
+            ln.setProtocol(link.getScheme());
+            addIfNotPresent(metadata().getMetadataLinkages(), ln);
+        }
+    }
+
     /**
      * Returns the metadata (optionally as an unmodifiable object), or {@code null} if none.
      * If {@code freeze} is {@code true}, then the returned metadata instance can not be modified.

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -67,6 +67,16 @@ public final class Resources extends Ind
         public static final short AmbiguousName_4 = 15;
 
         /**
+         * Can not read the Coordinate Reference System (CRS) Well Known Text (WKT) in “{0}”.
+         */
+        public static final short CanNotReadCRS_WKT_1 = 37;
+
+        /**
+         * 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 +112,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 +199,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 +242,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/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties [ISO-8859-1] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties [ISO-8859-1] Mon Oct 30 10:25:08 2017
@@ -20,6 +20,8 @@
 # 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.
+CanNotReadCRS_WKT_1               = Can not read the Coordinate Reference System (CRS) Well Known Text (WKT) in \u201c{0}\u201d.
+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 +29,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 +46,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 +55,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/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties [ISO-8859-1] Mon Oct 30 10:25:08 2017
@@ -25,6 +25,8 @@
 #   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.
+CanNotReadCRS_WKT_1               = Ne peut pas lire le syst\u00e8me de r\u00e9f\u00e9rence spatial dans le \u00ab\u202fWell Known Text\u202f\u00bb (WKT) 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 +34,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 +51,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 +60,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/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java [UTF-8] Mon Oct 30 10:25:08 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/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -25,6 +25,7 @@ import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStoreProvider;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.internal.storage.io.IOUtilities;
 
 
 /**
@@ -98,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);
@@ -120,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() {
@@ -160,4 +162,23 @@ public abstract class URIDataStore exten
             return new ParameterBuilder().addName(name).createGroup(LOCATION_PARAM);
         }
     }
+
+    /**
+     * Adds the filename (without extension) as the citation title if there is no title, or as the identifier otherwise.
+     * This method should be invoked last, after {@code DataStore} implementation did its best effort for adding a title.
+     * The intend is actually to provide an identifier, but since the title is mandatory in ISO 19115 metadata, providing
+     * only an identifier without title would be invalid.
+     *
+     * @param  builder  where to add the title or identifier.
+     */
+    protected final void addTitleOrIdentifier(final MetadataBuilder builder) {
+        if (location != null) {
+            /*
+             * The getDisplayName() contract does not allow us to use it as an identifier. However current implementation
+             * in super.getDisplayName() returns the filename provided that the input was a URI, URL, File or Path. Since
+             * all those types are convertibles to URI, we can use (location != null) as a criterion.
+             */
+            builder.addTitleOrIdentifier(IOUtilities.filenameWithoutExtension(super.getDisplayName()), MetadataBuilder.Scope.ALL);
+        }
+    }
 }

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -46,6 +46,7 @@ import org.apache.sis.internal.referenci
 import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.internal.storage.MetadataBuilder;
 import org.apache.sis.internal.storage.io.IOUtilities;
+import org.apache.sis.internal.storage.io.RewindableLineReader;
 import org.apache.sis.internal.feature.Geometries;
 import org.apache.sis.internal.feature.MovingFeature;
 import org.apache.sis.internal.storage.Resources;
@@ -64,6 +65,7 @@ import org.apache.sis.setup.OptionKey;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.io.InvalidSeekException;
 import org.apache.sis.measure.Units;
 
 // Branch-dependent imports
@@ -86,7 +88,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.
@@ -238,7 +240,7 @@ public final class Store extends URIData
             throw new UnsupportedStorageException(super.getLocale(), StoreProvider.NAME,
                     connector.getStorage(), connector.getOption(OptionKey.OPEN_OPTIONS));
         }
-        source = (r instanceof BufferedReader) ? (BufferedReader) r : new LineNumberReader(r);
+        source     = (r instanceof BufferedReader) ? (BufferedReader) r : new LineNumberReader(r);
         geometries = Geometries.implementation(connector.getOption(OptionKey.GEOMETRY_LIBRARY));
         dissociate = immediate;
         GeneralEnvelope envelope    = null;
@@ -246,7 +248,7 @@ public final class Store extends URIData
         Foliation       foliation   = null;
         try {
             final List<String> elements = new ArrayList<>();
-            source.mark(1024);
+            source.mark(RewindableLineReader.BUFFER_SIZE);
             String line;
             while ((line = source.readLine()) != null) {
                 line = line.trim();
@@ -292,9 +294,9 @@ public final class Store extends URIData
                     }
                 }
                 elements.clear();
-                source.mark(1024);
+                source.mark(RewindableLineReader.BUFFER_SIZE);
             }
-            source.reset();
+            source.reset();                 // Restore position to the first line after the header.
         } catch (IOException e) {
             throw new DataStoreException(getLocale(), StoreProvider.NAME, super.getDisplayName(), source).initCause(e);
         } catch (FactoryException e) {
@@ -310,6 +312,33 @@ public final class Store extends URIData
     }
 
     /**
+     * Moves the reader position to beginning of file, if possible. We try to use the mark defined by the constructor,
+     * which is set after the last header line. If the mark is no longer valid, then we have to create a new line reader.
+     * In this later case, we have to skip the header lines (i.e. we reproduce the constructor loop, but without parsing
+     * metadata).
+     */
+    private void rewind() throws IOException {
+        final BufferedReader reader = source;
+        if (!(reader instanceof RewindableLineReader)) {
+            throw new InvalidSeekException(Resources.forLocale(getLocale())
+                    .getString(Resources.Keys.StreamIsForwardOnly_1, getDisplayName()));
+        }
+        source = ((RewindableLineReader) reader).rewind();
+        if (source != reader) {
+            String line;
+            while ((line = source.readLine()) != null) {
+                line = line.trim();
+                if (!line.isEmpty()) {
+                    final char c = line.charAt(0);
+                    if (c != COMMENT && c != METADATA) break;
+                }
+                source.mark(RewindableLineReader.BUFFER_SIZE);
+            }
+            source.reset();         // Restore position to the first line after the header.
+        }
+    }
+
+    /**
      * Parses the envelope described by the header line starting with {@code @stboundedby}.
      * The envelope returned by this method will be stored in the {@link #envelope} field.
      *
@@ -618,6 +647,7 @@ public final class Store extends URIData
                 listeners.warning(null, e);
             }
             builder.addFeatureType(featureType, null);
+            addTitleOrIdentifier(builder);
             metadata = builder.build(true);
         }
         return metadata;
@@ -656,7 +686,7 @@ public final class Store extends URIData
      * @return a stream over all features in the CSV file.
      * @throws DataStoreException if an error occurred while creating the feature stream.
      *
-     * @todo Needs to reset the position when doing another pass on the features.
+     * @todo Need to reset the position when doing another pass on the features. See {@link #rewind()}.
      * @todo If sequential order, publish Feature as soon as identifier changed.
      */
     @Override

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelData.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelData.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelData.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelData.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -49,7 +49,8 @@ public abstract class ChannelData implem
     private static final int BIT_OFFSET_SIZE = 3;
 
     /**
-     * A file identifier used only for formatting error message.
+     * A short identifier (typically a filename without path) used for formatting error message.
+     * This is often the value given by {@link org.apache.sis.storage.StorageConnector#getStorageName()}.
      */
     public final String filename;
 
@@ -114,7 +115,7 @@ public abstract class ChannelData implem
      * Creates a new instance for the given channel and using the given buffer.
      * The channel is not stored by this class - it shall be stored by the subclass.
      *
-     * @param  filename  a file identifier used only for formatting error message.
+     * @param  filename  a short identifier (typically a filename without path) used for formatting error message.
      * @param  channel   the channel from where data are read or where to wrote.
      * @param  buffer    the buffer where to store the data.
      * @throws IOException if an error occurred while reading the channel.

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -26,6 +26,7 @@ import java.nio.IntBuffer;
 import java.nio.LongBuffer;
 import java.nio.FloatBuffer;
 import java.nio.DoubleBuffer;
+import java.nio.charset.Charset;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.SeekableByteChannel;
 import org.apache.sis.internal.storage.Resources;
@@ -76,7 +77,7 @@ public class ChannelDataInput extends Ch
      * If the buffer already contains some data, then the {@code filled} argument shall be {@code true}.
      * Otherwise (e.g. if it is a newly created buffer), then {@code filled} shall be {@code false}.
      *
-     * @param  filename  a file identifier used only for formatting error message.
+     * @param  filename  a short identifier (typically a filename without path) used for formatting error message.
      * @param  channel   the channel from where data are read.
      * @param  buffer    the buffer where to copy the data.
      * @param  filled    {@code true} if the buffer already contains data, or {@code false} if it needs
@@ -822,7 +823,7 @@ public class ChannelDataInput extends Ch
      * @return the string decoded from the {@code length} next bytes.
      * @throws IOException if an error occurred while reading the bytes, or if the given encoding is invalid.
      */
-    public final String readString(final int length, final String encoding) throws IOException {
+    public final String readString(final int length, final Charset encoding) throws IOException {
         if (buffer.hasArray() && length <= buffer.capacity()) {
             ensureBufferContains(length);
             final int position = buffer.position(); // Must be after 'ensureBufferContains(int)'.

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java [UTF-8] Mon Oct 30 10:25:08 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 #readable readable(…)} method can be invoked an arbitrary amount of times for creating as many
+ * channels as needed. But for other kinds of input like {@link InputStream}, only one channel can be returned.
+ * In such case, only the first {@link #readable readable(…)} method invocation will succeed and all subsequent ones
+ * will throw an exception.
  *
  * <div class="section">Multi-threading</div>
  * This class is not thread-safe, except for the static {@link #prepare prepare(…)} method.
- * Callers are responsible for synchronizing their call to any member methods ({@link #reader(String)}, <i>etc</i>).
+ * Callers are responsible for synchronizing their call to any member methods ({@link #readable readable(…)}, <i>etc</i>).
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
@@ -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 readable(String filename, WarningListeners<DataStore> listeners) throws IOException {
                     return Channels.newChannel(file.openStream());
                 }
-                @Override public WritableByteChannel writer(String filename) throws IOException {
+                @Override public WritableByteChannel writable(String filename, WarningListeners<DataStore> listeners) throws IOException {
                     return Channels.newChannel(file.openConnection().getOutputStream());
                 }
             };
         }
         if (storage instanceof Path) {
             final Path path = (Path) storage;
-            return new ChannelFactory() {
-                @Override public ReadableByteChannel reader(String filename) throws IOException {
-                    return Files.newByteChannel(path, optionSet);
-                }
-                @Override public WritableByteChannel writer(String filename) throws IOException {
-                    return Files.newByteChannel(path, optionSet);
-                }
-            };
+            if (Files.isRegularFile(path)) {
+                return new ChannelFactory() {
+                    @Override public ReadableByteChannel readable(String filename, WarningListeners<DataStore> listeners) throws IOException {
+                        return Files.newByteChannel(path, optionSet);
+                    }
+                    @Override public WritableByteChannel writable(String filename, WarningListeners<DataStore> listeners) throws IOException {
+                        return Files.newByteChannel(path, optionSet);
+                    }
+                };
+            }
         }
         return null;
     }
@@ -258,10 +267,11 @@ 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.
+     * Returns {@code true} if this factory is capable to create another readable byte channel.
+     * This method returns {@code false} if this factory is capable to create only one channel
+     * and {@link #readable readable(…)} has already been invoked.
      *
-     * @return whether {@link #reader(String)} can be invoked.
+     * @return whether {@link #readable readable(…)} or {@link #writable writable(…)} can be invoked.
      */
     public boolean canOpen() {
         return true;
@@ -272,12 +282,15 @@ public abstract class ChannelFactory {
      * it is caller's responsibility to wrap the stream in a {@link java.io.BufferedInputStream} if desired.
      *
      * @param  filename  data store name to report in case of failure.
+     * @param  listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none.
      * @return the input stream.
      * @throws DataStoreException if the channel is read-once.
      * @throws IOException if the input stream or its underlying byte channel can not be created.
      */
-    public InputStream inputStream(final String filename) throws DataStoreException, IOException {
-        return Channels.newInputStream(reader(filename));
+    public InputStream inputStream(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException
+    {
+        return Channels.newInputStream(readable(filename, listeners));
     }
 
     /**
@@ -285,12 +298,15 @@ public abstract class ChannelFactory {
      * it is caller's responsibility to wrap the stream in a {@link java.io.BufferedOutputStream} if desired.
      *
      * @param  filename  data store name to report in case of failure.
+     * @param  listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none.
      * @return the output stream.
      * @throws DataStoreException if the channel is write-once.
      * @throws IOException if the output stream or its underlying byte channel can not be created.
      */
-    public OutputStream outputStream(final String filename) throws DataStoreException, IOException {
-        return Channels.newOutputStream(writer(filename));
+    public OutputStream outputStream(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException
+    {
+        return Channels.newOutputStream(writable(filename, listeners));
     }
 
     /**
@@ -299,11 +315,13 @@ public abstract class ChannelFactory {
      * this method throws an exception.
      *
      * @param  filename  data store name to report in case of failure.
+     * @param  listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none.
      * @return the channel for the given input.
      * @throws DataStoreException if the channel is read-once.
      * @throws IOException if an error occurred while opening the channel.
      */
-    public abstract ReadableByteChannel reader(String filename) throws DataStoreException, IOException;
+    public abstract ReadableByteChannel readable(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException;
 
     /**
      * Returns a byte channel from the output given to the {@link #prepare prepare(…)} method.
@@ -311,11 +329,13 @@ public abstract class ChannelFactory {
      * this method throws an exception.
      *
      * @param  filename  data store name to report in case of failure.
+     * @param  listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none.
      * @return the channel for the given output.
      * @throws DataStoreException if the channel is write-once.
      * @throws IOException if an error occurred while opening the channel.
      */
-    public abstract WritableByteChannel writer(String filename) throws DataStoreException, IOException;
+    public abstract WritableByteChannel writable(String filename, WarningListeners<DataStore> listeners)
+            throws DataStoreException, IOException;
 
     /**
      * A factory that returns an existing channel <cite>as-is</cite>.
@@ -343,7 +363,7 @@ public abstract class ChannelFactory {
         }
 
         /**
-         * Returns whether {@link #reader(String)} or {@link #writer(String)} can be invoked.
+         * Returns whether {@link #readable readable(…)} or {@link #writable writable(…)} can be invoked.
          */
         @Override
         public boolean canOpen() {
@@ -355,7 +375,9 @@ public abstract class ChannelFactory {
          * throws an exception on all subsequent invocations.
          */
         @Override
-        public ReadableByteChannel reader(final String filename) throws DataStoreException, IOException {
+        public ReadableByteChannel readable(final String filename, final WarningListeners<DataStore> listeners)
+                throws DataStoreException, IOException
+        {
             final Channel in = channel;
             if (in instanceof ReadableByteChannel) {
                 channel = null;
@@ -375,7 +397,9 @@ public abstract class ChannelFactory {
          * throws an exception on all subsequent invocations.
          */
         @Override
-        public WritableByteChannel writer(final String filename) throws DataStoreException, IOException {
+        public WritableByteChannel writable(final String filename, final WarningListeners<DataStore> listeners)
+                throws DataStoreException, IOException
+        {
             final Channel out = channel;
             if (out instanceof WritableByteChannel) {
                 channel = null;
@@ -425,7 +449,9 @@ public abstract class ChannelFactory {
          * {@link File} to a {@link Path}. On all subsequent invocations, the file is opened silently.</p>
          */
         @Override
-        public FileInputStream inputStream(String filename) throws IOException {
+        public FileInputStream inputStream(final String filename, final WarningListeners<DataStore> listeners)
+                throws IOException
+        {
             final FileInputStream in;
             try {
                 in = new FileInputStream(file);
@@ -441,7 +467,7 @@ public abstract class ChannelFactory {
              * But the exception was nevertheless unexpected, so log its stack trace in order
              * to allow the developer to check if there is something wrong.
              */
-            warning();
+            warning("inputStream", listeners);
             return in;
         }
 
@@ -453,7 +479,9 @@ public abstract class ChannelFactory {
          * {@link File} to a {@link Path}. On all subsequent invocations, the file is opened silently.</p>
          */
         @Override
-        public FileOutputStream outputStream(String filename) throws IOException {
+        public FileOutputStream outputStream(final String filename, final WarningListeners<DataStore> listeners)
+                throws IOException
+        {
             final FileOutputStream out;
             try {
                 out = new FileOutputStream(file);
@@ -464,7 +492,7 @@ public abstract class ChannelFactory {
                 }
                 throw ioe;
             }
-            warning();
+            warning("outputStream", listeners);
             return out;
         }
 
@@ -473,10 +501,19 @@ public abstract class ChannelFactory {
          * Since the exception was nevertheless unexpected, log its stack trace in order to allow the developer
          * to check if there is something wrong.
          */
-        private void warning() {
+        private void warning(final String method, final WarningListeners<DataStore> listeners) {
             if (cause != null) {
-                Logging.unexpectedException(Logging.getLogger(Modules.STORAGE), ChannelFactory.class, "prepare", cause);
+                final LogRecord record = new LogRecord(Level.WARNING, cause.toString());
+                record.setLoggerName(Modules.STORAGE);
+                record.setSourceMethodName(method);
+                record.setSourceClassName(ChannelFactory.class.getName());
+                record.setThrown(cause);
                 cause = null;
+                if (listeners != null) {
+                    listeners.warning(record);
+                } else {
+                    Logging.getLogger(Modules.STORAGE).log(record);
+                }
             }
         }
 
@@ -484,16 +521,16 @@ public abstract class ChannelFactory {
          * Opens a new channel for the file given at construction time.
          */
         @Override
-        public ReadableByteChannel reader(String filename) throws IOException {
-            return inputStream(filename).getChannel();
+        public ReadableByteChannel readable(String filename, WarningListeners<DataStore> listeners) throws IOException {
+            return inputStream(filename, listeners).getChannel();
         }
 
         /**
          * Opens a new channel for the file given at construction time.
          */
         @Override
-        public WritableByteChannel writer(String filename) throws IOException {
-            return outputStream(filename).getChannel();
+        public WritableByteChannel writable(String filename, WarningListeners<DataStore> listeners) throws IOException {
+            return outputStream(filename, listeners).getChannel();
         }
     }
 

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -161,6 +161,29 @@ public final class IOUtilities extends S
     }
 
     /**
+     * Returns the given path without the directories and without the extension.
+     * For example if the given path is {@code "/Users/name/Map.png"}, then this
+     * method returns {@code "Map"}.
+     *
+     * @param  path  the path from which to get the filename without extension, or {@code null}.
+     * @return the filename without extension, or {@code null} if none.
+     */
+    public static String filenameWithoutExtension(String path) {
+        if (path != null) {
+            int s = path.lastIndexOf(File.separatorChar);
+            if (s < 0 && File.separatorChar != '/') {
+                s = path.lastIndexOf('/');
+            }
+            int e = path.lastIndexOf('.');
+            if (e <= ++s) {
+                e = path.length();
+            }
+            path = path.substring(s, e);
+        }
+        return path;
+    }
+
+    /**
      * Encodes the characters that are not legal for the {@link URI#URI(String)} constructor.
      * Note that in addition to unreserved characters ("{@code _-!.~'()*}"), the reserved
      * characters ("{@code ?/[]@}") and the punctuation characters ("{@code ,;:$&+=}")

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -27,6 +27,14 @@ import java.io.UncheckedIOException;
 /**
  * Wraps an {@link ImageInputStream} as a standard {@link InputStream}.
  *
+ * <div class="section">Thread-safety</div>
+ * This class is thread-safe only if the underlying {@link ImageInputStream} is itself thread-safe.
+ * For performance reasons, this class does not synchronize the frequently invoked {@code read(…)}
+ * methods since they do nothing else than delegating to {@code ImageInputStream}. This means that
+ * if the wrapped input is {@link ChannelImageInputStream}, then this class is <strong>not</strong>
+ * thread-safe. This is not necessarily a contradiction with Java API since input streams define no
+ * explicit synchronization lock (contrarily to {@link java.io.Reader}.
+ *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @version 0.8
  *
@@ -56,6 +64,13 @@ public final class InputStreamAdapter ex
     private int nestedMarks;
 
     /**
+     * Temporarily set to {@code true} if a call to {@link #close()} should not be propagated to the {@link #input}.
+     *
+     * @see RewindableLineReader#rewind()
+     */
+    boolean keepOpen;
+
+    /**
      * Constructs a new input stream.
      *
      * @param  input  the stream to wrap.
@@ -130,7 +145,7 @@ public final class InputStreamAdapter ex
      * @throws UncheckedIOException if the mark can not be set.
      */
     @Override
-    public void mark(final int readlimit) {
+    public synchronized void mark(final int readlimit) {
         try {
             markPosition = input.getStreamPosition();
             input.flushBefore(markPosition);
@@ -146,7 +161,7 @@ public final class InputStreamAdapter ex
      * It is okay to invoke this method after {@link #mark(int)} (but not before).
      */
     @Override
-    public void mark() {
+    public synchronized void mark() {
         input.mark();
         nestedMarks++;
     }
@@ -170,7 +185,7 @@ public final class InputStreamAdapter ex
      * @throws IOException if an I/O error occurs.
      */
     @Override
-    public void reset() throws IOException {
+    public synchronized void reset() throws IOException {
         if (--nestedMarks >= 0) {
             input.reset();
         } else {
@@ -195,7 +210,7 @@ public final class InputStreamAdapter ex
      * @throws IOException if an I/O error occurs.
      */
     @Override
-    public void close() throws IOException {
-        input.close();
+    public synchronized void close() throws IOException {
+        if (!keepOpen) input.close();
     }
 }

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -36,7 +36,10 @@ import java.io.IOException;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
- * @since   0.8
+ *
+ * @see Readable
+ *
+ * @since 0.8
  * @module
  */
 public interface Markable {

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -19,27 +19,22 @@ package org.apache.sis.internal.storage.
 import java.util.List;
 import java.util.Arrays;
 import java.util.ArrayList;
-import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import java.io.Reader;
 import java.io.IOException;
 import java.text.ParsePosition;
 import java.text.ParseException;
 import org.opengis.metadata.Metadata;
-import org.opengis.util.FactoryException;
 import org.opengis.referencing.ReferenceSystem;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.internal.storage.Resources;
 import org.apache.sis.internal.system.Loggers;
-import org.apache.sis.io.wkt.WKTFormat;
-import org.apache.sis.io.wkt.Warnings;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
 import org.apache.sis.storage.UnsupportedStorageException;
-import org.apache.sis.internal.referencing.DefinitionVerifier;
 import org.apache.sis.internal.storage.MetadataBuilder;
 import org.apache.sis.internal.storage.URIDataStore;
+import org.apache.sis.setup.GeometryLibrary;
 import org.apache.sis.setup.OptionKey;
 import org.apache.sis.util.CharSequences;
 
@@ -66,6 +61,11 @@ final class Store extends URIDataStore {
     private Reader source;
 
     /**
+     * The geometry library, or {@code null} for the default.
+     */
+    private final GeometryLibrary library;
+
+    /**
      * The parsed objects, filled only when first needed.
      * May still be empty if the parsing failed.
      */
@@ -92,6 +92,7 @@ final class Store extends URIDataStore {
             throw new UnsupportedStorageException(super.getLocale(), StoreProvider.NAME,
                     connector.getStorage(), connector.getOption(OptionKey.OPEN_OPTIONS));
         }
+        library = connector.getOption(OptionKey.GEOMETRY_LIBRARY);
     }
 
     /**
@@ -128,30 +129,12 @@ final class Store extends URIDataStore {
              * definitions.
              */
             final ParsePosition pos = new ParsePosition(0);
-            final WKTFormat parser = new WKTFormat(null, null);
+            final StoreFormat parser = new StoreFormat(library, listeners);
             do {
                 final Object obj = parser.parse(wkt, pos);
                 objects.add(obj);
                 pos.setIndex(CharSequences.skipLeadingWhitespaces(wkt, pos.getIndex(), wkt.length()));
-                final Warnings warnings = parser.getWarnings();
-                if (warnings != null) {
-                    log(new LogRecord(Level.WARNING, warnings.toString()));
-                }
-                /*
-                 * The WKT has been parsed. Below is a verification of whether the parsed WKT is conform with
-                 * the authority definition (if an authority code has been specified). This verification is not
-                 * really necessary since we will use the WKT definition anyway even if we find discrepancies.
-                 * But non-conform WKT definitions happen so often in practice that we are better to check.
-                 */
-                if (obj instanceof CoordinateReferenceSystem) try {
-                    final DefinitionVerifier v = DefinitionVerifier.withAuthority((CoordinateReferenceSystem) obj, null, false);
-                    if (v != null) {
-                        final LogRecord warning = v.warning(false);
-                        if (warning != null) log(warning);
-                    }
-                } catch (FactoryException e) {
-                    listeners.warning(null, e);
-                }
+                parser.validate(obj);
             } while (pos.getIndex() < wkt.length());
         } catch (ParseException e) {
             throw new DataStoreContentException(getLocale(), "WKT", getDisplayName(), in).initCause(e);
@@ -189,6 +172,7 @@ final class Store extends URIDataStore {
                     builder.addReferenceSystem((ReferenceSystem) object);
                 }
             }
+            addTitleOrIdentifier(builder);
             metadata = builder.build(true);
         }
         return metadata;

Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java?rev=1813747&r1=1813746&r2=1813747&view=diff
==============================================================================
--- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java [UTF-8] (original)
+++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java [UTF-8] Mon Oct 30 10:25:08 2017
@@ -64,7 +64,7 @@ import org.apache.sis.internal.storage.R
  */
 public interface Aggregate extends Resource {
     /**
-     * Returns the children resources of this aggregate. The returned collection contains at least all
+     * Returns the children resources of this aggregate. The returned collection contains
      * the resources listed by their name in the following {@linkplain #getMetadata() metadata} elements.
      * The returned collection may contain more resources if the metadata are incomplete,
      * and the resources do not need to be in the same order:
@@ -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.
      */



Mime
View raw message