This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 02b02b2 Add a base class for `DataStore` having a "*.prj" auxiliary
file.
02b02b2 is described below
commit 02b02b247dfc13abf6e12f5a8f9694a25d2588dd
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Thu Mar 31 19:01:10 2022 +0200
Add a base class for `DataStore` having a "*.prj" auxiliary file.
---
.../main/java/org/apache/sis/setup/OptionKey.java | 18 +-
.../apache/sis/storage/netcdf/MetadataReader.java | 2 +-
.../apache/sis/internal/storage/PRJDataStore.java | 306 +++++++++++++++++++++
.../sis/internal/storage/ResourceOnFileSystem.java | 6 +-
.../org/apache/sis/internal/storage/Resources.java | 5 +
.../sis/internal/storage/Resources.properties | 1 +
.../sis/internal/storage/Resources_fr.properties | 1 +
.../apache/sis/internal/storage/URIDataStore.java | 30 +-
.../sis/internal/storage/io/IOUtilities.java | 36 +++
.../org/apache/sis/internal/storage/wkt/Store.java | 30 +-
.../sis/internal/storage/wkt/StoreFormat.java | 14 +-
.../sis/internal/storage/wkt/package-info.java | 2 +-
.../java/org/apache/sis/storage/DataOptionKey.java | 5 +-
.../sis/internal/storage/io/IOUtilitiesTest.java | 14 +-
14 files changed, 428 insertions(+), 42 deletions(-)
diff --git a/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
b/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
index 2d9de2c..788645e 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
@@ -29,14 +29,15 @@ import java.util.TimeZone;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.internal.system.Modules;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
/**
* Keys in a map of options for configuring various services
* ({@link org.apache.sis.storage.DataStore}, <i>etc</i>).
* {@code OptionKey}s are used for aspects that usually do not need to be
configured, except in a few specialized cases.
- * For example most data file formats read by SIS do not require the user to
specify the character encoding, since the
- * encoding it is often given in the file header or in the format
specification. However if SIS needs to read plain
+ * For example most data file formats read by SIS do not require the user to
specify the character encoding, because
+ * the encoding is often given in the file header or in the format
specification. However if SIS needs to read plain
* text files <em>and</em> the default platform encoding is not suitable, then
the user can specify the desired encoding
* explicitly using the {@link #ENCODING} option.
*
@@ -61,7 +62,7 @@ import org.apache.sis.internal.system.Modules;
* }
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.2
*
* @param <T> the type of option values.
*
@@ -192,6 +193,17 @@ public class OptionKey<T> implements Serializable {
public static final OptionKey<GeometryLibrary> GEOMETRY_LIBRARY = new
OptionKey<>("GEOMETRY_LIBRARY", GeometryLibrary.class);
/**
+ * The coordinate reference system (CRS) of data.
+ * This option can be used when the file to read does not describe itself
the data CRS.
+ * For example this option can be used when reading ASCII Grid without CRS
information,
+ * but is ignored if the ASCII Grid file is accompanied by a {@code *.prj}
file giving the CRS.
+ *
+ * @since 1.2
+ */
+ public static final OptionKey<CoordinateReferenceSystem>
COORDINATE_REFERENCE_SYSTEM =
+ new OptionKey<>("COORDINATE_REFERENCE_SYSTEM",
CoordinateReferenceSystem.class);
+
+ /**
* The number of spaces to use for indentation when formatting text files
in WKT or XML formats.
* A value of {@value org.apache.sis.io.wkt.WKTFormat#SINGLE_LINE} means
to format the whole WKT
* or XML document on a single line without line feeds or indentation.
diff --git
a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
index 4d9ebe0..92fe35e 100644
---
a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
+++
b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
@@ -660,7 +660,7 @@ split: while ((start =
CharSequences.skipLeadingWhitespaces(value, start, lengt
*/
final String wkt = stringValue(GEOSPATIAL_BOUNDS);
if (wkt != null) {
- addBoundingPolygon(new StoreFormat(decoder.geomlib,
decoder.listeners).parseGeometry(wkt,
+ addBoundingPolygon(new StoreFormat(null, null, decoder.geomlib,
decoder.listeners).parseGeometry(wkt,
stringValue(GEOSPATIAL_BOUNDS + "_crs"),
stringValue(GEOSPATIAL_BOUNDS + "_vertical_crs")));
}
final String[] format = decoder.getFormatDescription();
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java
new file mode 100644
index 0000000..3889e26
--- /dev/null
+++
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.storage;
+
+import java.net.URL;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.FileNotFoundException;
+import java.nio.charset.Charset;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.TimeZone;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.storage.DataStore;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.DataStoreProvider;
+import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.internal.storage.io.IOUtilities;
+import org.apache.sis.internal.storage.wkt.StoreFormat;
+import org.apache.sis.io.wkt.Convention;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.setup.OptionKey;
+import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ArraysExt;
+
+
+/**
+ * A data store for a file or URI accompanied by an auxiliary file of the same
name with {@code .prj} extension.
+ * If the auxiliary file is absent, {@link
OptionKey#COORDINATE_REFERENCE_SYSTEM} is used as a fallback.
+ * The WKT 1 variant used for parsing the {@code "*.prj"} file is the variant
used by "World Files" and GDAL;
+ * this is not the standard specified by OGC 01-009 (they differ in there
interpretation of units of measurement).
+ *
+ * <p>It is still possible to create a data store with a {@link
java.nio.channels.ReadableByteChannel},
+ * {@link java.io.InputStream} or {@link java.io.Reader}, in which case the
{@linkplain #location} will
+ * be null and the CRS defined by the {@code OptionKey} will be used.</p>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since 1.2
+ * @module
+ */
+public abstract class PRJDataStore extends URIDataStore {
+ /**
+ * The filename extension of {@code "*.prj"} files.
+ *
+ * @see #getComponentFiles()
+ */
+ protected static final String PRJ = "prj";
+
+ /**
+ * Character encoding in {@code *.prj} or other auxiliary files,
+ * or {@code null} for the JVM default (usually UTF-8).
+ */
+ protected final Charset encoding;
+
+ /**
+ * The locale for texts in {@code *.prj} or other auxiliary files,
+ * or {@code null} for {@link Locale#ROOT} (usually English).
+ * This locale is <strong>not</strong> used for parsing numbers or dates.
+ */
+ private final Locale locale;
+
+ /**
+ * Timezone for dates in {@code *.prj} or other auxiliary files,
+ * or {@code null} for UTC.
+ */
+ private final TimeZone timezone;
+
+ /**
+ * The coordinate reference system. This is initialized on the value
provided
+ * by {@link OptionKey#COORDINATE_REFERENCE_SYSTEM} at construction time,
and
+ * is modified later if a {@code "*.prj"} file is found.
+ */
+ protected CoordinateReferenceSystem crs;
+
+ /**
+ * Creates a new data store. The following options are recognized:
+ *
+ * <ul>
+ * <li>{@link OptionKey#COORDINATE_REFERENCE_SYSTEM}: default CRS if no
auxiliary {@code "*.prj"} file is found.</li>
+ * <li>{@link OptionKey#ENCODING}: encoding of the {@code "*.prj"} file.
Default is the JVM default.</li>
+ * <li>{@link OptionKey#TIMEZONE}: timezone of dates in the {@code
"*.prj"} file. Default is UTC.</li>
+ * <li>{@link OptionKey#LOCALE}: locale for texts in the {@code "*.prj"}
file. Default is English.</li>
+ * </ul>
+ *
+ * @param provider the factory that created this {@code PRJDataStore}
instance, or {@code null} if unspecified.
+ * @param connector information about the storage (URL, stream, reader
instance, <i>etc</i>).
+ * @throws DataStoreException if an error occurred while creating the data
store for the given storage.
+ */
+ protected PRJDataStore(final DataStoreProvider provider, final
StorageConnector connector) throws DataStoreException {
+ super(provider, connector);
+ crs = connector.getOption(OptionKey.COORDINATE_REFERENCE_SYSTEM);
+ encoding = connector.getOption(OptionKey.ENCODING);
+ locale = connector.getOption(OptionKey.LOCALE); // For
`InternationalString`, not for numbers.
+ timezone = connector.getOption(OptionKey.TIMEZONE);
+ }
+
+ /**
+ * Reads the {@code "*.prj"} auxiliary file. If the file is not found,
then this method does nothing
+ * and {@link #crs} keeps its current value (usually the default value
found at construction time).
+ *
+ * <p>This method does not verify if it has been invoked multiple time.
+ * Caller should track whether the data store has been initialized.</p>
+ *
+ * @throws DataStoreException if an error occurred while reading the file.
+ */
+ protected final void readPRJ() throws DataStoreException {
+ try {
+ final String wkt = readAuxiliaryFile(PRJ, encoding);
+ if (wkt != null) {
+ final StoreFormat format = new StoreFormat(locale, timezone,
null, listeners);
+ format.setConvention(Convention.WKT1_COMMON_UNITS);
+ crs = (CoordinateReferenceSystem) format.parseObject(wkt);
+ format.validate(crs);
+ }
+ } catch (FileNotFoundException e) {
+
listeners.warning(Resources.format(Resources.Keys.CanNotReadAuxiliaryFile_1,
PRJ), e);
+ } catch (IOException | ParseException | ClassCastException e) {
+ throw new
DataStoreException(Resources.format(Resources.Keys.CanNotReadAuxiliaryFile_1,
PRJ), e);
+ }
+ }
+
+ /**
+ * Reads the content of the auxiliary file with the specified extension.
+ * This method uses the same URI than {@link #location},
+ * except for the extension which is replaced by the given value.
+ * This method is suitable for reasonably small files.
+ *
+ * @param extension the filename extension of the auxiliary file to open.
+ * @param encoding the encoding to use for reading the file content, or
{@code null} for default.
+ * @return a stream opened on the specified file, or {@code null} if the
file is not found.
+ * @throws FileNotFoundException if the auxiliary file has not been found.
+ * @throws IOException if another error occurred while opening the stream.
+ */
+ protected final String readAuxiliaryFile(final String extension, Charset
encoding) throws IOException {
+ final URL url = IOUtilities.toAuxiliaryURL(location, extension);
+ if (url == null) {
+ return null;
+ }
+ if (encoding == null) {
+ encoding = Charset.defaultCharset();
+ }
+ try (InputStreamReader reader = new
InputStreamReader(url.openStream(), encoding)) {
+ char[] buffer = new char[1024];
+ int offset = 0, count;
+ while ((count = reader.read(buffer, offset, buffer.length -
offset)) >= 0) {
+ offset += count;
+ if (offset >= buffer.length) {
+ buffer = Arrays.copyOf(buffer, offset*2);
+ }
+ }
+ return new String(buffer, 0, offset);
+ }
+ }
+
+ /**
+ * Returns the {@linkplain #location} as a {@code Path} component together
with auxiliary files.
+ * The default implementation does the same computation as the
super-class, then adds the sibling
+ * file with {@code ".prj"} extension if it exists.
+ *
+ * @return the URI as a path, or an empty array if the URI is null.
+ * @throws DataStoreException if the URI can not be converted to a {@link
Path}.
+ */
+ @Override
+ public Path[] getComponentFiles() throws DataStoreException {
+ return listComponentFiles(PRJ);
+ }
+
+ /**
+ * Returns the {@linkplain #location} as a {@code Path} component together
with auxiliary files.
+ * This method computes the path to the main file as {@link
URIDataStore#getComponentFiles()},
+ * then add the sibling files with all extensions specified in the {@code
auxiliaries} argument.
+ * Each auxiliary file is tested for existence. Paths that are not regular
files are omitted.
+ * This is a helper method for {@link #getComponentFiles()} implementation.
+ *
+ * @param auxiliaries filename extension (without leading dot) of all
auxiliary files.
+ * @return the URI as a path, followed by all auxiliary files that exist.
+ * @throws DataStoreException if the URI can not be converted to a {@link
Path}.
+ */
+ protected final Path[] listComponentFiles(final String... auxiliaries)
throws DataStoreException {
+ final Path path;
+ if (location == null) {
+ return new Path[0];
+ } else try {
+ path = Paths.get(location);
+ } catch (IllegalArgumentException | FileSystemNotFoundException e) {
+ throw new DataStoreException(e);
+ }
+ String base = path.getFileName().toString();
+ final int s = base.lastIndexOf('.');
+ if (s >= 0) {
+ base = base.substring(0, s+1);
+ }
+ final Path[] paths = new Path[auxiliaries.length + 1];
+ paths[0] = path;
+ int count = 1;
+ for (final String extension : auxiliaries) {
+ final Path p = path.resolveSibling(base.concat(extension));
+ if (Files.isRegularFile(p)) {
+ paths[count++] = p;
+ }
+ }
+ return ArraysExt.resize(paths, count);
+ }
+
+ /**
+ * Returns the parameters used to open this data store.
+ *
+ * @return parameters used for opening this {@code DataStore}.
+ */
+ @Override
+ public Optional<ParameterValueGroup> getOpenParameters() {
+ final ParameterValueGroup pg = parameters(provider, location);
+ if (pg != null) {
+ pg.parameter(Provider.CRS_NAME).setValue(crs);
+ return Optional.of(pg);
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Provider for {@link PRJDataStore} instances.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since 1.2
+ * @module
+ */
+ public abstract static class Provider extends URIDataStore.Provider {
+ /**
+ * Name of the {@link #COORDINATE_REFERENCE_SYSTEM} parameter.
+ */
+ static final String CRS_NAME = "crs";
+
+ /**
+ * Description of the optional parameter for the default coordinate
reference system.
+ */
+ public static final ParameterDescriptor<CoordinateReferenceSystem>
COORDINATE_REFERENCE_SYSTEM;
+ static {
+ final ParameterBuilder builder = new ParameterBuilder();
+ COORDINATE_REFERENCE_SYSTEM =
builder.addName(CRS_NAME).setDescription(
+
Vocabulary.formatInternational(Vocabulary.Keys.CoordinateRefSys))
+ .create(CoordinateReferenceSystem.class, null);
+ }
+
+ /**
+ * Creates a new provider.
+ */
+ protected Provider() {
+ }
+
+ /**
+ * 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 {@link
#LOCATION_PARAM} and {@link #COORDINATE_REFERENCE_SYSTEM}.
+ * Subclasses can override if they need to create a group with more
parameters.
+ *
+ * @param builder the builder to use for creating parameter
descriptor. The group name is already set.
+ * @return the parameters descriptor created from the given builder.
+ */
+ @Override
+ protected ParameterDescriptorGroup build(final ParameterBuilder
builder) {
+ return builder.createGroup(LOCATION_PARAM,
COORDINATE_REFERENCE_SYSTEM);
+ }
+
+ /**
+ * Returns a data store implementation from the given parameters.
+ *
+ * @return a data store implementation associated with this provider
for the given parameters.
+ * @throws DataStoreException if an error occurred while creating the
data store instance.
+ */
+ @Override
+ public DataStore open(final ParameterValueGroup parameters) throws
DataStoreException {
+ ArgumentChecks.ensureNonNull("parameter", parameters);
+ final StorageConnector connector = connector(this, parameters);
+ final Parameters pg = Parameters.castOrWrap(parameters);
+ connector.setOption(OptionKey.COORDINATE_REFERENCE_SYSTEM,
pg.getValue(COORDINATE_REFERENCE_SYSTEM));
+ return open(connector);
+ }
+ }
+}
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ResourceOnFileSystem.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ResourceOnFileSystem.java
index 85b74e8..11bb02e 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ResourceOnFileSystem.java
+++
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ResourceOnFileSystem.java
@@ -24,8 +24,8 @@ import org.apache.sis.storage.Resource;
/**
* A resource which is loaded from one or many files on an arbitrary file
system. This interface
* allows a resource (typically a {@linkplain org.apache.sis.storage.DataStore
data store}) to
- * list the files that it uses. This is for informative purpose only and
should not be used for
- * copying or deleting resources.
+ * list the files that it uses. It may be used for copying or deleting
resources if the caller
+ * is certain that those files are not in use.
*
* <h2>Alternatives</h2>
* <p>For copying data from one location to another, consider using
@@ -61,6 +61,8 @@ public interface ResourceOnFileSystem extends Resource {
* </div>
*
* This method should return paths to files only. It should not return
paths to directories.
+ * The caller should verify that all paths are regular files;
+ * non-existent paths should be omitted.
*
* @return files used by this resource. Should never be {@code null}.
* @throws DataStoreException if an error on the file system prevent the
creation of the list.
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
index d222f51..5bc9bef 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
+++
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.java
@@ -85,6 +85,11 @@ public final class Resources extends IndexedResourceBundle {
public static final short CanNotIntersectDataWithQuery_1 = 57;
/**
+ * Can not read “{0}” auxiliary file.
+ */
+ public static final short CanNotReadAuxiliaryFile_1 = 66;
+
+ /**
* Can not read the Coordinate Reference System (CRS) Well Known Text
(WKT) in “{0}”.
*/
public static final short CanNotReadCRS_WKT_1 = 37;
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
index b450bc4..da72a2a 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
+++
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources.properties
@@ -24,6 +24,7 @@ CanNotCreateFolderStore_1 = Can not create resources
based on the conten
CanNotDeriveTypeFromFeature_1 = Can not infer the feature type resulting
from \u201c{0}\u201d filtering.
CanNotGetCommonMetadata_2 = Can not get metadata common to
\u201c{0}\u201d files. The reason is: {1}
CanNotIntersectDataWithQuery_1 = Can not intersect \u201c{0}\u201d data
with specified query.
+CanNotReadAuxiliaryFile_1 = Can not read \u201c{0}\u201d auxiliary
file.
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.
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
index 9c37915..4d3b918 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
+++
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties
@@ -29,6 +29,7 @@ CanNotCreateFolderStore_1 = Ne peut pas cr\u00e9er
des ressources bas\u0
CanNotDeriveTypeFromFeature_1 = Ne peut pas d\u00e9terminer le type de
donn\u00e9es r\u00e9sultant du filtrage \u00ab\u202f{0}\u202f\u00bb.
CanNotGetCommonMetadata_2 = Ne peut pas obtenir les
m\u00e9ta-donn\u00e9es communes aux fichiers \u00ab\u202f{0}\u202f\u00bb. La
raison est\u00a0: {1}
CanNotIntersectDataWithQuery_1 = Ne peut pas intercepter les donn\u00e9es
de \u00ab\u202f{0}\u202f\u00bb avec la requ\u00eate qui a \u00e9t\u00e9
sp\u00e9cifi\u00e9e.
+CanNotReadAuxiliaryFile_1 = Ne peut pas lire le fichier auxiliaire de
type \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}.
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
index 4d04d9a..181f44b 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
+++
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
@@ -33,21 +33,18 @@ import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStoreProvider;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.IllegalOpenParameterException;
-import org.apache.sis.storage.event.StoreEvent;
-import org.apache.sis.storage.event.StoreListener;
-import org.apache.sis.storage.event.WarningEvent;
import org.apache.sis.internal.storage.io.IOUtilities;
import org.apache.sis.util.logging.Logging;
/**
* A data store for a storage that may be represented by a {@link URI}.
- * It is still possible to create a data store with an {@link
java.nio.channels.ReadableByteChannel},
+ * It is still possible to create a data store with a {@link
java.nio.channels.ReadableByteChannel},
* {@link java.io.InputStream} or {@link java.io.Reader}, in which case the
{@linkplain #location} will be null.
*
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
* @since 0.8
* @module
*/
@@ -55,7 +52,7 @@ public abstract class URIDataStore extends DataStore
implements StoreResource, R
/**
* The {@link DataStoreProvider#LOCATION} parameter value, or {@code null}
if none.
*/
- private final URI location;
+ protected final URI location;
/**
* Creates a new data store.
@@ -195,11 +192,10 @@ public abstract class URIDataStore extends DataStore
implements StoreResource, R
}
/**
- * 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
- * {@link #LOCATION_PARAM}. Subclasses can override if they need to
create a group with more
- * parameters.
+ * 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 {@link
#LOCATION_PARAM}.
+ * Subclasses can override if they need to create a group with more
parameters.
*
* @param builder the builder to use for creating parameter
descriptor. The group name is already set.
* @return the parameters descriptor created from the given builder.
@@ -307,16 +303,4 @@ public abstract class URIDataStore extends DataStore
implements StoreResource, R
builder.addTitleOrIdentifier(IOUtilities.filenameWithoutExtension(super.getDisplayName()),
MetadataBuilder.Scope.ALL);
}
}
-
- /**
- * Registers only listeners for {@link WarningEvent}s on the assumption
that most data stores
- * (at least the read-only ones) produce no change events.
- */
- @Override
- public <T extends StoreEvent> void addListener(Class<T> eventType,
StoreListener<? super T> listener) {
- // If an argument is null, we let the parent class throws (indirectly)
NullArgumentException.
- if (listener == null || eventType == null ||
eventType.isAssignableFrom(WarningEvent.class)) {
- super.addListener(eventType, listener);
- }
- }
}
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
index 7396df8..3439243 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
+++
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
@@ -202,6 +202,42 @@ public final class IOUtilities extends Static {
}
/**
+ * Converts the given {@link URI} to a {@link URL} with the same path
except for the file extension,
+ * which is replaced by the given extension. This method is used for
opening auxiliary files such as
+ * {@code "*.prj"} and {@code "*.tfw"} files that come with e.g. TIFF
files.
+ *
+ * @param location the URI to convert to a URL with a different
extension, or {@code null}.
+ * @param extension the file extension (without {@code '.'}) of the
auxiliary file.
+ * @return URL for the auxiliary file with the given extension, or {@code
null} if none.
+ * @throws MalformedURLException if the URI uses an unknown protocol or a
negative port number other than -1.
+ *
+ * @since 1.2
+ */
+ public static URL toAuxiliaryURL(final URI location, final String
extension) throws MalformedURLException {
+ if (location == null || !location.isAbsolute() || location.isOpaque())
{
+ return null;
+ }
+ String path = location.getRawPath(); // Raw because URL constructor
needs encoded strings.
+ int s = path.indexOf('?'); // Shall be before '#' in a
valid URL.
+ if (s < 0) {
+ s = path.indexOf('#'); // A '?' after '#' would be
part of the anchor.
+ if (s < 0) {
+ s = path.length();
+ }
+ }
+ s = path.lastIndexOf('.', s);
+ if (s >= 0) {
+ path = path.substring(0, s+1) + extension;
+ } else {
+ path = path + '.' + extension;
+ }
+ return new URL(location.getScheme(), // http, https, file
or jar.
+ location.getRawAuthority(), // Host name or
literal IP address.
+ location.getPort(), // -1 if undefined.
+ path);
+ }
+
+ /**
* 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"}.
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java
index d6d21ad..dc15d87 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java
+++
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java
@@ -19,6 +19,8 @@ package org.apache.sis.internal.storage.wkt;
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
+import java.util.Locale;
+import java.util.TimeZone;
import java.io.Reader;
import java.io.IOException;
import java.text.ParsePosition;
@@ -40,8 +42,12 @@ import org.apache.sis.util.CharSequences;
/**
* A data store which creates data objects from a WKT definition.
*
+ * <div class="note"><b>Note:</b>
+ * this class differs from {@link
org.apache.sis.internal.storage.PRJDataStore} in that
+ * the file containing WKT definition is the main file, not an auxiliary
file.</div>
+ *
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.2
* @since 0.7
* @module
*/
@@ -59,6 +65,18 @@ final class Store extends URIDataStore {
private Reader source;
/**
+ * The locale for {@link org.opengis.util.InternationalString} localization
+ * or {@code null} for {@link Locale#ROOT} (usually English).
+ * This locale is <strong>not</strong> used for parsing numbers or dates.
+ */
+ private final Locale locale;
+
+ /**
+ * Timezone for dates, or {@code null} for UTC.
+ */
+ private final TimeZone timezone;
+
+ /**
* The geometry library, or {@code null} for the default.
*/
private final GeometryLibrary library;
@@ -83,9 +101,11 @@ final class Store extends URIDataStore {
*/
public Store(final StoreProvider provider, final StorageConnector
connector) throws DataStoreException {
super(provider, connector);
- objects = new ArrayList<>();
- source = connector.commit(Reader.class, StoreProvider.NAME);
- library = connector.getOption(OptionKey.GEOMETRY_LIBRARY);
+ objects = new ArrayList<>();
+ locale = connector.getOption(OptionKey.LOCALE); // For
`InternationalString`, not for numbers.
+ timezone = connector.getOption(OptionKey.TIMEZONE);
+ library = connector.getOption(OptionKey.GEOMETRY_LIBRARY);
+ source = connector.commit(Reader.class, StoreProvider.NAME);
listeners.useWarningEventsOnly();
}
@@ -123,7 +143,7 @@ final class Store extends URIDataStore {
* definitions.
*/
final ParsePosition pos = new ParsePosition(0);
- final StoreFormat parser = new StoreFormat(library, listeners);
+ final StoreFormat parser = new StoreFormat(locale, timezone,
library, listeners);
do {
final Object obj = parser.parse(wkt, pos);
objects.add(obj);
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreFormat.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreFormat.java
index a74cc84..cf1ead8 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreFormat.java
+++
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreFormat.java
@@ -17,6 +17,8 @@
package org.apache.sis.internal.storage.wkt;
import java.text.ParseException;
+import java.util.Locale;
+import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.opengis.geometry.Geometry;
@@ -41,7 +43,7 @@ import org.apache.sis.util.ArraysExt;
* For example WKT may also appear in some global attributes of CF-netCDF
files.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.2
* @since 0.8
* @module
*/
@@ -59,12 +61,18 @@ public final class StoreFormat extends WKTFormat {
/**
* Creates a new WKT parser and encoder.
+ * The given locale will be used for {@link InternationalString}
localization;
+ * this is <strong>not</strong> the locale for number format.
*
+ * @param locale the locale for the new {@code Format}, or {@code
null} for {@code Locale.ROOT}.
+ * @param timezone the timezone, or {@code null} for UTC.
* @param library the geometry library, or {@code null} for the
default.
* @param listeners where to send warnings.
*/
- public StoreFormat(final GeometryLibrary library, final StoreListeners
listeners) {
- super(null, null);
+ public StoreFormat(final Locale locale, final TimeZone timezone,
+ final GeometryLibrary library, final StoreListeners
listeners)
+ {
+ super(locale, timezone);
this.library = library;
this.listeners = listeners;
}
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/package-info.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/package-info.java
index 51f03d9..21ef05a 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/package-info.java
+++
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/package-info.java
@@ -19,7 +19,7 @@
* {@link org.apache.sis.storage.DataStore} implementation for Well Known Text.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
* @since 0.7
* @module
*/
diff --git
a/storage/sis-storage/src/main/java/org/apache/sis/storage/DataOptionKey.java
b/storage/sis-storage/src/main/java/org/apache/sis/storage/DataOptionKey.java
index 17f0936..699dcf5 100644
---
a/storage/sis-storage/src/main/java/org/apache/sis/storage/DataOptionKey.java
+++
b/storage/sis-storage/src/main/java/org/apache/sis/storage/DataOptionKey.java
@@ -22,9 +22,8 @@ import org.apache.sis.feature.FoliationRepresentation;
/**
* Keys in a map of options for configuring the way data are read or written
to a storage.
- * {@code DataOptionKey} extends {@link OptionKey} with options about
features, coverages or other kinds of structure
- * in data files. Contrarily to {@code OptionKey}, the options defined in this
{@code DataOptionKey} class are usually
- * not applicable to other kinds of file (e.g. configuration or program files).
+ * {@code DataOptionKey} extends {@link OptionKey} with options about
features, coverages
+ * or other kinds of structure that are specific to some data formats.
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.0
diff --git
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java
b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java
index dae3080..09b0d56 100644
---
a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java
+++
b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java
@@ -35,7 +35,7 @@ import static org.junit.Assert.*;
*
* @author Martin Desruisseaux (Geomatys)
* @author Johann Sorel (Geomatys)
- * @version 0.8
+ * @version 1.2
* @since 0.3
* @module
*/
@@ -112,6 +112,18 @@ public final strictfp class IOUtilitiesTest extends
TestCase {
}
/**
+ * Tests {@link IOUtilities#toAuxiliaryURL(URI, int)}.
+ *
+ * @throws URISyntaxException if a URI can not be parsed.
+ * @throws MalformedURLException if a URL can not be parsed.
+ */
+ @Test
+ public void testAuxiliaryURL() throws URISyntaxException,
MalformedURLException {
+ assertEquals(new URL("http://localhost/directory/image.tfw"),
+ IOUtilities.toAuxiliaryURL(new
URI("http://localhost/directory/image.tiff"), "tfw"));
+ }
+
+ /**
* Tests {@link IOUtilities#filenameWithoutExtension(String)}.
*/
@Test