This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits 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 da27f39d5e Add access control when opening a datum shift grid file.
The operation is denied if the WKT `PARAMETERFILE` value is not relative to the
`$SIS_DATA/DatumChanges` directory or a sibling of the file that provide the
WKT definition. This default behavior can be customized.
da27f39d5e is described below
commit da27f39d5e6d7f881ad8044e762753d5440b6dfb
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue Sep 22 12:52:15 2026 +0900
Add access control when opening a datum shift grid file.
The operation is denied if the WKT `PARAMETERFILE` value
is not relative to the `$SIS_DATA/DatumChanges` directory
or a sibling of the file that provide the WKT definition.
This default behavior can be customized.
---
.../main/org/apache/sis/xml/bind/Context.java | 13 +-
.../main/org/apache/sis/io/wkt/WKTFormat.java | 5 +-
.../sis/parameter/DefaultParameterValue.java | 60 +++---
.../main/org/apache/sis/parameter/Parameters.java | 6 +-
.../referencing/operation/gridded/GridFile.java | 183 +++++-------------
.../referencing/operation/gridded/LoadedGrid.java | 6 +-
.../provider/FranceGeocentricInterpolation.java | 2 +-
.../sis/referencing/operation/provider/NADCON.java | 10 +-
.../sis/referencing/operation/provider/NTv2.java | 33 ++--
.../operation/transform/MathTransformBuilder.java | 51 ++++-
.../xml/bind/referencing/CC_OperationMethod.java | 4 +-
.../test/org/apache/sis/io/wkt/ElementTest.java | 3 +-
.../operation/gridded/GridFileTest.java | 117 ++++++++++++
.../FranceGeocentricInterpolationTest.java | 24 +--
.../provider/GeocentricTranslationTest.java | 2 +-
.../referencing/operation/provider/NADCONTest.java | 21 +-
.../referencing/operation/provider/NTv2Test.java | 21 +-
.../provider/PositionVector7ParamTest.java | 3 +-
.../InterpolatedGeocentricTransformTest.java | 42 ++--
.../transform/InterpolatedTransformTest.java | 28 +--
.../apache/sis/storage/netcdf/MetadataReader.java | 15 +-
.../org/apache/sis/storage/base/PRJDataStore.java | 4 +-
.../main/org/apache/sis/storage/wkt/Store.java | 2 +-
.../org/apache/sis/storage/wkt/StoreFormat.java | 5 +-
.../main/org/apache/sis/io/Authorization.java | 59 ++++++
.../main/org/apache/sis/io/package-info.java | 34 ++--
.../main/org/apache/sis/system/DataURI.java | 211 +++++++++++++++++++++
.../main/org/apache/sis/util/resources/Errors.java | 5 +
.../apache/sis/util/resources/Errors.properties | 1 +
.../apache/sis/util/resources/Errors_fr.properties | 3 +-
30 files changed, 676 insertions(+), 297 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java
index 91dd7fa3c0..bdedd3343e 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/Context.java
@@ -159,7 +159,7 @@ public final class Context extends MarshalContext {
* The base URL of ISO 19115-3 (or other standards) schemas.
* The valid values are documented in the {@link
org.apache.sis.xml.XML#SCHEMAS} property.
*/
- private final Map<String,String> schemas;
+ private final Map<String, String> schemas;
/**
* The GML version to be marshalled or unmarshalled, or {@code null} if
unspecified.
@@ -198,7 +198,7 @@ public final class Context extends MarshalContext {
*
* @see #getObjectForID(Context, String)
*/
- private final Map<String,Object> xmlidToObject;
+ private final Map<String, Object> xmlidToObject;
/**
* The identifiers used for marshalled objects in the current document.
@@ -206,7 +206,7 @@ public final class Context extends MarshalContext {
* for a given object. The {@code gml:id} values to use are not
necessarily the same as the values associated
* to {@link IdentifierSpace#ID} if some identifiers were already used for
other objects in the same XML document.
*/
- private final Map<Object,String> objectToXmlid;
+ private final Map<Object, String> objectToXmlid;
/**
* The {@link #xmlidToObject} map for each document being unmarshalled.
@@ -223,7 +223,7 @@ public final class Context extends MarshalContext {
* <p>Values of this map are the {@link #xmlidToObject} maps of the
corresponding document.
* See {@link #xmlidToObject} for a description of the meaning of those
maps.</p>
*/
- private final Map<Object, Map<String,Object>> documentToXmlids;
+ private final Map<Object, Map<String, Object>> documentToXmlids;
/**
* All identified objects associated to a global identifier (not {@code
gml:id}).
@@ -285,11 +285,12 @@ public final class Context extends MarshalContext {
* @param converter the converter in use.
* @param logFilter the object to inform about warnings.
*/
+ @SuppressWarnings("LeakingThisInConstructor")
public Context(int bitMasks,
final MarshallerPool pool,
final Locale locale,
final ZoneId timezone,
- final Map<String,String> schemas,
+ final Map<String, String> schemas,
final Version versionGML,
final Version versionMetadata,
final ExternalLinkHandler linkHandler,
@@ -902,6 +903,8 @@ public final class Context extends MarshalContext {
/**
* Returns a string representation of this context for debugging purposes.
+ *
+ * @return debugging information.
*/
@Override
public String toString() {
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
index 365840cdc6..5ba36e940f 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
@@ -800,8 +800,9 @@ public class WKTFormat extends CompoundFormat<Object> {
/**
* Sets the URI to declare as the source of the WKT definitions. This
information will be stored in
- * {@link org.apache.sis.parameter.DefaultParameterValue#getSourceFile()}
at WKT parsing time as a
- * hint for resolving relative paths as absolute paths. This value has no
effect at formatting time.
+ * {@link
org.apache.sis.parameter.DefaultParameterValue#setSourceFile(URI)} at WKT
parsing time as a
+ * hint for resolving relative paths found in {@code PARAMETERFILE[…]}
elements.
+ * This value has no effect at formatting time.
*
* @param document URI to the file that contains the WKT definitions to
parse, or {@code null} if none.
*
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterValue.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterValue.java
index 7b5197913b..f4503627d7 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterValue.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterValue.java
@@ -21,6 +21,7 @@ import java.lang.reflect.Type;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Logger;
+import java.util.function.BiFunction;
import java.io.Serializable;
import java.io.File;
import java.nio.file.Path;
@@ -52,6 +53,7 @@ import
org.apache.sis.metadata.internal.shared.ImplementationHelper;
import org.apache.sis.referencing.internal.Resources;
import org.apache.sis.referencing.internal.shared.WKTUtilities;
import org.apache.sis.referencing.internal.shared.WKTKeywords;
+import org.apache.sis.referencing.operation.transform.MathTransformBuilder;
import org.apache.sis.math.DecimalFunctions;
import org.apache.sis.math.NumberType;
import org.apache.sis.measure.Units;
@@ -101,22 +103,25 @@ import org.apache.sis.util.logging.Logging;
* Class<T> valueClass = parameter.getDescriptor().getValueClass();
* }
*
- * <h2>Absolute paths of value files</h2>
+ * <h2>Resolution of <abbr>URI</abbr> parameter values</h2>
* Parameters that are too complex for being expressed as an {@code int[]},
{@code double[]} or {@code String} type
* may be encoded in auxiliary files. It is the case, for example, of gridded
data such as datum shift grids.
* The name of an auxiliary file is given by {@link #valueFile()}, but often
as a <em>relative</em> path.
* The directory where that file is located depends on the operation using the
parameter.
- * For example, datum shift grids used by coordinate transformations are
searched in the
+ * For example, datum shift grid files used by coordinate transformations are
searched in the
* {@code $SIS_DATA/DatumChanges} directory, where {@code $SIS_DATA} is the
value of the environment variable.
- * However, the latest approach requires that all potentially used auxiliary
files are preexisting on the local machine.
- * This assumption may be applicable for parameters coming from a well-known
registry such as EPSG, but cannot work
- * with arbitrary operations where the auxiliary files need to be transferred
together with the parameter values.
- * For the latter case, an alternative is to consider the auxiliary files as
relative to the GML document or WKT file
- * that provides the parameter values. For allowing users to resolve or
download auxiliary files in that way,
- * a {@link #getSourceFile()} method is provided. Operations can then use
{@link URI#resolve(URI)} for getting the
- * absolute path of an auxiliary file from the same server or directory than
the GML or WKT file of parameter values.
+ * However, this approach requires that all potentially used auxiliary files
are preexisting on the local machine.
+ * This assumption may be applicable for parameters coming from a well-known
registry such as <abbr>EPSG</abbr>,
+ * but cannot work with arbitrary operations where the auxiliary files need to
be transferred together with the parameter values.
+ * For the latter case, Apache <abbr>SIS</abbr> fallbacks on <abbr>URI</abbr>
relative to the directory of the
+ * <abbr>JSON</abbr>, <abbr>GML</abbr> or <abbr>WKT</abbr> document where the
parameter value appears.
+ * This resolution can be done only if the document directory is specified by
the {@link #getSourceFile()} method.
*
- * <h2>Instantiation</h2>
+ * <p><abbr>URI</abbr> parameter values are not resolved by this class, but by
the operation which uses this parameter.
+ * For security reasons, an operation may reject <abbr>URI</abbr>s that are
not in the expected directory.
+ * For controlling which <abbr>URI</abbr>s to accept, see {@link
MathTransformBuilder#setAccessControl(BiFunction)}.</p>
+ *
+ * <h2>Instantiation of parameter values</h2>
* A {@linkplain DefaultParameterDescriptor parameter descriptor} must be
defined before parameter value can be created.
* Descriptors are usually predefined (often hard-coded) by map projection or
process providers. Given a descriptor,
* the preferred way to create a parameter value is to invoke the {@link
ParameterDescriptor#createValue()} method.
@@ -250,10 +255,14 @@ public class DefaultParameterValue<T> extends
FormattableObject implements Param
}
/**
- * Returns the <abbr>URI</abbr> of the <abbr>GML</abbr> document
- * or <abbr>WKT</abbr> file from which the parameter values are read.
- * This information allows to interpret {@link #valueFile()} as a path
relative to the file that defined
- * this parameter value. For example, the following snippet gets the file,
then tries to make it absolute:
+ * Returns the <abbr>URI</abbr> of the <abbr>JSON</abbr>, <abbr>GML</abbr>
or <abbr>WKT</abbr>
+ * document providing this parameter value. In the case of formats such as
<abbr>GML</abbr>
+ * where parameter values can be declared in separated files referenced by
{@code xlink:href},
+ * each parameter may have its own source <abbr>URI</abbr>.
+ *
+ * <p>This information can be used for resolving relative {@link
#valueFile()} as a sibling
+ * (i.e., a file in the same directory) of the source file that defined
this parameter value.
+ * For example, the following snippet gets the file, then tries to make it
absolute:</p>
*
* {@snippet lang="java" :
* DefaultParameterValue<?> pv = ...;
@@ -261,7 +270,7 @@ public class DefaultParameterValue<T> extends
FormattableObject implements Param
* file = pv.getSourceFile().map((base) ->
base.resolve(file)).orElse(file);
* }
*
- * @return the <abbr>URI</abbr> of the document from which the parameter
values are read.
+ * @return the <abbr>URI</abbr> of the document from which the parameter
value has is read.
*
* @see #setSourceFile(URI)
* @see Parameters#getSourceFile(ParameterDescriptor)
@@ -547,11 +556,12 @@ public class DefaultParameterValue<T> extends
FormattableObject implements Param
* The default implementation can convert the following value types:
* {@link URI}, {@link URL}, {@link Path}, {@link File}.
*
- * <h4>Relative paths to absolute paths</h4>
- * This parameter value is often a path relative to an unspecified
directory. The base directory
- * depends on the context. For example, it may be a directory where all
datum grids are cached.
- * Sometime, it is convenient to interpret the path as relative to the GML
document or WKT file
- * that defined this parameter value. For such resolution, see {@link
#getSourceFile()}.
+ * <p>This parameter value is often a path relative to a context-dependent
directory.
+ * For example, if this parameter specifies a datum shift grid file,
+ * then the returned value may be relative to the {@code
$SIS_DATA/DatumChanges} directory.
+ * If the file is not found in that directory or if this parameter is not
for a datum shift,
+ * then the returned value may be relative to the directory of the
<abbr>JSON</abbr>, <abbr>GML</abbr>
+ * or <abbr>WKT</abbr> document where this parameter value appears.</p>
*
* @return the reference to a file containing parameter values.
* @throws InvalidParameterTypeException if the value is not a reference
to a file or a URI.
@@ -559,6 +569,8 @@ public class DefaultParameterValue<T> extends
FormattableObject implements Param
*
* @see #getValue()
* @see #setValue(Object)
+ * @see #getSourceFile()
+ * @see MathTransformBuilder#setAccessControl(BiFunction)
*/
@Override
public URI valueFile() throws IllegalStateException {
@@ -602,9 +614,11 @@ public class DefaultParameterValue<T> extends
FormattableObject implements Param
}
/**
- * Sets the URI of the GML document or WKT file from which this parameter
value has been read.
- * The given URI is a hint to be returned by {@link #getSourceFile()} for
allowing callers to
- * {@linkplain URI#resolve(URI) resolve} relative {@linkplain #valueFile()
value files}.
+ * Sets the <abbr>URI</abbr> of the <abbr>JSON</abbr>, <abbr>GML</abbr> or
<abbr>WKT</abbr>
+ * document providing this parameter value.
+ * This information can be used for resolving relative file returned by
{@link #valueFile()}.
+ * Each parameter can declare its own source <abbr>URI</abbr> because some
formats such as <abbr>GML</abbr>
+ * can declare parameter values in separated files referenced by {@code
xlink:href}.
*
* @param document URI of the document from which this parameter value
has been read, or {@code null} if none.
*
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Parameters.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Parameters.java
index b850532f36..9c6a0cbec1 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Parameters.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Parameters.java
@@ -517,10 +517,10 @@ public abstract class Parameters implements
ParameterValueGroup, Cloneable, Prin
}
/**
- * Returns the <abbr>URI</abbr> of the <abbr>GML</abbr> document
- * or <abbr>WKT</abbr> file from which the parameter values are read.
+ * Returns the <abbr>URI</abbr> of the <abbr>JSON</abbr>, <abbr>GML</abbr>
or <abbr>WKT</abbr>
+ * document providing the value of the specified parameter.
* This information can be used together with {@code
getValue(ParameterDescriptor<URI>)} for
- * resolving a parameter value as a path relative to the GML or WKT file
declaring the parameter.
+ * resolving a parameter value as a path relative to the document
declaring the parameter.
* Note that the source file is not necessarily the same for all
parameters in a group, because a GML
* document could define parameters in files referenced by different
{@code xlink:href} attribute values.
*
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/gridded/GridFile.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/gridded/GridFile.java
index f8bd80929a..5f8f588e17 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/gridded/GridFile.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/gridded/GridFile.java
@@ -17,17 +17,9 @@
package org.apache.sis.referencing.operation.gridded;
import java.net.URI;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.nio.channels.Channels;
-import java.nio.channels.ReadableByteChannel;
import java.nio.file.Path;
-import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
-import java.nio.file.FileSystemNotFoundException;
-import java.util.Optional;
import java.util.logging.Level;
import java.util.concurrent.atomic.AtomicBoolean;
import org.opengis.util.FactoryException;
@@ -36,9 +28,13 @@ import org.opengis.parameter.ParameterNotFoundException;
import org.apache.sis.parameter.Parameters;
import org.apache.sis.referencing.factory.FactoryDataException;
import org.apache.sis.referencing.factory.MissingFactoryResourceException;
+import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
import org.apache.sis.referencing.operation.provider.AbstractProvider;
+import org.apache.sis.referencing.operation.transform.MathTransformBuilder;
+import
org.apache.sis.referencing.operation.transform.MathTransformProvider.Context;
import org.apache.sis.referencing.internal.Resources;
import org.apache.sis.system.DataDirectory;
+import org.apache.sis.system.DataURI;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.resources.Messages;
@@ -48,12 +44,15 @@ import org.apache.sis.util.resources.Messages;
* Resolved path to a grid file. The starting point is the path specified by a
parameter.
* If that path is relative, then this class tries to resolve it in a
directory specified
* by the {@code SIS_DATA} environment variable. If the path cannot be
resolved that way,
- * then this method check if it can be resolved relatively to the GML or WKT
file containing
+ * then this class checks if the path can be resolved relatively to the
document containing
* the parameter.
*
+ * <p>Instances of this class should be temporary.
+ * This is an helper class for loading data and discarded after the loading
completed.</p>
+ *
* @author Martin Desruisseaux (Geomatys)
*/
-public final class GridFile {
+public final class GridFile extends DataURI {
/**
* Whether the tip about the location of datum shift files has been logged.
* We log this tip only once, and only if we failed to load at least one
grid.
@@ -67,28 +66,6 @@ public final class GridFile {
return DataDirectory.DATUM_CHANGES;
}
- /**
- * The URI specified in the parameter. This URI is usually relative to an
unspecified directory.
- *
- * @see #resolved()
- */
- public final URI parameter;
-
- /**
- * The URI as an absolute path.
- */
- private URI resolved;
-
- /**
- * The base URI used for resolving the parameter, or {@code null} if none.
- */
- private URI base;
-
- /**
- * The resolved URI as a path, or {@code null} if not yet computed or not
convertible.
- */
- private Path asPath;
-
/**
* Resolves the given parameter as an absolute URI, resolved in the {@code
"$SIS_DATA/DatumChanges"} directory
* if the URI is relative. If the URI cannot be resolved, a {@link
MissingFactoryResourceException} is thrown.
@@ -96,117 +73,59 @@ public final class GridFile {
* but cannot be constructed because an optional configuration is missing.
* It is typically because the {@code SIS_DATA} environment variable has
not been set.
*
- * @param group the group of parameters from which to get the URI.
- * @param param identification of the parameter to fetch.
+ * @param context context of the transform to create, or {@code null}.
+ * @param group the group of parameters from which to get the URI.
+ * @param param identification of the parameter to fetch.
* @throws ParameterNotFoundException if the specified parameter is not
found in the given group.
* @throws MissingFactoryResourceException if the path cannot be resolved.
+ * @throws InvalidGeodeticParameterException if access is denied.
*/
- public GridFile(final Parameters group, final ParameterDescriptor<URI>
param) throws MissingFactoryResourceException {
- RuntimeException error = null;
- parameter = group.getMandatoryValue(param);
- if (parameter.isAbsolute()) {
- resolved = parameter.normalize();
- } else {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ public GridFile(final Context context, final Parameters group, final
ParameterDescriptor<URI> param)
+ throws FactoryException
+ {
+ super(group.getMandatoryValue(param));
+ /*
+ * First, try to resolve the parameter relatively to the
"$SIS_DATA/DatumChanges" directory.
+ * That directory can be seen as a cache to be tried before to
potentially download the data.
+ */
+ if (!tryResolve(localDirectory().getDirectoryAsURI()) ||
isFileMissing()) {
/*
- * First, try to resolve the parameter relative to the
"$SIS_DATA/DatumChanges" directory.
- * That directory can be seen as a cache to be tried before to
download data that may be
- * on the network.
+ * If the "$SIS_DATA/DatumChanges" directory cannot be used,
assume a file in the same directory
+ * as the document that provided the parameter. Throw an exception
if no resolution was possible,
+ * including with previous attempt. Do not throw an exception for
file not found,
+ * because that check will be done when the file will be opened.
*/
- base = localDirectory().getDirectoryAsURI();
- if (base != null) try {
- resolved = base.resolve(parameter).normalize();
- asPath = Path.of(resolved);
- if (Files.exists(asPath)) {
- return;
- }
- } catch (IllegalArgumentException | FileSystemNotFoundException e)
{
- error = e;
- }
- /*
- * If the "$SIS_DATA/DatumChanges" directory cannot be used, check
if we
- * have another base URI that we could try. If not, we cannot
continue.
- */
- final URI document = group.getSourceFile(param).orElse(null);
- if (document == null) {
- if (resolved != null) {
- return; // NoSuchFileException will be thrown
later by `newByteChannel()`.
- }
+ if (!tryResolve(group.getSourceFile(param).orElse(null)) &&
resolved() == null) {
+ /*
+ * If the URL cannot be resolved, the most important reason is
because `SIS_DATA` was not set.
+ * Try to provide an helpful error message. This is not about
whether the file exists.
+ */
final String message;
- if (parameter.isOpaque()) {
- message = Errors.format(Errors.Keys.CanNotOpen_1,
parameter);
+ if (DataDirectory.getenv() == null) {
+ message =
Messages.format(Messages.Keys.DataDirectoryNotSpecified_1, DataDirectory.ENV);
} else {
- final String env = DataDirectory.getenv();
- if (env == null) {
- message =
Messages.format(Messages.Keys.DataDirectoryNotSpecified_1, DataDirectory.ENV);
- } else {
- message =
Messages.format(Messages.Keys.DataDirectoryNotAccessible_2, DataDirectory.ENV,
env);
- }
+ message = Errors.format(Errors.Keys.CanNotOpen_1,
parameter);
}
throw new MissingFactoryResourceException(message, error);
}
- /*
- * Use the alternative base URI without checking if it exists.
- * This check will be done when the file will be opened.
- */
- base = document;
- resolved = document.resolve(parameter).normalize();
- }
- try {
- asPath = Path.of(resolved);
- } catch (IllegalArgumentException | FileSystemNotFoundException e) {
- if (error == null) error = e;
- else error.addSuppressed(e);
- asPath = null;
}
if (error != null) {
Logging.ignorableException(AbstractProvider.LOGGER,
GridFile.class, "<init>", error);
}
- }
-
- /**
- * Returns the resolved <abbr>URI</abbr>.
- *
- * @see #parameter
- */
- public URI resolved() {
- return resolved;
- }
-
- /**
- * Returns the resolved <abbr>URI</abbr> as a path if possible.
- * A use case for this method is grids to open as a {@link
org.apache.sis.storage.DataStore}.
- */
- public Optional<Path> path() {
- return Optional.ofNullable(asPath);
- }
-
- /**
- * Creates a channel for reading bytes from the file at the path specified
at construction time.
- * This method tries to open using the file system before to open from the
URL.
- *
- * @return a channel for reading bytes from the file.
- * @throws IOException if the channel cannot be created.
- */
- public ReadableByteChannel newByteChannel() throws IOException {
- if (asPath != null) {
- return Files.newByteChannel(asPath);
- } else {
- return Channels.newChannel(resolved.toURL().openStream());
+ /*
+ * Verify authorization to read the file at the given URL. If there is
no user-specified access control,
+ * the default is the verify that the URL is not outside the local
data directory or the parent directory.
+ */
+ if (context instanceof MathTransformBuilder) {
+ final var builder = (MathTransformBuilder) context;
+ switch (builder.getAccessControl().apply(param, resolved())) {
+ case GRANTED: return;
+ case DENIED: isRelative = false; break;
+ }
}
- }
-
- /**
- * Creates a buffered reader for reading characters from the file at the
path specified at construction time.
- * This method tries to open using the file system before to open from the
URL.
- *
- * @return a channel for reading bytes from the file.
- * @throws IOException if the reader cannot be created.
- */
- public BufferedReader newBufferedReader() throws IOException {
- if (asPath != null) {
- return Files.newBufferedReader(asPath);
- } else {
- return new BufferedReader(new
InputStreamReader(resolved.toURL().openStream()));
+ if (!isRelative) {
+ throw new InvalidGeodeticParameterException(accessDenied());
}
}
@@ -259,12 +178,4 @@ public final class GridFile {
return new FactoryDataException(message, cause);
}
}
-
- /**
- * Returns a string representation of this path for debugging purposes.
- */
- @Override
- public String toString() {
- return String.valueOf(resolved);
- }
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/gridded/LoadedGrid.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/gridded/LoadedGrid.java
index f19d618c50..1a5eeaf7c8 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/gridded/LoadedGrid.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/gridded/LoadedGrid.java
@@ -487,15 +487,15 @@ public abstract class LoadedGrid<C extends Quantity<C>, T
extends Quantity<T>> e
* @see
InterpolatedTransform#createGeodeticTransformation(MathTransformFactory,
DatumShiftGrid)
*/
public static MathTransform createGeodeticTransformation(final Class<?
extends AbstractProvider> provider,
- final MathTransformFactory factory, final LoadedGrid<Angle,Angle>
grid) throws FactoryException
+ final MathTransformFactory factory, final LoadedGrid<Angle, Angle>
grid) throws FactoryException
{
MathTransform global =
InterpolatedTransform.createGeodeticTransformation(factory, grid);
- final LoadedGrid<Angle,Angle>[] subgrids = grid.subgrids;
+ final LoadedGrid<Angle, Angle>[] subgrids = grid.subgrids;
if (subgrids == null) {
return global;
}
final Map<Envelope, MathTransform> specializations =
JDK19.newLinkedHashMap(subgrids.length);
- for (final LoadedGrid<Angle,Angle> sg : subgrids) try {
+ for (final LoadedGrid<Angle, Angle> sg : subgrids) try {
final Envelope domain = sg.getDomainOfValidity(Units.DEGREE);
final MathTransform st = createGeodeticTransformation(provider,
factory, sg);
if (specializations.putIfAbsent(domain, st) != null) {
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/FranceGeocentricInterpolation.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/FranceGeocentricInterpolation.java
index 5f32bd89c9..1b6b66c991 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/FranceGeocentricInterpolation.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/FranceGeocentricInterpolation.java
@@ -280,7 +280,7 @@ public final class FranceGeocentricInterpolation extends
AbstractProvider {
public MathTransform createMathTransform(final Context context) throws
FactoryException {
final Parameters pg =
Parameters.castOrWrap(context.getCompletedParameters());
final int dim = pg.getValue(Molodensky.DIMENSION);
- final GridFile file = new GridFile(pg, FILE);
+ final var file = new GridFile(context, pg, FILE);
final LoadedGrid<Angle, Length> grid;
try {
grid = getOrLoad(file, isRecognized(file) ? new double[] {TX, TY,
TZ} : null, PRECISION);
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/NADCON.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/NADCON.java
index 39abed6781..75c39f6524 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/NADCON.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/NADCON.java
@@ -139,10 +139,10 @@ public final class NADCON extends AbstractProvider {
@Override
public MathTransform createMathTransform(final Context context) throws
FactoryException {
final Parameters pg =
Parameters.castOrWrap(context.getCompletedParameters());
- final GridFile latitudeShifts = new GridFile(pg, LATITUDE);
- final GridFile longitudeShifts = new GridFile(pg, LONGITUDE);
+ final var latitudeShifts = new GridFile(context, pg, LATITUDE);
+ final var longitudeShifts = new GridFile(context, pg, LONGITUDE);
try {
- LoadedGrid<Angle,Angle> grid = getOrLoad(latitudeShifts,
longitudeShifts);
+ LoadedGrid<Angle, Angle> grid = getOrLoad(latitudeShifts,
longitudeShifts);
return LoadedGrid.createGeodeticTransformation(NADCON.class,
context.getFactory(), grid);
} catch (NoSuchFileException e) {
throw new MissingFactoryResourceException(e.getMessage(), e);
@@ -161,7 +161,7 @@ public final class NADCON extends AbstractProvider {
* @param longitudeShifts relative or absolute path name of the grid
file for longitude shifts.
* @throws Exception if an error occurred while loading the grid.
*/
- static LoadedGrid<Angle,Angle> getOrLoad(final GridFile latitudeShifts,
final GridFile longitudeShifts)
+ static LoadedGrid<Angle, Angle> getOrLoad(final GridFile latitudeShifts,
final GridFile longitudeShifts)
throws Exception
{
return LoadedGrid.getOrLoad(latitudeShifts, longitudeShifts, () -> {
@@ -258,7 +258,7 @@ public final class NADCON extends AbstractProvider {
/**
* The grid created by {@link #readGrid(FloatBuffer, Loader, URI)}.
*/
- LoadedGrid.Float<Angle,Angle> grid;
+ LoadedGrid.Float<Angle, Angle> grid;
/**
* Creates a new reader for the given channel. The file can be binary
or ASCII.
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/NTv2.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/NTv2.java
index 9a15b6f4b5..18d8174b64 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/NTv2.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/NTv2.java
@@ -140,8 +140,9 @@ public final class NTv2 extends AbstractProvider {
static MathTransform createMathTransform(final Class<? extends
AbstractProvider> provider,
final Context context, final int version) throws FactoryException
{
- final GridFile file = new
GridFile(Parameters.castOrWrap(context.getCompletedParameters()), FILE);
- final LoadedGrid<Angle,Angle> grid;
+ final Parameters pg =
Parameters.castOrWrap(context.getCompletedParameters());
+ final var file = new GridFile(context, pg, FILE);
+ final LoadedGrid<Angle, Angle> grid;
try {
grid = getOrLoad(provider, file, version);
} catch (FactoryException e) {
@@ -164,14 +165,14 @@ public final class NTv2 extends AbstractProvider {
*
* @see GridLoader#canNotLoad(String, URI, Exception)
*/
- static LoadedGrid<Angle,Angle> getOrLoad(final Class<? extends
AbstractProvider> provider,
+ static LoadedGrid<Angle, Angle> getOrLoad(final Class<? extends
AbstractProvider> provider,
final GridFile file, final int version) throws Exception
{
return LoadedGrid.getOrLoad(file, null, () -> {
final LoadedGrid<?,?> grid;
try (ReadableByteChannel in = file.newByteChannel()) {
file.startLoading(provider);
- final Loader loader = new Loader(in, file, version);
+ final var loader = new Loader(in, file, version);
grid = loader.readAllGrids();
loader.report(provider);
}
@@ -437,9 +438,9 @@ public final class NTv2 extends AbstractProvider {
* them in a child-parent relationship. The result is a tree with a
single root containing
* sub-grids (if any) as children.
*/
- final LoadedGrid<Angle,Angle> readAllGrids() throws IOException,
FactoryException, NoninvertibleTransformException {
- final Map<String, LoadedGrid<Angle,Angle>> grids =
JDK19.newHashMap(numGrids);
- final Map<String, List<LoadedGrid<Angle,Angle>>> children = new
LinkedHashMap<>(); // Should have few entries.
+ final LoadedGrid<Angle, Angle> readAllGrids() throws IOException,
FactoryException, NoninvertibleTransformException {
+ final Map<String, LoadedGrid<Angle, Angle>> grids =
JDK19.newHashMap(numGrids);
+ final Map<String, List<LoadedGrid<Angle, Angle>>> children = new
LinkedHashMap<>(); // Should have few entries.
while (grids.size() < numGrids) {
readGrid(grids, children);
}
@@ -451,10 +452,10 @@ public final class NTv2 extends AbstractProvider {
* the grids in cycles will be lost. This is because we
need a grid without parent for getting the
* graph added in the roots list. There is currently no
mechanism for detecting those problems.
*/
- final List<LoadedGrid<Angle,Angle>> roots = new ArrayList<>();
- for (final Map.Entry<String, List<LoadedGrid<Angle,Angle>>> entry
: children.entrySet()) {
- final LoadedGrid<Angle,Angle> parent =
grids.get(entry.getKey());
- final List<LoadedGrid<Angle,Angle>> subgrids =
entry.getValue();
+ final List<LoadedGrid<Angle, Angle>> roots = new ArrayList<>();
+ for (final Map.Entry<String, List<LoadedGrid<Angle, Angle>>> entry
: children.entrySet()) {
+ final LoadedGrid<Angle, Angle> parent =
grids.get(entry.getKey());
+ final List<LoadedGrid<Angle, Angle>> subgrids =
entry.getValue();
if (parent != null) {
/*
* Verify that the children does not declare themselves as
their parent.
@@ -494,8 +495,8 @@ public final class NTv2 extends AbstractProvider {
* @param addTo the map where to add the grid with the grid name
as the key.
* @param children the map where to add children with the parent
name as the key.
*/
- private void readGrid(final Map<String, LoadedGrid<Angle,Angle>> addTo,
- final Map<String, List<LoadedGrid<Angle,Angle>>> children)
+ private void readGrid(final Map<String, LoadedGrid<Angle, Angle>>
addTo,
+ final Map<String, List<LoadedGrid<Angle, Angle>>> children)
throws IOException, FactoryException,
NoninvertibleTransformException
{
if (isV2) {
@@ -545,9 +546,9 @@ public final class NTv2 extends AbstractProvider {
* will be handled by grid.coordinateToGrid MathTransform and its
inverse.
*/
final double size = Math.max(dx, dy);
- final LoadedGrid<Angle,Angle> grid;
+ final LoadedGrid<Angle, Angle> grid;
if (isV2) {
- final LoadedGrid.Float<Angle,Angle> data;
+ final LoadedGrid.Float<Angle, Angle> data;
data = new LoadedGrid.Float<>(2, unit, unit, true,
-xmin, ymin, -dx, dy, width, height, PARAMETERS, file);
@SuppressWarnings("MismatchedReadAndWriteOfArray") final
float[] tx = data.offsets[0];
@@ -567,7 +568,7 @@ public final class NTv2 extends AbstractProvider {
/*
* NTv1: same as NTv2 but using double precision and without
accuracy information.
*/
- final LoadedGrid.Double<Angle,Angle> data;
+ final LoadedGrid.Double<Angle, Angle> data;
grid = data = new LoadedGrid.Double<>(2, unit, unit, true,
-xmin, ymin, -dx, dy, width, height, PARAMETERS, file);
@SuppressWarnings("MismatchedReadAndWriteOfArray") final
double[] tx = data.offsets[0];
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformBuilder.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformBuilder.java
index b6b1435956..8de5698b8a 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformBuilder.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MathTransformBuilder.java
@@ -16,12 +16,16 @@
*/
package org.apache.sis.referencing.operation.transform;
+import java.net.URI;
import java.util.Objects;
import java.util.Optional;
+import java.util.function.BiFunction;
+import org.opengis.parameter.ParameterDescriptor;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransformFactory;
import org.opengis.referencing.operation.OperationMethod;
import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.io.Authorization;
import org.apache.sis.util.Classes;
import org.apache.sis.util.internal.shared.Strings;
@@ -35,7 +39,7 @@ import org.apache.sis.util.internal.shared.Strings;
* Then, the transform is created by a call to {@link #create()}.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.5
+ * @version 1.7
* @since 1.5
*/
public abstract class MathTransformBuilder implements MathTransform.Builder {
@@ -44,6 +48,12 @@ public abstract class MathTransformBuilder implements
MathTransform.Builder {
*/
protected final MathTransformFactory factory;
+ /**
+ * A function which determines whether the <abbr>URI</abbr> specified in a
parameter can be opened.
+ * The default access control returns {@link Authorization#DEFAULT}.
+ */
+ private BiFunction<ParameterDescriptor<URI>, URI, Authorization>
accessControl;
+
/**
* The provider that created the parameterized {@link MathTransform}
instance, or {@code null}
* if this information does not apply. This is initially set to the
operation method specified
@@ -63,6 +73,11 @@ public abstract class MathTransformBuilder implements
MathTransform.Builder {
*/
protected MathTransformBuilder(final MathTransformFactory factory) {
this.factory = Objects.requireNonNull(factory);
+ accessControl = (param, file) -> {
+ Objects.requireNonNull(param);
+ Objects.requireNonNull(file);
+ return Authorization.DEFAULT;
+ };
}
/**
@@ -78,6 +93,40 @@ public abstract class MathTransformBuilder implements
MathTransform.Builder {
return Optional.ofNullable(provider);
}
+ /**
+ * Returns a function which determines whether the <abbr>URI</abbr>
specified in a parameter can be opened.
+ * The function will receive the following arguments:
+ *
+ * <ol>
+ * <li>a description of the <abbr>URI</abbr> parameter,</li>
+ * <li>the actual <abbr>URI</abbr> parameter value.</li>
+ * </ol>
+ *
+ * The default access control is a function returning {@link
Authorization#DEFAULT}.
+ * The default authorization grants access to files in the {@code
$SIS_DATA/DatumChanges}
+ * directory for parameters that are datum shift grid files, and to files
in the same directory as the
+ * <abbr>JSON</abbr>, <abbr>GML</abbr> or <abbr>WKT</abbr> document where
the parameter value appears.
+ *
+ * @return a function deciding whether the <abbr>URI</abbr> can be opened.
+ *
+ * @since 1.7
+ */
+ public BiFunction<ParameterDescriptor<URI>, URI, Authorization>
getAccessControl() {
+ return accessControl;
+ }
+
+ /**
+ * Sets a function which determines whether the <abbr>URI</abbr> specified
in a parameter can be opened.
+ * See {@link #getAccessControl()} for more information.
+ *
+ * @param ac function telling whether the <abbr>URI</abbr> can be opened.
+ *
+ * @since 1.7
+ */
+ public void setAccessControl(BiFunction<ParameterDescriptor<URI>, URI,
Authorization> ac) {
+ accessControl = Objects.requireNonNull(ac);
+ }
+
/**
* Eventually replaces the given transform by a unique instance. The
replacement is done
* only if the {@linkplain #factory} is an instance of {@link
DefaultMathTransformFactory}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/xml/bind/referencing/CC_OperationMethod.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/xml/bind/referencing/CC_OperationMethod.java
index 9156a6f2ba..535139baaf 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/xml/bind/referencing/CC_OperationMethod.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/xml/bind/referencing/CC_OperationMethod.java
@@ -229,8 +229,8 @@ public final class CC_OperationMethod extends
PropertyType<CC_OperationMethod, O
}
p = target;
} else if (p instanceof ParameterValueGroup) {
- final ParameterValueGroup source = (ParameterValueGroup) p;
- final ParameterValueGroup target = new
DefaultParameterValueGroup((ParameterDescriptorGroup) replacement);
+ final var source = (ParameterValueGroup) p;
+ final var target = new
DefaultParameterValueGroup((ParameterDescriptorGroup) replacement);
final Collection<GeneralParameterValue> values =
source.values();
store(values.toArray(GeneralParameterValue[]::new),
target.values(), replacements);
p = target;
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ElementTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ElementTest.java
index df20596fca..af5f2c3706 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ElementTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/ElementTest.java
@@ -35,6 +35,7 @@ import org.apache.sis.test.TestCase;
*
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public final class ElementTest extends TestCase {
/**
* Creates a new test case.
@@ -61,7 +62,7 @@ public final class ElementTest extends TestCase {
* Parses the given text and ensures that {@link ParsePosition} index is
set at to the end of string.
*/
private Element parse(final String text) throws ParseException {
- final ParsePosition position = new ParsePosition(0);
+ final var position = new ParsePosition(0);
final Element element;
try {
element = new Element(parser, text, position);
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/gridded/GridFileTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/gridded/GridFileTest.java
new file mode 100644
index 0000000000..6bd47e14c3
--- /dev/null
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/gridded/GridFileTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.referencing.operation.gridded;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import org.opengis.util.FactoryException;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.GeneralParameterValue;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.parameter.DefaultParameterValue;
+import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
+
+// Test dependencies
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.apache.sis.test.Assertions.assertMessageContains;
+import org.apache.sis.test.TestCase;
+
+
+/**
+ * Tests {@link GridFile}.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+public final class GridFileTest extends TestCase {
+ /**
+ * Operation parameter descriptor for a dummy file
+ */
+ private final ParameterDescriptor<URI> param;
+
+ /**
+ * Dummy operation parameter.
+ */
+ private final Parameters group;
+
+ /**
+ * Creates a new test case.
+ */
+ public GridFileTest() {
+ final var builder = new ParameterBuilder();
+ param = builder.addName("GridFile").create(URI.class, null);
+ group = Parameters.castOrWrap(builder.addName("Test
parameteters").createGroup(param).createValue());
+ }
+
+ /**
+ * Creates the grid file with the current parameter values.
+ */
+ private GridFile newGridFile() throws FactoryException {
+ return new GridFile(null, group, param);
+ }
+
+ /**
+ * Tests construction with a file in the local directory.
+ *
+ * @throws URISyntaxException if an error occurred during URI construction.
+ * @throws FactoryException if the construction failed.
+ */
+ @Test
+ public void testLocalDirectory() throws URISyntaxException,
FactoryException {
+ final URI file = new URI("file:///tmp/test/dummy.txt");
+ group.getOrCreate(param).setValue(file);
+ assertMessageContains(
+ assertThrows(InvalidGeodeticParameterException.class, () ->
newGridFile()),
+ file.getPath());
+ /*
+ * Test again, but replacing the full path by a path local to a dummy
directory.
+ * The access should no longer be denied.
+ */
+ makeParameterRelativeToSourceFile(group);
+ final GridFile grid = newGridFile();
+ assertEquals(new URI("dummy.txt"), grid.parameter);
+ assertEquals(Path.of(file), grid.path().orElseThrow());
+ }
+
+ /**
+ * Replaces absolute <abbr>URI</abbr> by paths relative to a dummy
document.
+ * This change is needed for avoiding "access denied" during test
execution,
+ * because {@link GridFile} accepts to open only grid file from the same
host
+ * as the <abbr>JSON</abbr>, <abbr>GML</abbr> or <abbr>WKT</abbr> document.
+ *
+ * @param group the group of parameters to edit.
+ * @throws URISyntaxException if an error occurred during URI construction.
+ */
+ public static void makeParameterRelativeToSourceFile(final
ParameterValueGroup group) throws URISyntaxException {
+ for (GeneralParameterValue param : group.values()) {
+ final String name = param.getDescriptor().getName().getCode();
+ final var dp = assertInstanceOf(DefaultParameterValue.class,
param, name);
+ if (dp.getDescriptor().getValueClass() == URI.class) {
+ final URI file = dp.valueFile();
+ if (file.isAbsolute()) {
+ assertTrue(dp.getSourceFile().isEmpty(), name);
+ dp.setSourceFile(file.resolve("ImaginaryDocument.xml"));
+ URI parent = Path.of(file).getParent().toUri();
+ dp.setValue(parent.relativize(file));
+ }
+ }
+ }
+ }
+}
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/FranceGeocentricInterpolationTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/FranceGeocentricInterpolationTest.java
index 889d460104..2bee566881 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/FranceGeocentricInterpolationTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/FranceGeocentricInterpolationTest.java
@@ -29,7 +29,6 @@ import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.operation.gridded.GridFile;
import org.apache.sis.referencing.operation.gridded.LoadedGrid;
import org.apache.sis.referencing.operation.gridded.CompressedGrid;
-import org.apache.sis.referencing.factory.MissingFactoryResourceException;
import org.apache.sis.parameter.Parameters;
// Test dependencies
@@ -37,6 +36,7 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import org.apache.sis.test.TestCase;
import org.apache.sis.test.TestStep;
+import org.apache.sis.referencing.operation.gridded.GridFileTest;
/**
@@ -102,12 +102,14 @@ public final class FranceGeocentricInterpolationTest
extends TestCase {
*
* @param file the grid file <abbr>URI</abbr>.
* @return an object representing the grid file at the given URI.
- * @throws MissingFactoryResourceException if the path cannot be resolved.
+ * @throws URISyntaxException if the URL to the test file is not valid.
+ * @throws FactoryException if the path cannot be resolved.
*/
- private static GridFile newGridFile(final URI file) throws
MissingFactoryResourceException {
+ private static GridFile newGridFile(final URI file) throws
URISyntaxException, FactoryException {
Parameters pg =
Parameters.castOrWrap(FranceGeocentricInterpolation.PARAMETERS.createValue());
pg.getOrCreate(FranceGeocentricInterpolation.FILE).setValue(file);
- return new GridFile(pg, FranceGeocentricInterpolation.FILE);
+ GridFileTest.makeParameterRelativeToSourceFile(pg);
+ return new GridFile(null, pg, FranceGeocentricInterpolation.FILE);
}
/**
@@ -116,10 +118,10 @@ public final class FranceGeocentricInterpolationTest
extends TestCase {
* @param filename filename of the grid to load.
* @return an object representing the grid file for the specified resource.
* @throws URISyntaxException if the URL to the test file is not valid.
- * @throws MissingFactoryResourceException if the path cannot be resolved.
+ * @throws FactoryException if the path cannot be resolved.
*/
private static GridFile getResource(final String filename)
- throws URISyntaxException, MissingFactoryResourceException
+ throws URISyntaxException, FactoryException
{
URL file =
FranceGeocentricInterpolationTest.class.getResource(filename);
assertNotNull(file, filename);
@@ -132,12 +134,10 @@ public final class FranceGeocentricInterpolationTest
extends TestCase {
*
* @param resolved the <abbr>URI</abbr> to test.
* @throws URISyntaxException if the URL to the test file is not valid.
- * @throws MissingFactoryResourceException if the path cannot be resolved.
+ * @throws FactoryException if the path cannot be resolved.
* @return result of {@code FranceGeocentricInterpolation.isRecognized(…)}.
*/
- private static boolean isRecognized(final String resolved)
- throws URISyntaxException, MissingFactoryResourceException
- {
+ private static boolean isRecognized(final String resolved) throws
URISyntaxException, FactoryException {
return FranceGeocentricInterpolation.isRecognized(newGridFile(new
URI(resolved)));
}
@@ -145,10 +145,10 @@ public final class FranceGeocentricInterpolationTest
extends TestCase {
* Tests {@link FranceGeocentricInterpolation#isRecognized(URI)}.
*
* @throws URISyntaxException if the URL to the test file is not valid.
- * @throws MissingFactoryResourceException if the path cannot be resolved.
+ * @throws FactoryException if the path cannot be resolved.
*/
@Test
- public void testIsRecognized() throws URISyntaxException,
MissingFactoryResourceException {
+ public void testIsRecognized() throws URISyntaxException, FactoryException
{
assertTrue (isRecognized("GR3DF97A.txt"));
assertTrue (isRecognized("gr3df"));
assertFalse(isRecognized("gr3d"));
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/GeocentricTranslationTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/GeocentricTranslationTest.java
index 341ed42f68..b1cde929ac 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/GeocentricTranslationTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/GeocentricTranslationTest.java
@@ -142,7 +142,7 @@ public final class GeocentricTranslationTest extends
MathTransformTestCase {
if (method instanceof GeocentricAffineBetweenGeographic) {
setEllipsoids(values, CommonCRS.WGS84.ellipsoid(),
CommonCRS.ED50.ellipsoid());
}
- transform =
method.createMathTransform(DefaultMathTransformFactory.provider(), values);
+ transform = method.createMathTransform(null, values);
}
/**
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/NADCONTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/NADCONTest.java
index 5747469378..91cc9ab133 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/NADCONTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/NADCONTest.java
@@ -27,12 +27,12 @@ import java.nio.file.Files;
import javax.measure.quantity.Angle;
import org.opengis.geometry.Envelope;
import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.util.FactoryException;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.operation.gridded.GridFile;
import org.apache.sis.referencing.operation.gridded.GridLoader;
import org.apache.sis.referencing.operation.gridded.LoadedGrid;
import org.apache.sis.referencing.operation.matrix.Matrix3;
-import org.apache.sis.referencing.factory.MissingFactoryResourceException;
import org.apache.sis.geometry.Envelope2D;
import org.apache.sis.geometry.Envelopes;
import org.apache.sis.measure.Units;
@@ -42,6 +42,7 @@ import org.apache.sis.parameter.Parameters;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import org.apache.sis.test.TestCase;
+import org.apache.sis.referencing.operation.gridded.GridFileTest;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
import static org.opengis.test.Assertions.assertMatrixEquals;
@@ -109,14 +110,16 @@ public final class NADCONTest extends TestCase {
* @param file the grid file <abbr>URI</abbr>.
* @param parameter {@link NADCON#LONGITUDE} or {@link NADCON#LATITUDE}.
* @return an object representing the grid file at the given URI.
- * @throws MissingFactoryResourceException if the path cannot be resolved.
+ * @throws URISyntaxException if the URL to the test file is not valid.
+ * @throws FactoryException if the path cannot be resolved.
*/
- public static GridFile newGridFile(final URI file, final
ParameterDescriptor<URI> parameter)
- throws MissingFactoryResourceException
+ public static GridFile newGridFile(URI file, ParameterDescriptor<URI>
parameter)
+ throws URISyntaxException, FactoryException
{
Parameters pg = Parameters.castOrWrap(NADCON.PARAMETERS.createValue());
pg.getOrCreate(parameter).setValue(file);
- return new GridFile(pg, parameter);
+ GridFileTest.makeParameterRelativeToSourceFile(pg);
+ return new GridFile(null, pg, parameter);
}
/**
@@ -126,10 +129,10 @@ public final class NADCONTest extends TestCase {
* @param parameter {@link NADCON#LONGITUDE} or {@link NADCON#LATITUDE}.
* @return an object representing the grid file for the specified resource.
* @throws URISyntaxException if the URL to the test file is not valid.
- * @throws MissingFactoryResourceException if the path cannot be resolved.
+ * @throws FactoryException if the path cannot be resolved.
*/
private static GridFile getResource(final String filename, final
ParameterDescriptor<URI> parameter)
- throws URISyntaxException, MissingFactoryResourceException
+ throws URISyntaxException, FactoryException
{
URL file = NADCONTest.class.getResource(filename);
assertNotNull(file, filename);
@@ -175,7 +178,7 @@ public final class NADCONTest extends TestCase {
final double xmin, final double xmax, final double ymin, final
double ymax)
throws Exception
{
- final LoadedGrid<Angle,Angle> grid = NADCON.getOrLoad(latitudeShifts,
longitudeShifts);
+ final LoadedGrid<Angle, Angle> grid = NADCON.getOrLoad(latitudeShifts,
longitudeShifts);
assertInstanceOf(LoadedGrid.Float.class, grid, "Should not be
compressed.");
assertEquals(Units.DEGREE, grid.getCoordinateUnit());
assertEquals(Units.DEGREE, grid.getTranslationUnit());
@@ -262,7 +265,7 @@ public final class NADCONTest extends TestCase {
* @throws TransformException if an error occurred while computing the
envelope.
* @throws IOException if an error occurred while writing the test file.
*/
- public static void writeSubGrid(final LoadedGrid<Angle,Angle> grid, final
Path file, final int dim,
+ public static void writeSubGrid(final LoadedGrid<Angle, Angle> grid, final
Path file, final int dim,
final int gridX, final int gridY, final int nx, final int ny)
throws IOException, TransformException
{
Envelope envelope = new Envelope2D(null, gridX, gridY, nx - 1, ny - 1);
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/NTv2Test.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/NTv2Test.java
index e07e471260..550f4dce9f 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/NTv2Test.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/NTv2Test.java
@@ -29,13 +29,13 @@ import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import javax.measure.quantity.Angle;
import org.opengis.geometry.Envelope;
+import org.opengis.util.FactoryException;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.internal.shared.Formulas;
import org.apache.sis.referencing.operation.matrix.Matrix3;
import org.apache.sis.referencing.operation.gridded.GridFile;
import org.apache.sis.referencing.operation.gridded.GridGroup;
import org.apache.sis.referencing.operation.gridded.LoadedGrid;
-import org.apache.sis.referencing.factory.MissingFactoryResourceException;
import static
org.apache.sis.referencing.operation.gridded.GridLoader.DEGREES_TO_SECONDS;
import org.apache.sis.geometry.Envelope2D;
import org.apache.sis.geometry.Envelopes;
@@ -47,6 +47,7 @@ import org.apache.sis.system.DataDirectory;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import org.apache.sis.test.TestCase;
+import org.apache.sis.referencing.operation.gridded.GridFileTest;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
import static org.opengis.test.Assertions.assertMatrixEquals;
@@ -91,12 +92,14 @@ public final class NTv2Test extends TestCase {
*
* @param file the grid file <abbr>URI</abbr>.
* @return an object representing the grid file at the given URI.
- * @throws MissingFactoryResourceException if the path cannot be resolved.
+ * @throws URISyntaxException if the URL to the test file is not valid.
+ * @throws FactoryException if the path cannot be resolved.
*/
- public static GridFile newGridFile(final URI file) throws
MissingFactoryResourceException {
+ public static GridFile newGridFile(final URI file) throws
URISyntaxException, FactoryException {
Parameters pg = Parameters.castOrWrap(NTv2.PARAMETERS.createValue());
pg.getOrCreate(NTv2.FILE).setValue(file);
- return new GridFile(pg, NTv2.FILE);
+ GridFileTest.makeParameterRelativeToSourceFile(pg);
+ return new GridFile(null, pg, NTv2.FILE);
}
/**
@@ -105,11 +108,9 @@ public final class NTv2Test extends TestCase {
* @param filename filename of the grid to load.
* @return an object representing the grid file for the specified resource.
* @throws URISyntaxException if the URL to the test file is not valid.
- * @throws MissingFactoryResourceException if the path cannot be resolved.
+ * @throws FactoryException if the path cannot be resolved.
*/
- private static GridFile getResource(final String filename)
- throws URISyntaxException, MissingFactoryResourceException
- {
+ private static GridFile getResource(final String filename) throws
URISyntaxException, FactoryException {
URL file = NTv2Test.class.getResource(filename);
assertNotNull(file, filename);
return newGridFile(file.toURI());
@@ -218,7 +219,7 @@ public final class NTv2Test extends TestCase {
assumeDataExists(DataDirectory.DATUM_CHANGES, MULTIGRID_TEST_FILE);
final Parameters pg =
Parameters.castOrWrap(NTv2.PARAMETERS.createValue());
pg.getOrCreate(NTv2.FILE).setValue(new URI(MULTIGRID_TEST_FILE));
- final GridFile file = new GridFile(pg, NTv2.FILE);
+ final var file = new GridFile(null, pg, NTv2.FILE);
final LoadedGrid<Angle, Angle> grid = NTv2.getOrLoad(NTv2.class, file,
2);
assertInstanceOf(GridGroup.class, grid, "Should contain many grids.");
@@ -318,7 +319,7 @@ public final class NTv2Test extends TestCase {
* @throws TransformException if an error occurred while computing the
envelope.
* @throws IOException if an error occurred while writing the test file.
*/
- public static void writeSubGrid(final LoadedGrid<Angle,Angle> grid, final
Path out,
+ public static void writeSubGrid(final LoadedGrid<Angle, Angle> grid, final
Path out,
final int gridX, final int gridY, final int nx, final int ny)
throws IOException, TransformException
{
Envelope envelope = new Envelope2D(null, gridX, gridY, nx - 1, ny - 1);
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/PositionVector7ParamTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/PositionVector7ParamTest.java
index ef4b056a31..8949d4b75c 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/PositionVector7ParamTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/PositionVector7ParamTest.java
@@ -24,7 +24,6 @@ import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.referencing.internal.shared.Formulas;
import org.apache.sis.referencing.operation.transform.LinearTransform;
-import
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
// Test dependencies
import org.junit.jupiter.api.Test;
@@ -95,7 +94,7 @@ public final class PositionVector7ParamTest extends
MathTransformTestCase {
if (method instanceof GeocentricAffineBetweenGeographic) {
GeocentricTranslationTest.setEllipsoids(values,
CommonCRS.WGS72.ellipsoid(), CommonCRS.WGS84.ellipsoid());
}
- return
method.createMathTransform(DefaultMathTransformFactory.provider(), values);
+ return method.createMathTransform(null, values);
}
/**
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/InterpolatedGeocentricTransformTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/InterpolatedGeocentricTransformTest.java
index 20efd27cf5..1027d12358 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/InterpolatedGeocentricTransformTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/InterpolatedGeocentricTransformTest.java
@@ -17,15 +17,14 @@
package org.apache.sis.referencing.operation.transform;
import java.net.URL;
-import org.opengis.util.FactoryException;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.datum.Ellipsoid;
-import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.CommonCRS;
import
org.apache.sis.referencing.operation.provider.FranceGeocentricInterpolation;
// Test dependencies
import org.junit.jupiter.api.Test;
+import org.apache.sis.referencing.operation.gridded.GridFileTest;
import
org.apache.sis.referencing.operation.provider.FranceGeocentricInterpolationTest;
import org.apache.sis.referencing.datum.HardCodedDatum;
@@ -47,16 +46,16 @@ public final class InterpolatedGeocentricTransformTest
extends MathTransformTest
* Creates the <q>France geocentric interpolation</q> transform,
* including the normalization and denormalization parts.
*
- * @throws FactoryException if an error occurred while loading the grid.
+ * @throws Exception if an error occurred while loading the grid.
*/
- void createGeodeticTransformation() throws FactoryException {
+ private void createGeodeticTransformation() throws Exception {
createGeodeticTransformation(new FranceGeocentricInterpolation());
}
/**
* Creates the transform using the given provider.
*/
- final void createGeodeticTransformation(final
FranceGeocentricInterpolation provider) throws FactoryException {
+ private void createGeodeticTransformation(final
FranceGeocentricInterpolation provider) throws Exception {
final URL file =
FranceGeocentricInterpolationTest.class.getResource(FranceGeocentricInterpolationTest.TEST_FILE);
final Ellipsoid source = HardCodedDatum.NTF.getEllipsoid(); //
Clarke 1880 (IGN)
final Ellipsoid target = CommonCRS.ETRS89.ellipsoid(); // GRS
1980 ellipsoid
@@ -66,7 +65,8 @@ public final class InterpolatedGeocentricTransformTest
extends MathTransformTest
values.parameter("tgt_semi_major").setValue(target.getSemiMajorAxis());
values.parameter("tgt_semi_minor").setValue(target.getSemiMinorAxis());
values.parameter("Geocentric translation file").setValue(file); //
Automatic conversion from URL to Path.
- transform =
provider.createMathTransform(DefaultMathTransformFactory.provider(), values);
+ GridFileTest.makeParameterRelativeToSourceFile(values);
+ transform = provider.createMathTransform(null, values);
tolerance = FranceGeocentricInterpolationTest.ANGULAR_TOLERANCE;
}
@@ -75,11 +75,10 @@ public final class InterpolatedGeocentricTransformTest
extends MathTransformTest
* We call this transformation "forward" because it uses the grid values
directly,
* without doing first an approximation followed by an iteration.
*
- * @throws FactoryException if an error occurred while loading the grid.
- * @throws TransformException if an error occurred while transforming the
coordinate.
+ * @throws Exception if an error occurred while loading the grid or
transforming the coordinate.
*/
@Test
- public void testForwardTransform() throws FactoryException,
TransformException {
+ public void testForwardTransform() throws Exception {
createGeodeticTransformation(); // Create the inverse of the
transform we are interested in.
transform = transform.inverse();
isInverseTransformSupported = false;
@@ -97,11 +96,10 @@ public final class InterpolatedGeocentricTransformTest
extends MathTransformTest
/**
* Tests transformation of sample point from NTF to RGF93.
*
- * @throws FactoryException if an error occurred while loading the grid.
- * @throws TransformException if an error occurred while transforming the
coordinate.
+ * @throws Exception if an error occurred while loading the grid or
transforming the coordinate.
*/
@Test
- public void testInverseTransform() throws FactoryException,
TransformException {
+ public void testInverseTransform() throws Exception {
createGeodeticTransformation();
isInverseTransformSupported = false;
verifyTransform(FranceGeocentricInterpolationTest.samplePoint(1),
@@ -113,11 +111,10 @@ public final class InterpolatedGeocentricTransformTest
extends MathTransformTest
* Tests the derivatives at the sample point. This method compares the
derivatives computed by
* the transform with an estimation of derivatives computed by the finite
differences method.
*
- * @throws FactoryException if an error occurred while loading the grid.
- * @throws TransformException if an error occurred while transforming the
coordinate.
+ * @throws Exception if an error occurred while loading the grid or
transforming the coordinate.
*/
@Test
- public void testForwardDerivative() throws FactoryException,
TransformException {
+ public void testForwardDerivative() throws Exception {
createGeodeticTransformation();
transform = transform.inverse();
final double delta = (100.0 / 60) / 1852; // Approximately
100 metres.
@@ -130,11 +127,10 @@ public final class InterpolatedGeocentricTransformTest
extends MathTransformTest
* Tests the derivatives at the sample point. This method compares the
derivatives computed by
* the transform with an estimation of derivatives computed by the finite
differences method.
*
- * @throws FactoryException if an error occurred while loading the grid.
- * @throws TransformException if an error occurred while transforming the
coordinate.
+ * @throws Exception if an error occurred while loading the grid or
transforming the coordinate.
*/
@Test
- public void testInverseDerivative() throws FactoryException,
TransformException {
+ public void testInverseDerivative() throws Exception {
createGeodeticTransformation();
final double delta = (100.0 / 60) / 1852; // Approximately
100 metres.
derivativeDeltas = new double[] {delta, delta};
@@ -146,11 +142,10 @@ public final class InterpolatedGeocentricTransformTest
extends MathTransformTest
* Tests the Well Known Text (version 1) formatting.
* The result is what we show to users, but may quite different than what
SIS has in memory.
*
- * @throws FactoryException if an error occurred while creating a
transform.
- * @throws TransformException should never happen.
+ * @throws Exception if an error occurred while loading the grid or
transforming the coordinate.
*/
@Test
- public void testWKT() throws FactoryException, TransformException {
+ public void testWKT() throws Exception {
createGeodeticTransformation();
transform = transform.inverse();
assertWktEqualsRegex("(?m)\\Q" +
@@ -180,11 +175,10 @@ public final class InterpolatedGeocentricTransformTest
extends MathTransformTest
* This WKT shows what SIS has in memory for debugging purpose.
* This is normally not what we show to users.
*
- * @throws FactoryException if an error occurred while creating a
transform.
- * @throws TransformException should never happen.
+ * @throws Exception if an error occurred while loading the grid or
transforming the coordinate.
*/
@Test
- public void testInternalWKT() throws FactoryException, TransformException {
+ public void testInternalWKT() throws Exception {
createGeodeticTransformation();
assertInternalWktEqualsRegex("(?m)\\Q" +
"Concat_MT[\n" +
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/InterpolatedTransformTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/InterpolatedTransformTest.java
index 98061ba8c0..72cba172ca 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/InterpolatedTransformTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/InterpolatedTransformTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.sis.referencing.operation.transform;
+import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import org.opengis.util.FactoryException;
@@ -23,6 +24,7 @@ import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.operation.provider.NADCON;
import org.apache.sis.referencing.operation.provider.NTv2;
+import org.apache.sis.referencing.operation.gridded.GridFileTest;
import org.apache.sis.referencing.internal.shared.Formulas;
// Test dependencies
@@ -73,12 +75,13 @@ public final class InterpolatedTransformTest extends
MathTransformTestCase {
*
* @throws FactoryException if an error occurred while loading the grid.
*/
- private void createRGF93() throws FactoryException {
+ private void createRGF93() throws URISyntaxException, FactoryException {
final URL file = NTv2Test.class.getResource(NTv2Test.TEST_FILE);
final NTv2 provider = new NTv2();
final ParameterValueGroup values =
provider.getParameters().createValue();
- values.parameter("Latitude and longitude difference
file").setValue(file); // Automatic conversion from URL to Path.
- transform =
provider.createMathTransform(DefaultMathTransformFactory.provider(), values);
+ values.parameter("Latitude and longitude difference
file").setValue(file);
+ GridFileTest.makeParameterRelativeToSourceFile(values);
+ transform = provider.createMathTransform(null, values);
tolerance = Formulas.ANGULAR_TOLERANCE;
validate();
}
@@ -86,16 +89,17 @@ public final class InterpolatedTransformTest extends
MathTransformTestCase {
/**
* Creates a transformation from NAD27 to NAD93.
*
- * @throws FactoryException if an error occurred while loading the grid.
+ * @throws Exception if an error occurred while loading the grid.
*/
- private void createNADCON() throws FactoryException {
+ private void createNADCON() throws Exception {
final URL latitudeShifts =
NADCONTest.class.getResource(NADCONTest.TEST_FILE + ".laa");
final URL longitudeShifts =
NADCONTest.class.getResource(NADCONTest.TEST_FILE + ".loa");
final NADCON provider = new NADCON();
final ParameterValueGroup values =
provider.getParameters().createValue();
values.parameter("Latitude difference file").setValue(latitudeShifts);
values.parameter("Longitude difference
file").setValue(longitudeShifts);
- transform =
provider.createMathTransform(DefaultMathTransformFactory.provider(), values);
+ GridFileTest.makeParameterRelativeToSourceFile(values);
+ transform = provider.createMathTransform(null, values);
tolerance = NADCONTest.ANGULAR_TOLERANCE;
validate();
}
@@ -214,7 +218,7 @@ public final class InterpolatedTransformTest extends
MathTransformTestCase {
* @see InterpolatedGeocentricTransformTest#testInverseTransform()
*/
@Test
- public void testRGF93() throws FactoryException, TransformException {
+ public void testRGF93() throws Exception {
createRGF93();
// Forward transform
@@ -240,11 +244,10 @@ public final class InterpolatedTransformTest extends
MathTransformTestCase {
/**
* Performs the tests using the transformation from NAD27 to NAD93.
*
- * @throws FactoryException if an error occurred while creating a
transform.
- * @throws TransformException if an error occurred while transforming a
coordinate.
+ * @throws Exception if an error occurred while creating a transform or
transforming a coordinate.
*/
@Test
- public void testNADCON() throws FactoryException, TransformException {
+ public void testNADCON() throws Exception {
createNADCON();
// Forward transform
@@ -262,11 +265,10 @@ public final class InterpolatedTransformTest extends
MathTransformTestCase {
* Tests the Well Known Text (version 1) formatting.
* The result is what we show to users, but may be quite different than
what SIS has in memory.
*
- * @throws FactoryException if an error occurred while creating a
transform.
- * @throws TransformException should never happen.
+ * @throws Exception if an error occurred while creating a transform.
*/
@Test
- public void testWKT() throws FactoryException, TransformException {
+ public void testWKT() throws Exception {
createRGF93();
assertWktEqualsRegex("(?m)\\Q" +
"PARAM_MT[“NTv2”,\n" +
diff --git
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java
index 191f1a4d7b..43ae7b17ff 100644
---
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java
+++
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/MetadataReader.java
@@ -26,6 +26,7 @@ import java.util.LinkedHashMap;
import java.util.ArrayList;
import java.util.Collection;
import java.io.IOException;
+import java.nio.file.Path;
import java.time.temporal.Temporal;
import ucar.nc2.constants.CF; // String constants are copied by the
compiler with no UCAR reference left.
import ucar.nc2.constants.CDM; // idem
@@ -189,6 +190,14 @@ final class MetadataReader extends MetadataBuilder {
searchPath = decoder.getSearchPath();
}
+ /**
+ * Returns the netCDF file as an <abbr>URI</abbr>, or {@code null} if none.
+ */
+ private URI location() {
+ final Path location = decoder.location;
+ return (location != null) ? location.toUri() : null;
+ }
+
/**
* Invoked when a non-fatal exception occurred while reading metadata.
* This method sends a record to the registered listeners if any,
@@ -652,8 +661,10 @@ split: while ((start =
CharSequences.skipLeadingWhitespaces(value, start, lengt
*/
final String wkt = stringValue(GEOSPATIAL_BOUNDS);
if (wkt != null) {
- addBoundingPolygon(new StoreFormat(null, null, decoder.geomlib,
decoder.listeners).parseGeometry(wkt,
- stringValue(GEOSPATIAL_BOUNDS + "_crs"),
stringValue(GEOSPATIAL_BOUNDS + "_vertical_crs")));
+ var parser = new StoreFormat(location(), null, null,
decoder.geomlib, decoder.listeners);
+ addBoundingPolygon(parser.parseGeometry(wkt,
+ stringValue(GEOSPATIAL_BOUNDS + "_crs"),
+ stringValue(GEOSPATIAL_BOUNDS + "_vertical_crs")));
}
/*
* Add a description of the format. The description is determined by
the decoder in use.
diff --git
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/PRJDataStore.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/PRJDataStore.java
index 7ae3a2d2a0..772ab836b9 100644
---
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/PRJDataStore.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/PRJDataStore.java
@@ -165,7 +165,7 @@ public abstract class PRJDataStore extends URIDataStore {
throw new DataStoreException(s.getMessage(getLocale()),
s.exception);
}
final String wkt = content.toString();
- final var format = new StoreFormat(dataLocale, timezone, null,
listeners);
+ final var format = new StoreFormat(content.getURI(), dataLocale,
timezone, null, listeners);
format.setConvention(getConvention()); // Ignored if the
format is WKT 2.
try {
format.setSourceFile(content.getURI());
@@ -213,7 +213,7 @@ public abstract class PRJDataStore extends URIDataStore {
if (crs == null) {
deleteAuxiliaryFile(PRJ);
} else try (BufferedWriter out = writeAuxiliaryFile(PRJ)) {
- final var format = new StoreFormat(dataLocale, timezone, null,
listeners);
+ final var format = new StoreFormat(null, dataLocale, timezone,
null, listeners);
format.setConvention(Convention.WKT2_2015); // TODO:
upgrade to newer version.
format.format(crs, out);
out.newLine();
diff --git
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java
index aa2996c988..544da67e8f 100644
---
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/Store.java
@@ -125,7 +125,7 @@ final class Store extends URIDataStore {
* definitions.
*/
final var pos = new ParsePosition(0);
- final var parser = new StoreFormat(dataLocale, timezone, library,
listeners);
+ final var parser = new StoreFormat(location, dataLocale, timezone,
library, listeners);
do {
final Object obj = parser.parse(wkt, pos);
objects.add(obj);
diff --git
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/StoreFormat.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/StoreFormat.java
index df464ea39f..fe4b4cf2a4 100644
---
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/StoreFormat.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/wkt/StoreFormat.java
@@ -16,6 +16,7 @@
*/
package org.apache.sis.storage.wkt;
+import java.net.URI;
import java.text.ParseException;
import java.time.ZoneId;
import java.util.Locale;
@@ -62,17 +63,19 @@ public final class StoreFormat extends WKTFormat {
* The given locale will be used for {@link InternationalString}
localization;
* this is <strong>not</strong> the locale for number format.
*
+ * @param source the file being parsed, or {@code null} if unknown.
* @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 Locale locale, final ZoneId timezone,
+ public StoreFormat(final URI source, final Locale locale, final ZoneId
timezone,
final GeometryLibrary library, final StoreListeners
listeners)
{
super(locale, timezone);
this.library = library;
this.listeners = listeners;
+ setSourceFile(source);
}
/**
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/Authorization.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/Authorization.java
new file mode 100644
index 0000000000..0349db0905
--- /dev/null
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/Authorization.java
@@ -0,0 +1,59 @@
+/*
+ * 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.io;
+
+import java.nio.file.AccessDeniedException;
+
+
+/**
+ * Indication of whether access to a file or <abbr>URL</abbr> is granted or
denied.
+ * A file may be specified in a {@code xlink:href} attribute of an
<abbr>XML</abbr> document,
+ * or as a parameter in the definition of a coordinate operation (e.g. a datum
shift grid file).
+ * By default, Apache <abbr>SIS</abbr> opens these files only if they are in
dedicated directories.
+ * This enumeration is used when the default behavior is replaced by user
policy.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.7
+ * @since 1.7
+ */
+public enum Authorization {
+ /**
+ * Access to the file or <abbr>URL</abbr> is authorized.
+ * The file may be opened and its content read.
+ */
+ GRANTED,
+
+ /**
+ * Access to the file or <abbr>URL</abbr> is denied.
+ * It may result in an {@link AccessDeniedException} to be thrown.
+ */
+ DENIED,
+
+ /**
+ * Access to the file or <abbr>URL</abbr> is determined by Apache
<abbr>SIS</abbr> default policy.
+ * These defaults depend on the type of document containing references by
<abbr>URL</abbr>s.
+ * Examples:
+ *
+ * <ul>
+ * <li>In a <abbr>GML</abbr> document, follow {@code xlink:href} only if
the reference is local to the document.</li>
+ * <li>In coordinate operations defined in <abbr>JSON</abbr>,
<abbr>GML</abbr> or <abbr>WKT</abbr> documents,
+ * read datum shift grid file only if inside the {@code
$SIS_DATA/DatumChanges} directory or in the same
+ * directory or server as the document.</li>
+ * </ul>
+ */
+ DEFAULT
+}
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java
index 7e54a9c545..b9ccfdb8e1 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java
@@ -16,29 +16,21 @@
*/
/**
- * Extensions to standard Java I/O ({@link java.io.Reader}, {@link
java.io.Writer},
- * {@link java.lang.Appendable}) and {@link java.text.Format}.
- * Many classes defined in this package are filters applying on-the-fly
formatting while writing
- * text to the output device. For example, {@link
org.apache.sis.io.LineAppender} can wrap lines
- * to some maximal line length (e.g. 80 characters), and {@link
org.apache.sis.io.TableAppender}
- * replaces all occurrence of {@code '\t'} by the number of spaces needed for
producing a tabular
- * output.
+ * Extensions to standard Java I/O and formatting <abbr>API</abbr>.
+ * This package provides subtypes or utility methods for the
+ * {@link java.io.Reader}, {@link java.io.Writer}, {@link
java.lang.Appendable} and {@link java.text.Format} classes.
+ * Some subclasses are filters applying on-the-fly formatting while writing
text to the output stream.
+ * For example, {@link org.apache.sis.io.LineAppender} can wrap lines to some
maximal line length (e.g. 80 characters),
+ * and {@link org.apache.sis.io.TableAppender} replaces all occurrence of
{@code '\t'} by the number of spaces needed
+ * for producing a tabular output.
*
- * <div class="note"><b>Note:</b>
- * One of the formatter classes defined in this package is a {@link
java.text.Format java.text.Format} subclass.
- * While traditionally though as part of {@code text} packages, that {@code
Format} is defined in this I/O package
- * because it can format to an {@link java.lang.Appendable} and for
consistency with the {@link org.apache.sis.io.wkt}
- * package.</div>
+ * <h2>Unicode characters usage in <abbr>SIS</abbr></h2>
+ * Some classes in this package make extensive use of Unicode characters, in
particular for the formatting of trees and tables.
+ * Outputs printed to {@link java.lang.System#out} may not appear correctly if
the character encoding of the console is not the
+ * character encoding specified by the {@code stdout.encoding} system property
(usually <abbr>UTF</abbr>-8).
*
- * <h2>Unicode characters</h2>
- * Some formatters in this package make extensive use of Unicode characters.
This may produce
- * unexpected results in a Windows console, unless the underlying output
stream uses the correct
- * encoding (e.g. {@code new OutputStreamWriter(System.out, "Cp437")}). To
display the appropriate
- * code page for a Windows console, type {@code chcp} on the command line.
- *
- * <h2>Supplementary Unicode characters</h2>
- * This package can handle the {@linkplain
java.lang.Character#isSupplementaryCodePoint(int)
- * Unicode supplementary characters}.
+ * <p>This package, like most of Apache <abbr>SIS</abbr> library, can handle
the
+ * {@linkplain java.lang.Character#isSupplementaryCodePoint(int) Unicode
supplementary characters}.</p>
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 1.7
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/DataURI.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/DataURI.java
new file mode 100644
index 0000000000..e78471652c
--- /dev/null
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/DataURI.java
@@ -0,0 +1,211 @@
+/*
+ * 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.system;
+
+import java.util.Optional;
+import java.net.URI;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Files;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import org.apache.sis.io.Authorization;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.internal.shared.Strings;
+
+
+/**
+ * Helper base class for services which will access an user-specified
<abbr>URI</abbr>.
+ * This is a support class for access control. For example, the
<abbr>URI</abbr> can be
+ * restricted to the directory specified by {@link DataDirectory}.
+ *
+ * <p>Instances of this class should be temporary.
+ * This is an helper class for loading data and discarded after the loading
completed.</p>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ *
+ * @see DataDirectory
+ * @see Authorization
+ */
+public class DataURI {
+ /**
+ * The <abbr>URI</abbr> specified in parameter, usually relative to an
unspecified directory.
+ * This information is stored for formatting error messages in case of
failure to load data.
+ * For reading the data, use {@link #resolved()} instead.
+ *
+ * @see #resolved()
+ */
+ public final URI parameter;
+
+ /**
+ * The <abbr>URI</abbr> parameter as a resolved (usually absolute) and
normalized reference.
+ */
+ private URI resolved;
+
+ /**
+ * The resolved <abbr>URI</abbr> as a path, or {@code null} if not
convertible.
+ */
+ private Path asPath;
+
+ /**
+ * Errors that occurred while trying to resolve the <abbr>URI</abbr> or
convert it to a path.
+ * Used for logging purpose or for declaration as the cause of another
exception.
+ */
+ protected Exception error;
+
+ /**
+ * Whether the resolved <abbr>URI</abbr> is relative to the last specified
base.
+ * This is {@code true} if the last call to {@link #tryResolve(URI)}
resulted in an <abbr>URI</abbr>
+ * starting with the given base. This information can be used for access
control, in order to ensure
+ * that the file is inside the expected directory.
+ */
+ protected boolean isRelative;
+
+ /**
+ * Creates a new instance for the given user-specified <abbr>URI</abbr>.
+ *
+ * @param parameter the <abbr>URI</abbr> from a user-specified parameter.
+ */
+ protected DataURI(final URI parameter) {
+ this.parameter = parameter;
+ }
+
+ /**
+ * Tries to resolve the user-specified parameter relatively to the given
base.
+ * This method returns {@code true} if the parameter has been resolved,
not necessarily by using
+ * the given {@code base} parameter. For distinguishing whether the
resolved <abbr>URI</abbr> is
+ * relative to the given base, see the {@link #isRelative} flag.
+ *
+ * <p>If the {@code base} argument is a file instead of a directory, then
this method
+ * resolves the user-specified {@linkplain #parameter} as a sibling of the
given file.
+ * This is {@link URI#resolve(URI)} standard behavior, not a special case
of this method.
+ * This behavior is useful when a file is expected to be found in the same
directory as the
+ * <abbr>JSON</abbr>, <abbr>GML</abbr> or <abbr>WKT</abbr> document
containing the parameter.</p>
+ *
+ * @param base base directory, or {@code null} if unknown.
+ * @return whether the parameter could be resolved.
+ */
+ protected final boolean tryResolve(final URI base) {
+ if (base == null || parameter == null) {
+ return false;
+ }
+ final URI result = base.resolve(parameter).normalize();
+ if (result != resolved) {
+ if (result == parameter) {
+ isRelative = false;
+ } else {
+ String path = Strings.orEmpty(base.getPath());
+ path = path.substring(0, path.lastIndexOf('/') + 1);
+ isRelative =
Strings.orEmpty(result.getPath()).startsWith(path);
+ }
+ resolved = result;
+ try {
+ asPath = Path.of(result);
+ } catch (IllegalArgumentException | FileSystemNotFoundException e)
{
+ if (error == null) error = e;
+ else error.addSuppressed(e);
+ asPath = null;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns {@code true} if the file is inside the expected directory but
does not exists.
+ * In case of doubt, or if the file is outside the expected directory,
returns {@code false}.
+ *
+ * @return {@code true} if the path does <em>not</em> exists.
+ */
+ protected final boolean isFileMissing() {
+ return isRelative && (asPath != null) && Files.notExists(asPath);
+ }
+
+ /**
+ * Returns an error message saying that we are not authorized to read from
the user-provided <abbr>URL</abbr>.
+ *
+ * @return error message to provide in the exception to be thrown.
+ */
+ protected final String accessDenied() {
+ return Errors.format(Errors.Keys.AccessDenied_1, (asPath != null) ?
asPath : resolved);
+ }
+
+ /**
+ * Returns the <abbr>URI</abbr> parameter as a resolved (usually absolute)
and normalized reference.
+ *
+ * @return the resolved and normalized <abbr>URI</abbr>, or {@code null}
if none.
+ *
+ * @see #parameter
+ */
+ public final URI resolved() {
+ return resolved;
+ }
+
+ /**
+ * Returns the resolved <abbr>URI</abbr> as a path if possible.
+ * A use case for this method is grids to open as a {@link
org.apache.sis.storage.DataStore}.
+ *
+ * @return the resolved <abbr>URI</abbr> as a path.
+ */
+ public final Optional<Path> path() {
+ return Optional.ofNullable(asPath);
+ }
+
+ /**
+ * Creates a channel for reading bytes from the file at the path specified
at construction time.
+ * This method tries to open using the file system before to open from the
<abbr>URL</abbr>.
+ * Caller should have verified authorization before to invoke this method.
+ *
+ * @return a channel for reading bytes from the file.
+ * @throws IOException if the channel cannot be created.
+ */
+ public final ReadableByteChannel newByteChannel() throws IOException {
+ if (asPath != null) {
+ return Files.newByteChannel(asPath);
+ } else {
+ return Channels.newChannel(resolved.toURL().openStream());
+ }
+ }
+
+ /**
+ * Creates a buffered reader for reading characters from the file at the
path specified at construction time.
+ * This method tries to open using the file system before to open from the
<abbr>URL</abbr>.
+ * Caller should have verified authorization before to invoke this method.
+ *
+ * @return a channel for reading bytes from the file.
+ * @throws IOException if the reader cannot be created.
+ */
+ public final BufferedReader newBufferedReader() throws IOException {
+ if (asPath != null) {
+ return Files.newBufferedReader(asPath);
+ } else {
+ return new BufferedReader(new
InputStreamReader(resolved.toURL().openStream()));
+ }
+ }
+
+ /**
+ * Returns a string representation of this path for debugging purposes.
+ *
+ * @return string representation for debugging purposes.
+ */
+ @Override
+ public String toString() {
+ return String.valueOf(resolved != null ? resolved : parameter);
+ }
+}
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
index ca443d01ff..446813058d 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
@@ -71,6 +71,11 @@ public class Errors extends IndexedResourceBundle {
throw new IllegalAccessException();
}
+ /**
+ * Access to the “{0}” file has been denied by application
configuration.
+ */
+ public static final short AccessDenied_1 = 208;
+
/**
* ‘{0}’ is already initialized.
*/
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
index 14a647c6b6..de7b59e6e8 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.properties
@@ -24,6 +24,7 @@
# programmatic parameters do not have to be last in the formatted text, since
each localized message
# can reorder the parameters as they want.
#
+AccessDenied_1 = Access to the \u201c{0}\u201d file has
been denied by application configuration.
AlreadyInitialized_1 = \u2018{0}\u2019 is already initialized.
AmbiguousName_3 = Name \u201c{2}\u201d is ambiguous because
it can be understood as either \u201c{0}\u201d or \u201c{1}\u201d.
AmbiguousType_2 = Type \u2018{1}\u2019 is ambiguous because
it implements more than one subtype of \u2018{0}\u2019.
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
index e0a2b070fc..cde32ec641 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors_fr.properties
@@ -21,9 +21,10 @@
# U+202F NARROW NO-BREAK SPACE before ; ! and ?
# U+00A0 NO-BREAK SPACE before :
#
+AccessDenied_1 = L\u2019acc\u00e8s au fichier
\u00ab\u202f{0}\u202f\u00bb a \u00e9t\u00e9 refus\u00e9 par la configuration de
l\u2019application.
AlreadyInitialized_1 = \u2018{0}\u2019 est d\u00e9j\u00e0
initialis\u00e9.
AmbiguousName_3 = Le nom \u00ab\u202f{2}\u202f\u00bb est
ambigu\u00eb car il peut \u00eatre interpr\u00e9t\u00e9 comme
\u00ab\u202f{0}\u202f\u00bb ou \u00ab\u202f{1}\u202f\u00bb.
-AmbiguousType_2 = Le type \u2018{1}\u2019 est ambigu\u00eb
car il impl\u00e9mente plus qu'un sous-type de \u2018{0}\u2019.
+AmbiguousType_2 = Le type \u2018{1}\u2019 est ambigu\u00eb
car il impl\u00e9mente plus qu\u2019un sous-type de \u2018{0}\u2019.
BackgroundComputationFailed = Le calcul en arri\u00e8re-plan a
\u00e9chou\u00e9.
CanIterateOnlyOnce = Cet objet ne peut it\u00e9rer qu\u2019une
seule fois.
CanNotAddToExclusiveSet_2 = Aucun \u00e9l\u00e9ment ne peut \u00eatre
ajout\u00e9 \u00e0 cet ensemble car les propri\u00e9t\u00e9s \u2018{0}\u2019 et
\u2018{1}\u2019 sont mutuellement exclusives.