This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 9beb499ee7bfed73abfcde27e63b4db74a4f62a1 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Wed Dec 27 16:17:41 2023 +0100 When parsing a GML or WKT from a URI, store the URI on a new `DefaultParameterValue.sourceFile` property. This information allows to resolve `ParameterValue.valueFile` relatively to the source document. https://issues.apache.org/jira/browse/SIS-593 --- .../apache/sis/xml/util/ExternalLinkHandler.java | 3 +- .../main/org/apache/sis/io/wkt/AbstractParser.java | 23 ++- .../apache/sis/io/wkt/GeodeticObjectParser.java | 11 +- .../org/apache/sis/io/wkt/MathTransformParser.java | 20 ++- .../main/org/apache/sis/io/wkt/Parser.java | 4 +- .../main/org/apache/sis/io/wkt/WKTFormat.java | 129 ++++++++++------ .../sis/parameter/AbstractParameterDescriptor.java | 13 +- .../sis/parameter/DefaultParameterDescriptor.java | 15 +- .../parameter/DefaultParameterDescriptorGroup.java | 15 +- .../sis/parameter/DefaultParameterValue.java | 171 ++++++++++++++++----- .../sis/parameter/UnmodifiableParameterValue.java | 10 ++ .../org/apache/sis/parameter/package-info.java | 2 +- .../main/org/apache/sis/referencing/CRS.java | 7 +- .../referencing/factory/GeodeticObjectFactory.java | 8 +- .../transform/DefaultMathTransformFactory.java | 8 +- .../test/org/apache/sis/io/wkt/ElementTest.java | 4 +- .../sis/io/wkt/GeodeticObjectParserTest.java | 4 +- .../org/apache/sis/storage/base/PRJDataStore.java | 21 ++- 18 files changed, 315 insertions(+), 153 deletions(-) diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java index 6bd949f064..6bb7082bc7 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/util/ExternalLinkHandler.java @@ -132,7 +132,7 @@ public class ExternalLinkHandler { if (b == null) { return null; } - final URI baseURI; + URI baseURI; if (b instanceof URI) { // `instanceof` check of final classes are efficient. baseURI = (URI) b; } else { @@ -151,6 +151,7 @@ public class ExternalLinkHandler { warningOccured(b, e); return null; } + baseURI = baseURI.normalize(); base = baseURI; } return baseURI; diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java index 439d33516c..03b137c7b2 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java @@ -16,6 +16,7 @@ */ package org.apache.sis.io.wkt; +import java.net.URI; import java.util.Map; import java.util.List; import java.util.Date; @@ -35,11 +36,10 @@ import org.opengis.util.FactoryException; import org.opengis.util.InternationalString; import org.apache.sis.system.Loggers; import org.apache.sis.util.CharSequences; +import org.apache.sis.util.resources.Errors; import org.apache.sis.util.internal.StandardDateFormat; import org.apache.sis.measure.Units; import org.apache.sis.measure.UnitFormat; -import org.apache.sis.util.resources.Errors; -import static org.apache.sis.util.ArgumentChecks.ensureNonNull; /** @@ -83,6 +83,13 @@ abstract class AbstractParser implements Parser { */ static final int MANDATORY = 2; + /** + * The URI to declare as the source of the WKT definitions, or {@code null} if unknown. + * This information is not used directly by the parser, but will be stored in parameter values + * as a hint for resolving relative paths as absolute paths. + */ + final URI sourceFile; + /** * The locale for formatting error messages if parsing fails, or {@code null} for system default. * This is <strong>not</strong> the locale for parsing number or date values. @@ -151,22 +158,24 @@ abstract class AbstractParser implements Parser { /** * Constructs a parser using the specified set of symbols. * - * @param symbols the set of symbols to use. + * @param sourceFile URI to declare as the source of the WKT definitions, or {@code null} if unknown. * @param fragments reference to the {@link WKTFormat#fragments} map, or an empty map if none. + * @param symbols the set of symbols to use. Cannot be null. * @param numberFormat the number format provided by {@link WKTFormat}, or {@code null} for a default format. * @param dateFormat the date format provided by {@link WKTFormat}, or {@code null} for a default format. * @param unitFormat the unit format provided by {@link WKTFormat}, or {@code null} for a default format. * @param errorLocale the locale for error messages (not for parsing), or {@code null} for the system default. */ - AbstractParser(final Symbols symbols, final Map<String,StoredTree> fragments, NumberFormat numberFormat, - final DateFormat dateFormat, final UnitFormat unitFormat, final Locale errorLocale) + AbstractParser(final URI sourceFile, final Map<String,StoredTree> fragments, final Symbols symbols, + NumberFormat numberFormat, final DateFormat dateFormat, final UnitFormat unitFormat, + final Locale errorLocale) { - ensureNonNull("symbols", symbols); if (numberFormat == null) { numberFormat = symbols.createNumberFormat(); } - this.symbols = symbols; + this.sourceFile = sourceFile; this.fragments = fragments; + this.symbols = symbols; this.dateFormat = dateFormat; this.unitFormat = unitFormat; this.errorLocale = errorLocale; diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java index 1610727a53..be411c60d7 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java @@ -16,6 +16,7 @@ */ package org.apache.sis.io.wkt; +import java.net.URI; import java.util.Map; import java.util.List; import java.util.Locale; @@ -94,6 +95,7 @@ import org.apache.sis.util.iso.Types; * @author Martin Desruisseaux (IRD, Geomatys) * @author Johann Sorel (Geomatys) */ +@SuppressWarnings("LocalVariableHidesMemberVariable") // We hide with the same value made final. class GeodeticObjectParser extends MathTransformParser implements Comparator<CoordinateSystemAxis> { /* * Force class initialization of `AxisDirections` in order to have @@ -182,7 +184,7 @@ class GeodeticObjectParser extends MathTransformParser implements Comparator<Coo public GeodeticObjectParser(final Map<String,?> defaultProperties, final ObjectFactory factories, final MathTransformFactory mtFactory) { - super(Symbols.getDefault(), Collections.emptyMap(), null, null, null, + super(null, Collections.emptyMap(), Symbols.getDefault(), null, null, null, new ReferencingFactoryContainer(defaultProperties, (CRSFactory) factories, (CSFactory) factories, @@ -198,8 +200,9 @@ class GeodeticObjectParser extends MathTransformParser implements Comparator<Coo * Constructs a parser for the specified set of symbols using the specified set of factories. * This constructor is for {@link WKTFormat} usage only. * - * @param symbols the set of symbols to use. + * @param sourceFile URI to declare as the source of the WKT definitions, or {@code null} if unknown. * @param fragments reference to the {@link WKTFormat#fragments} map, or an empty map if none. + * @param symbols the set of symbols to use. Cannot be null. * @param numberFormat the number format provided by {@link WKTFormat}, or {@code null} for a default format. * @param dateFormat the date format provided by {@link WKTFormat}, or {@code null} for a default format. * @param unitFormat the unit format provided by {@link WKTFormat}, or {@code null} for a default format. @@ -207,12 +210,12 @@ class GeodeticObjectParser extends MathTransformParser implements Comparator<Coo * @param errorLocale the locale for error messages (not for parsing), or {@code null} for the system default. * @param factories on input, the factories to use. On output, the factories used. Can be null. */ - GeodeticObjectParser(final Symbols symbols, final Map<String,StoredTree> fragments, + GeodeticObjectParser(final URI sourceFile, final Map<String,StoredTree> fragments, final Symbols symbols, final NumberFormat numberFormat, final DateFormat dateFormat, final UnitFormat unitFormat, final Convention convention, final Transliterator transliterator, final Locale errorLocale, final ReferencingFactoryContainer factories) { - super(symbols, fragments, numberFormat, dateFormat, unitFormat, factories, errorLocale); + super(sourceFile, fragments, symbols, numberFormat, dateFormat, unitFormat, factories, errorLocale); this.transliterator = transliterator; usesCommonUnits = convention.usesCommonUnits; ignoreAxes = convention == Convention.WKT1_IGNORE_AXES; diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/MathTransformParser.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/MathTransformParser.java index 1afebc6c84..af4f45d5d5 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/MathTransformParser.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/MathTransformParser.java @@ -16,6 +16,7 @@ */ package org.apache.sis.io.wkt; +import java.net.URI; import java.util.Map; import java.util.Arrays; import java.util.Locale; @@ -40,13 +41,13 @@ import org.opengis.referencing.operation.OperationMethod; import org.apache.sis.referencing.util.CoordinateOperations; import org.apache.sis.referencing.util.ReferencingFactoryContainer; import org.apache.sis.referencing.util.WKTKeywords; +import org.apache.sis.parameter.DefaultParameterValue; import org.apache.sis.util.Numbers; +import org.apache.sis.util.resources.Errors; import org.apache.sis.util.internal.Constants; import org.apache.sis.math.DecimalFunctions; import org.apache.sis.measure.UnitFormat; import org.apache.sis.measure.Units; -import org.apache.sis.util.resources.Errors; -import static org.apache.sis.util.ArgumentChecks.ensureNonNull; /** @@ -134,28 +135,28 @@ class MathTransformParser extends AbstractParser { * @param mtFactory the factory to use for creating {@link MathTransform} objects. */ public MathTransformParser(final MathTransformFactory mtFactory) { - this(Symbols.getDefault(), Map.of(), null, null, null, + this(null, Map.of(), Symbols.getDefault(), null, null, null, new ReferencingFactoryContainer(null, null, null, null, null, mtFactory), null); } /** * Creates a parser using the specified set of symbols and factories. * - * @param symbols the set of symbols to use. + * @param sourceFile URI to declare as the source of the WKT definitions, or {@code null} if unknown. * @param fragments reference to the {@link WKTFormat#fragments} map, or an empty map if none. + * @param symbols the set of symbols to use. Cannot be null. * @param numberFormat the number format provided by {@link WKTFormat}, or {@code null} for a default format. * @param dateFormat the date format provided by {@link WKTFormat}, or {@code null} for a default format. * @param unitFormat the unit format provided by {@link WKTFormat}, or {@code null} for a default format. * @param factories the factories to use for creating math transforms and geodetic objects. * @param errorLocale the locale for error messages (not for parsing), or {@code null} for the system default. */ - MathTransformParser(final Symbols symbols, final Map<String,StoredTree> fragments, + MathTransformParser(final URI sourceFile, final Map<String,StoredTree> fragments, final Symbols symbols, final NumberFormat numberFormat, final DateFormat dateFormat, final UnitFormat unitFormat, final ReferencingFactoryContainer factories, final Locale errorLocale) { - super(symbols, fragments, numberFormat, dateFormat, unitFormat, errorLocale); + super(sourceFile, fragments, symbols, numberFormat, dateFormat, unitFormat, errorLocale); this.factories = factories; - ensureNonNull("factories", factories); } /** @@ -347,7 +348,7 @@ class MathTransformParser extends AbstractParser { final Unit<?> defaultSI = (defaultUnit != null) ? defaultUnit.getSystemUnit() : null; Element param = element; try { - while ((param = element.pullElement(OPTIONAL, WKTKeywords.Parameter)) != null) { + while ((param = element.pullElement(OPTIONAL, WKTKeywords.Parameter, WKTKeywords.ParameterFile)) != null) { final String name = param.pullString("name"); Unit<?> unit = parseUnit(param); param.pullElement(OPTIONAL, ID_KEYWORDS); @@ -386,6 +387,9 @@ class MathTransformParser extends AbstractParser { } else { parameter.setValue(param.pullString("stringValue")); } + if (sourceFile != null && parameter instanceof DefaultParameterValue<?>) { + ((DefaultParameterValue<?>) parameter).setSourceFile(sourceFile); + } param.close(ignoredElements); } } catch (ParameterNotFoundException e) { diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Parser.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Parser.java index eee7b89b1c..1076ef12cc 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Parser.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Parser.java @@ -60,9 +60,9 @@ public interface Parser { * For processing warnings in a different way than logging them, one can use * {@link WKTFormat#parseObject(String)} followed by a call to {@link WKTFormat#getWarnings()}. * - * @param text object encoded in Well-Known Text format (version 1 or 2). + * @param wkt object encoded in Well-Known Text format (version 1 or 2). * @return the result of parsing the given text. * @throws FactoryException if the object creation failed. */ - Object createFromWKT(String text) throws FactoryException; + Object createFromWKT(String wkt) throws FactoryException; } 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 cc7047cc0f..8fcc5b6679 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 @@ -25,10 +25,12 @@ import java.util.HashMap; import java.util.TreeMap; import java.util.List; import java.util.ArrayList; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.LogRecord; import java.util.function.Function; +import java.net.URI; import java.io.IOException; import java.text.Format; import java.text.NumberFormat; @@ -119,7 +121,7 @@ import org.opengis.metadata.Identifier; * * @author Martin Desruisseaux (Geomatys) * @author Rémi Eve (IRD) - * @version 1.4 + * @version 1.5 * * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html">WKT 2 specification</a> * @see <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html">Legacy WKT 1</a> @@ -236,6 +238,15 @@ public class WKTFormat extends CompoundFormat<Object> { */ private TreeMap<String,StoredTree> fragments; + /** + * The URI to declare as the source of the WKT definitions, or {@code null} if unknown. + * This information is not used directly by the parser, but will be stored in parameter values + * as a hint for resolving relative paths as absolute paths. + * + * @see #getSourceFile() + */ + private URI sourceFile; + /** * {@code true} if the {@link #fragments} map is shared by two or more {@code WKTFormat} instances. * In such case, the map shall not be modified; instead it must be copied before any modification. @@ -386,12 +397,11 @@ public class WKTFormat extends CompoundFormat<Object> { /** * Sets the symbols used for parsing and formatting WKT. * - * @param symbols the new set of symbols to use for parsing and formatting WKT. + * @param style the new set of symbols to use for parsing and formatting WKT. */ - public void setSymbols(final Symbols symbols) { - ArgumentChecks.ensureNonNull("symbols", symbols); - if (!symbols.equals(this.symbols)) { - this.symbols = symbols.immutable(); + public void setSymbols(final Symbols style) { + if (!style.equals(symbols)) { // Intentional NullPointerException. + symbols = style.immutable(); formatter = null; parser = null; } @@ -428,13 +438,13 @@ public class WKTFormat extends CompoundFormat<Object> { * then the default mapper is {@link Transliterator#DEFAULT} except for WKT formatted according * the {@linkplain Convention#INTERNAL internal convention}.</p> * - * @param transliterator the new mapper to use, or {@code null} for restoring the default value. + * @param map the new mapper to use, or {@code null} for restoring the default value. * * @since 0.6 */ - public void setTransliterator(final Transliterator transliterator) { - if (this.transliterator != transliterator) { - this.transliterator = transliterator; + public void setTransliterator(final Transliterator map) { + if (transliterator != map) { + transliterator = map; updateFormatter(formatter); parser = null; } @@ -452,11 +462,10 @@ public class WKTFormat extends CompoundFormat<Object> { /** * Sets whether WKT keywords should be written with upper cases or camel cases. * - * @param keywordCase the case to use for formatting keywords. + * @param style the case to use for formatting keywords. */ - public void setKeywordCase(final KeywordCase keywordCase) { - ArgumentChecks.ensureNonNull("keywordCase", keywordCase); - this.keywordCase = keywordCase; + public void setKeywordCase(final KeywordCase style) { + keywordCase = Objects.requireNonNull(style); updateFormatter(formatter); } @@ -474,13 +483,12 @@ public class WKTFormat extends CompoundFormat<Object> { /** * Sets whether to use short or long WKT keywords. * - * @param keywordStyle the style to use for formatting keywords. + * @param style the style to use for formatting keywords. * * @since 0.6 */ - public void setKeywordStyle(final KeywordStyle keywordStyle) { - ArgumentChecks.ensureNonNull("keywordStyle", keywordStyle); - this.keywordStyle = keywordStyle; + public void setKeywordStyle(final KeywordStyle style) { + keywordStyle = Objects.requireNonNull(style); updateFormatter(formatter); } @@ -505,13 +513,13 @@ public class WKTFormat extends CompoundFormat<Object> { * method tries to highlight most of the elements that are relevant to * {@link org.apache.sis.util.Utilities#equalsIgnoreMetadata(Object, Object)}.</p> * - * @param colors the colors for syntax coloring, or {@code null} if none. + * @param emphasis the colors for syntax coloring, or {@code null} if none. */ - public void setColors(Colors colors) { - if (colors != null) { - colors = colors.immutable(); + public void setColors(Colors emphasis) { + if (emphasis != null) { + emphasis = emphasis.immutable(); } - this.colors = colors; + colors = emphasis; updateFormatter(formatter); } @@ -528,12 +536,11 @@ public class WKTFormat extends CompoundFormat<Object> { /** * Sets the convention for parsing and formatting WKT elements. * - * @param convention the new convention to use for parsing and formatting WKT elements. + * @param variant the new convention to use for parsing and formatting WKT elements. */ - public void setConvention(final Convention convention) { - ArgumentChecks.ensureNonNull("convention", convention); - if (this.convention != convention) { - this.convention = convention; + public void setConvention(final Convention variant) { + if (convention != variant) { + convention = Objects.requireNonNull(variant); updateFormatter(formatter); parser = null; } @@ -625,13 +632,13 @@ public class WKTFormat extends CompoundFormat<Object> { * Sets a new indentation to be used for formatting objects. * The {@value #SINGLE_LINE} value means that the whole WKT is to be formatted on a single line. * - * @param indentation the new indentation to use. + * @param numSpaces the new indentation to use in number of spaces. * * @see org.apache.sis.setup.OptionKey#INDENTATION */ - public void setIndentation(final int indentation) { - ArgumentChecks.ensureBetween("indentation", SINGLE_LINE, Byte.MAX_VALUE, indentation); - this.indentation = (byte) indentation; + public void setIndentation(final int numSpaces) { + ArgumentChecks.ensureBetween("indentation", SINGLE_LINE, Byte.MAX_VALUE, numSpaces); + indentation = (byte) numSpaces; updateFormatter(formatter); } @@ -658,7 +665,7 @@ public class WKTFormat extends CompoundFormat<Object> { * @since 1.0 */ public void setMaximumListElements(final int limit) { - ArgumentChecks.ensureStrictlyPositive("limit", limit); + ArgumentChecks.ensureStrictlyPositive("listSizeLimit", limit); listSizeLimit = limit; updateFormatter(formatter); } @@ -668,7 +675,7 @@ public class WKTFormat extends CompoundFormat<Object> { * explicit {@code ID[…]} or {@code AUTHORITY[…]} element. The main use case is for implementing * a {@link org.opengis.referencing.crs.CRSAuthorityFactory} backed by definitions in WKT format. * - * <p>Note that this identifier apply to all objects to be created, which is generally not desirable. + * <p>Note that this identifier applies to all objects to be created, which is generally not desirable. * Callers should invoke {@code setDefaultIdentifier(null)} in a {@code finally} block.</p> * * <p>This is not a publicly committed API. If we want to make this functionality public in a future @@ -747,9 +754,47 @@ public class WKTFormat extends CompoundFormat<Object> { } } + /** + * Returns the URI to declare as the source of the WKT definitions. This value is not used directly by the parser, + * but stored as a hint for allowing users to interpret the {@code PARAMETERFILE[…]} value as a file relative to + * the file containing the WKT. The default value is {@code null}. + * + * @return the URI to declare as the source of the WKT definitions, or {@code null} if none. + * + * @see #setSourceFile(URI) + * @see org.apache.sis.parameter.DefaultParameterValue#getSourceFile() + * @see org.apache.sis.xml.MarshalContext#getDocumentURI() + * @see URI#resolve(URI) + * + * @since 1.5 + */ + public URI getSourceFile() { + return sourceFile; + } + + /** + * 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. + * + * @param document URI to the file that contains the WKT definitions to parse, or {@code null} if none. + * + * @see #getSourceFile() + * @see org.apache.sis.parameter.DefaultParameterValue#setSourceFile(URI) + * @see URI#resolve(URI) + * + * @since 1.5 + */ + public void setSourceFile(final URI document) { + if (!Objects.equals(sourceFile, document)) { + sourceFile = document; + parser = null; + } + } + /** * Returns the type of objects formatted by this class. This method has to return {@code Object.class} - * since it is the only common parent to all object types accepted by this formatter. + * because it is the only common parent to all object types accepted by this formatter. * * @return {@code Object.class} */ @@ -947,14 +992,12 @@ public class WKTFormat extends CompoundFormat<Object> { * @param modifiable whether the caller intents to modify the {@link #fragments} map. */ private AbstractParser parser(final boolean modifiable) { - @SuppressWarnings("LocalVariableHidesMemberVariable") - AbstractParser parser = this.parser; /* * `parser` is always null on a fresh clone. However, the `fragments` * map may need to be cloned if the caller intents to modify it. */ if (parser == null || (isCloned & modifiable)) { - this.parser = parser = new Parser(symbols, fragments(modifiable), + parser = new Parser(sourceFile, fragments(modifiable), symbols, (NumberFormat) getFormat(Number.class), (DateFormat) getFormat(Date.class), (UnitFormat) getFormat(Unit.class), @@ -973,12 +1016,12 @@ public class WKTFormat extends CompoundFormat<Object> { */ private final class Parser extends GeodeticObjectParser implements Function<Object,Object> { /** Creates a new parser. */ - Parser(final Symbols symbols, final Map<String,StoredTree> fragments, - final NumberFormat numberFormat, final DateFormat dateFormat, final UnitFormat unitFormat, - final Convention convention, final Transliterator transliterator, final Locale errorLocale, - final ReferencingFactoryContainer factories) + Parser(final URI sourceFile, final Map<String,StoredTree> fragments, final Symbols symbols, + final NumberFormat numberFormat, final DateFormat dateFormat, final UnitFormat unitFormat, + final Convention convention, final Transliterator transliterator, final Locale errorLocale, + final ReferencingFactoryContainer factories) { - super(symbols, fragments, numberFormat, dateFormat, unitFormat, convention, transliterator, errorLocale, factories); + super(sourceFile, fragments, symbols, numberFormat, dateFormat, unitFormat, convention, transliterator, errorLocale, factories); } /** Returns the source class and method to declare in log records. */ diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/AbstractParameterDescriptor.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/AbstractParameterDescriptor.java index 8d8a015c42..9f89555788 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/AbstractParameterDescriptor.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/AbstractParameterDescriptor.java @@ -52,8 +52,7 @@ import static org.apache.sis.util.Utilities.deepEquals; * <th class="sep">WPS</th> * <th class="sep">ISO 19115</th> * <th class="sep">Remarks</th> - * </tr> - * <tr> + * </tr><tr> * <td>{@link #getName() getName()}</td> * <td class="sep">{@code name}</td> * <td class="sep">{@code Identifier}</td> @@ -67,22 +66,19 @@ import static org.apache.sis.util.Utilities.deepEquals; * <td class="sep">{@code Abstract}</td> * <td class="sep">{@code description}</td> * <td class="sep">Also known as “definition”.</td> - * </tr> - * <tr> + * </tr><tr> * <td>{@link #getDirection()}</td> * <td class="sep"></td> * <td class="sep"></td> * <td class="sep">{@code direction}</td> * <td class="sep">Tells if the parameter is a WPS {@code Input} or {@code Output} structure.</td> - * </tr> - * <tr> + * </tr><tr> * <td>{@link #getMinimumOccurs()}</td> * <td class="sep">{@code minimumOccurs}</td> * <td class="sep">{@code MinOccurs}</td> * <td class="sep">{@code optionality}</td> * <td class="sep">{@code optionality = (minimumOccurs > 0)}</td> - * </tr> - * <tr> + * </tr><tr> * <td>{@link #getMaximumOccurs()}</td> * <td class="sep">{@code maximumOccurs}</td> * <td class="sep">{@code MaxOccurs}</td> @@ -171,6 +167,7 @@ public abstract class AbstractParameterDescriptor extends AbstractIdentifiedObje * @param maximumOccurs the {@linkplain #getMaximumOccurs() maximum number of times} that values * for this parameter group are required, or {@link Integer#MAX_VALUE} if no restriction. */ + @SuppressWarnings("this-escape") protected AbstractParameterDescriptor(final Map<String,?> properties, final int minimumOccurs, final int maximumOccurs) { diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptor.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptor.java index c0d020d969..17ae032cbb 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptor.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptor.java @@ -137,28 +137,23 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i * <th>Property name</th> * <th>Value type</th> * <th>Returned by</th> - * </tr> - * <tr> + * </tr><tr> * <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td> * <td>{@link org.opengis.metadata.Identifier} or {@link String}</td> * <td>{@link #getName()}</td> - * </tr> - * <tr> + * </tr><tr> * <td>{@value org.opengis.referencing.IdentifiedObject#ALIAS_KEY}</td> * <td>{@link org.opengis.util.GenericName} or {@link CharSequence} (optionally as array)</td> * <td>{@link #getAlias()}</td> - * </tr> - * <tr> + * </tr><tr> * <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td> * <td>{@link org.opengis.metadata.Identifier} (optionally as array)</td> * <td>{@link #getIdentifiers()}</td> - * </tr> - * <tr> + * </tr><tr> * <td>{@value org.opengis.metadata.Identifier#DESCRIPTION_KEY}</td> * <td>{@link org.opengis.util.InternationalString} or {@link String}</td> * <td>{@link #getDescription()}</td> - * </tr> - * <tr> + * </tr><tr> * <td>{@value org.opengis.referencing.IdentifiedObject#REMARKS_KEY}</td> * <td>{@link org.opengis.util.InternationalString} or {@link String}</td> * <td>{@link #getRemarks()}</td> diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptorGroup.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptorGroup.java index aa17f0fcdf..472656654e 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptorGroup.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptorGroup.java @@ -122,28 +122,23 @@ public class DefaultParameterDescriptorGroup extends AbstractParameterDescriptor * <th>Property name</th> * <th>Value type</th> * <th>Returned by</th> - * </tr> - * <tr> + * </tr><tr> * <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td> * <td>{@link org.opengis.metadata.Identifier} or {@link String}</td> * <td>{@link #getName()}</td> - * </tr> - * <tr> + * </tr><tr> * <td>{@value org.opengis.referencing.IdentifiedObject#ALIAS_KEY}</td> * <td>{@link org.opengis.util.GenericName} or {@link CharSequence} (optionally as array)</td> * <td>{@link #getAlias()}</td> - * </tr> - * <tr> + * </tr><tr> * <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td> * <td>{@link org.opengis.metadata.Identifier} (optionally as array)</td> * <td>{@link #getIdentifiers()}</td> - * </tr> - * <tr> + * </tr><tr> * <td>{@value org.opengis.metadata.Identifier#DESCRIPTION_KEY}</td> * <td>{@link org.opengis.util.InternationalString} or {@link String}</td> * <td>{@link #getDescription()}</td> - * </tr> - * <tr> + * </tr><tr> * <td>{@value org.opengis.referencing.IdentifiedObject#REMARKS_KEY}</td> * <td>{@link org.opengis.util.InternationalString} or {@link String}</td> * <td>{@link #getRemarks()}</td> 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 d9817c7107..7cdc01b585 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 @@ -18,6 +18,7 @@ package org.apache.sis.parameter; import java.lang.reflect.Array; import java.util.Objects; +import java.util.Optional; import java.util.logging.Logger; import java.io.Serializable; import java.io.File; @@ -41,37 +42,38 @@ import org.apache.sis.io.wkt.FormattableObject; import org.apache.sis.io.wkt.Formatter; import org.apache.sis.io.wkt.Convention; import org.apache.sis.io.wkt.ElementKind; +import org.apache.sis.xml.bind.Context; import org.apache.sis.xml.bind.gml.Measure; import org.apache.sis.xml.bind.gml.MeasureList; +import org.apache.sis.xml.util.ExternalLinkHandler; +import org.apache.sis.metadata.internal.ImplementationHelper; import org.apache.sis.referencing.internal.Resources; import org.apache.sis.referencing.util.WKTUtilities; import org.apache.sis.referencing.util.WKTKeywords; -import org.apache.sis.metadata.internal.ImplementationHelper; -import org.apache.sis.util.Numbers; +import org.apache.sis.math.DecimalFunctions; +import org.apache.sis.system.Loggers; +import org.apache.sis.util.Utilities; +import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.ComparisonMode; import org.apache.sis.util.LenientComparable; import org.apache.sis.util.ObjectConverters; import org.apache.sis.util.UnconvertibleObjectException; +import org.apache.sis.util.Numbers; import org.apache.sis.util.internal.Numerics; -import org.apache.sis.system.Loggers; -import org.apache.sis.math.DecimalFunctions; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.logging.Logging; -import static org.apache.sis.util.ArgumentChecks.ensureNonNull; -import static org.apache.sis.util.Utilities.deepEquals; /** - * A single parameter value used by an operation method. {@code ParameterValue} instances are elements in - * a {@linkplain DefaultParameterValueGroup parameter value group}, in a way similar to {@code Map.Entry} - * instances in a {@code java.util.Map}. - * - * <p>In the context of coordinate operations, most parameter values are numeric and can be obtained by the + * A single parameter value used by an operation method. Each {@code ParameterValue} is a + * (<var>key</var>, <var>value</var>) pair, and an arbitrary number of those pairs can be + * stored in a a {@linkplain DefaultParameterValueGroup parameter value group}. + * In the context of coordinate operations, parameter values are often numeric and can be obtained by the * {@link #intValue()} or {@link #doubleValue()} methods. But other types of parameter values are possible * and can be handled by the more generic {@link #getValue()} and {@link #setValue(Object)} methods. * All {@code xxxValue()} methods in this class are convenience methods converting the value from {@code Object} * to some commonly used types. Those types are specified in ISO 19111 as an union of attributes, listed below with - * the corresponding getter and setter methods:</p> + * the corresponding getter and setter methods: * * <table class="sis"> * <caption>Mapping from ISO attributes to getters and setters</caption> @@ -96,28 +98,44 @@ import static org.apache.sis.util.Utilities.deepEquals; * Class<T> valueClass = parameter.getDescriptor().getValueClass(); * } * + * <h2>Absolute paths of value files</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 + * {@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. + * * <h2>Instantiation</h2> * A {@linkplain DefaultParameterDescriptor parameter descriptor} must be defined before parameter value can be created. - * Descriptors are usually predefined by map projection or process providers. Given a descriptor, a parameter value can - * be created by a call to the {@link #DefaultParameterValue(ParameterDescriptor)} constructor or by a call to the - * {@link ParameterDescriptor#createValue()} method. The latter is recommended since it allows descriptors to return - * specialized implementations. + * 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. + * It is also possible to invoke the {@linkplain #DefaultParameterValue(ParameterDescriptor) constructor} directly, + * but the former is recommended because it allows descriptors to return specialized implementations. * * <h2>Implementation note for subclasses</h2> - * All read and write operations (except constructors, {@link #equals(Object)} and {@link #hashCode()}) + * All read and write operations except constructors, {@link #equals(Object)} and {@link #hashCode()}, * ultimately delegates to the following methods: * * <ul> - * <li>All getter methods will invoke {@link #getValue()} and {@link #getUnit()} (if needed), + * <li>The source file property is accessed by {@link #getSourceFile()} and {@link #setSourceFile(URI)}.</li> + * <li>All other getter methods will invoke {@link #getValue()} and {@link #getUnit()} (if needed), * then perform their processing on the values returned by those methods.</li> - * <li>All setter methods delegate to the {@link #setValue(Object, Unit)} method.</li> + * <li>All other setter methods delegate to the {@link #setValue(Object, Unit)} method.</li> * </ul> * * Consequently, the above-cited methods provide single points that subclasses can override * for modifying the behavior of all getter and setter methods. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @param <T> the type of the value stored in this parameter. * @@ -175,6 +193,17 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param @SuppressWarnings("serial") // Most SIS implementations are serializable. protected Unit<?> unit; + /** + * If the parameter value is a relative path, the base URI to use for resolving that path. + * This field is {@code null} by default, unless this parameter value has been read from a + * GML document or a WKT file, in which case this field contains the URI of that document. + * This information allows to interpret {@link #valueFile()} as relative to the GML or WKT file. + * + * @see #getSourceFile() + * @see URI#resolve(URI) + */ + private URI sourceFile; + /** * Creates a parameter value from the specified descriptor. * The value will be initialized to the default value, if any. @@ -182,7 +211,7 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param * @param descriptor the abstract definition of this parameter. */ public DefaultParameterValue(final ParameterDescriptor<T> descriptor) { - ensureNonNull("descriptor", descriptor); + ArgumentChecks.ensureNonNull("descriptor", descriptor); this.descriptor = descriptor; this.value = descriptor.getDefaultValue(); this.unit = descriptor.getUnit(); @@ -190,8 +219,8 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param /** * Creates a new instance initialized with the values from the specified parameter object. - * This is a <em>shallow</em> copy constructor, since the value contained in the given - * object is not cloned. + * This is a <em>shallow</em> copy constructor, since the {@linkplain #getValue() value} + * contained in the given object is not cloned. * * @param parameter the parameter to copy values from. * @@ -199,16 +228,17 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param * @see #unmodifiable(ParameterValue) */ public DefaultParameterValue(final ParameterValue<T> parameter) { - ensureNonNull("parameter", parameter); + ArgumentChecks.ensureNonNull("parameter", parameter); descriptor = parameter.getDescriptor(); value = parameter.getValue(); unit = parameter.getUnit(); + if (parameter instanceof DefaultParameterValue<?>) { + sourceFile = ((DefaultParameterValue<?>) parameter).getSourceFile().orElse(null); + } } /** - * Returns the definition of this parameter. - * - * @return the definition of this parameter. + * {@return the definition of this parameter}. */ @Override @XmlElement(name = "operationParameter", required = true) @@ -216,6 +246,28 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param return descriptor; } + /** + * {@return the URI of the GML document or WKT file from which this parameter value has been 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: + * + * {@snippet lang="java" : + * DefaultParameterValue<?> pv = ...; + * URI file = pv.valueFile(); + * file = pv.getSourceFile().map((base) -> base.resolve(file)).orElse(file); + * } + * + * @see #setSourceFile(URI) + * @see org.apache.sis.io.wkt.WKTFormat#getSourceFile() + * @see org.apache.sis.xml.MarshalContext#getDocumentURI() + * @see URI#resolve(URI) + * + * @since 1.5 + */ + public Optional<URI> getSourceFile() { + return Optional.ofNullable(sourceFile); + } + /** * Returns the unit of measure of the parameter value. * If the parameter value has no unit (for example because it is a {@link String} type), @@ -272,6 +324,7 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param */ @Override public boolean booleanValue() throws IllegalStateException { + @SuppressWarnings("LocalVariableHidesMemberVariable") final T value = getValue(); if (value instanceof Boolean) { return (Boolean) value; @@ -296,6 +349,7 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param */ @Override public int intValue() throws IllegalStateException { + @SuppressWarnings("LocalVariableHidesMemberVariable") final T value = getValue(); if (value instanceof Number) { final int integer = ((Number) value).intValue(); @@ -323,6 +377,7 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param */ @Override public int[] intValueList() throws IllegalStateException { + @SuppressWarnings("LocalVariableHidesMemberVariable") final T value = getValue(); if (value instanceof int[]) { return ((int[]) value).clone(); @@ -349,6 +404,7 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param */ @Override public double doubleValue() throws IllegalStateException { + @SuppressWarnings("LocalVariableHidesMemberVariable") final T value = getValue(); if (value instanceof Number) { return ((Number) value).doubleValue(); @@ -374,6 +430,7 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param */ @Override public double[] doubleValueList() throws IllegalStateException { + @SuppressWarnings("LocalVariableHidesMemberVariable") final T value = getValue(); if (value instanceof double[]) { return ((double[]) value).clone(); @@ -384,20 +441,20 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param /** * Returns the converter to be used by {@link #doubleValue(Unit)} and {@link #doubleValueList(Unit)}. */ - private UnitConverter getConverterTo(final Unit<?> unit) { + private UnitConverter getConverterTo(final Unit<?> target) { final Unit<?> source = getUnit(); if (source == null) { throw new IllegalStateException(Resources.format(Resources.Keys.UnitlessParameter_1, Verifier.getDisplayName(descriptor))); } - ensureNonNull("unit", unit); + ArgumentChecks.ensureNonNull("unit", target); final short expectedID = Verifier.getUnitMessageID(source); - if (Verifier.getUnitMessageID(unit) != expectedID) { - throw new IllegalArgumentException(Errors.format(expectedID, unit)); + if (Verifier.getUnitMessageID(target) != expectedID) { + throw new IllegalArgumentException(Errors.format(expectedID, target)); } try { - return source.getConverterToAny(unit); + return source.getConverterToAny(target); } catch (IncommensurableException e) { - throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatibleUnits_2, source, unit), e); + throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatibleUnits_2, source, target), e); } } @@ -422,6 +479,7 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param */ @Override public double doubleValue(final Unit<?> unit) throws IllegalArgumentException, IllegalStateException { + @SuppressWarnings("LocalVariableHidesMemberVariable") final double value = doubleValue(); // Invoke first in case it throws an exception. return getConverterTo(unit).convert(value); } @@ -469,6 +527,7 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param */ @Override public String stringValue() throws IllegalStateException { + @SuppressWarnings("LocalVariableHidesMemberVariable") final T value = getValue(); if (value instanceof CharSequence) { return value.toString(); @@ -481,6 +540,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()}. + * * @return the reference to a file containing parameter values. * @throws InvalidParameterTypeException if the value is not a reference to a file or a URI. * @throws IllegalStateException if the value is not defined and there is no default value. @@ -490,6 +555,7 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param */ @Override public URI valueFile() throws IllegalStateException { + @SuppressWarnings("LocalVariableHidesMemberVariable") final T value = getValue(); if (value instanceof URI) return (URI) value; if (value instanceof File) return ((File) value).toURI(); @@ -528,6 +594,23 @@ public class DefaultParameterValue<T> extends FormattableObject implements Param return isFile(newValue); } + /** + * 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}. + * + * @param document URI of the document from which this parameter value has been read, or {@code null} if none. + * + * @see #getSourceFile() + * @see org.apache.sis.io.wkt.WKTFormat#setSourceFile(URI) + * @see URI#resolve(URI) + * + * @see 1.5 + */ + public void setSourceFile(final URI document) { + sourceFile = document; + } + /** * Returns the exception to throw when an incompatible method is invoked for the value type. */ @@ -723,8 +806,8 @@ convert: if (componentType != null) { setValue(n, unit); /* * Above code used `unit` instead of `getUnit()` despite class Javadoc claim because units are not expected - * to be involved in this method. We access this field only as a matter of principle, for making sure that no - * property other than the value is altered by this method call. + * to be involved in this method. We access this field only as a matter of principle, for making sure that + * no property other than the value is altered by this method call. */ } @@ -841,15 +924,16 @@ convert: if (componentType != null) { if (mode == ComparisonMode.STRICT) { if (getClass() == object.getClass()) { final DefaultParameterValue<?> that = (DefaultParameterValue<?>) object; - return Objects.equals(descriptor, that.descriptor) && - Objects.deepEquals(value, that.value) && - Objects.equals(unit, that.unit); + return Objects.equals (descriptor, that.descriptor) && + Objects.deepEquals(sourceFile, that.sourceFile) && + Objects.deepEquals(value, that.value) && + Objects.equals (unit, that.unit); } } else if (object instanceof ParameterValue<?>) { final ParameterValue<?> that = (ParameterValue<?>) object; - return deepEquals(getDescriptor(), that.getDescriptor(), mode) && - deepEquals(getValue(), that.getValue(), mode) && - deepEquals(getUnit(), that.getUnit(), mode); + return Utilities.deepEquals(getDescriptor(), that.getDescriptor(), mode) && + Utilities.deepEquals(getValue(), that.getValue(), mode) && + Utilities.deepEquals(getUnit(), that.getUnit(), mode); } } return false; @@ -1162,6 +1246,7 @@ convert: if (componentType != null) { @XmlElement(name = "valueList", type = MeasureList.class) }) private Object getXmlValue() { + @SuppressWarnings("LocalVariableHidesMemberVariable") final Object value = getValue(); // Give to user a chance to override. if (value != null) { if (value instanceof Number) { @@ -1196,6 +1281,10 @@ convert: if (componentType != null) { */ @SuppressWarnings("unchecked") private void setXmlValue(Object xmlValue) { + ExternalLinkHandler linkHandler = Context.linkHandler(Context.current()); + if (linkHandler != null) { + sourceFile = linkHandler.getURI(); + } if (value == null && unit == null) { if (xmlValue instanceof Measure) { final Measure measure = (Measure) xmlValue; diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/UnmodifiableParameterValue.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/UnmodifiableParameterValue.java index 8939067bee..fb22b0c6eb 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/UnmodifiableParameterValue.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/UnmodifiableParameterValue.java @@ -16,6 +16,7 @@ */ package org.apache.sis.parameter; +import java.net.URI; import jakarta.xml.bind.annotation.XmlTransient; import javax.measure.Unit; import org.opengis.parameter.ParameterValue; @@ -92,6 +93,7 @@ final class UnmodifiableParameterValue<T> extends DefaultParameterValue<T> { */ @Override public T getValue() { + @SuppressWarnings("LocalVariableHidesMemberVariable") T value = super.getValue(); if (value instanceof Cloneable) { final Class<T> type = getDescriptor().getValueClass(); // May be null after GML unmarshalling. @@ -112,6 +114,14 @@ final class UnmodifiableParameterValue<T> extends DefaultParameterValue<T> { throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1, getClass())); } + /** + * Do not allow modification of the parameter value. + */ + @Override + public void setSourceFile(final URI source) { + throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1, getClass())); + } + /** * Returns a modifiable copy of this parameter. */ diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/package-info.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/package-info.java index 4b3a2e0910..1299b3c4eb 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/package-info.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/package-info.java @@ -85,7 +85,7 @@ * if the given value is not assignable to the expected class or is not inside the value domain. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * @since 0.4 */ @XmlSchema(elementFormDefault= XmlNsForm.QUALIFIED, namespace = Namespaces.GML, xmlns = { diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java index ca0cba5a49..cc2600eaa3 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java @@ -306,7 +306,7 @@ public final class CRS extends Static { * Applications which need to parse a large amount of WKT strings should consider to use * the {@link org.apache.sis.io.wkt.WKTFormat} class instead of this method. * - * @param text coordinate system encoded in Well-Known Text format (version 1 or 2). + * @param wkt coordinate system encoded in Well-Known Text format (version 1 or 2). * @return the parsed Coordinate Reference System. * @throws FactoryException if the given WKT cannot be parsed. * @@ -317,9 +317,8 @@ public final class CRS extends Static { * * @since 0.6 */ - public static CoordinateReferenceSystem fromWKT(final String text) throws FactoryException { - ArgumentChecks.ensureNonNull("text", text); - final CoordinateReferenceSystem crs = GeodeticObjectFactory.provider().createFromWKT(text); + public static CoordinateReferenceSystem fromWKT(final String wkt) throws FactoryException { + final CoordinateReferenceSystem crs = GeodeticObjectFactory.provider().createFromWKT(wkt); DefinitionVerifier.withAuthority(crs, Loggers.WKT, CRS.class, "fromWKT"); return crs; } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java index 95c4401371..ca55b08f28 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java @@ -1661,7 +1661,7 @@ public class GeodeticObjectFactory extends AbstractFactory implements CRSFactory * Applications which need to parse a large amount of WKT strings should consider to use * the {@link org.apache.sis.io.wkt.WKTFormat} class instead of this method. * - * @param text coordinate system encoded in Well-Known Text format (version 1 or 2). + * @param wkt coordinate system encoded in Well-Known Text format (version 1 or 2). * @throws FactoryException if the object creation failed. * * @see org.apache.sis.io.wkt @@ -1670,8 +1670,8 @@ public class GeodeticObjectFactory extends AbstractFactory implements CRSFactory * @see <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html">Legacy WKT 1</a> */ @Override - public CoordinateReferenceSystem createFromWKT(final String text) throws FactoryException { - ArgumentChecks.ensureNonEmpty("text", text); + public CoordinateReferenceSystem createFromWKT(final String wkt) throws FactoryException { + ArgumentChecks.ensureNonEmpty("wkt", wkt); Parser p = parser.getAndSet(null); if (p == null) try { Constructor<? extends Parser> c = parserConstructor; @@ -1687,7 +1687,7 @@ public class GeodeticObjectFactory extends AbstractFactory implements CRSFactory } final Object object; try { - object = p.createFromWKT(text); + object = p.createFromWKT(wkt); } catch (FactoryException e) { /* * In the case of map projection, the parsing may fail because a projection parameter is not known to SIS. diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java index e634c53788..6360e9bdd4 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java @@ -1659,15 +1659,15 @@ public class DefaultMathTransformFactory extends AbstractFactory implements Math * inverse matrix but those information are lost at WKT formatting time. A similar "hidden" information * lost may also happen with {@link WraparoundTransform}, also making that transform non-invertible.</p> * - * @param text math transform encoded in Well-Known Text format. + * @param wkt math transform encoded in Well-Known Text format. * @return the math transform (never {@code null}). * @throws FactoryException if the Well-Known Text cannot be parsed, * or if the math transform creation failed from some other reason. */ @Override - public MathTransform createFromWKT(final String text) throws FactoryException { + public MathTransform createFromWKT(final String wkt) throws FactoryException { lastMethod.remove(); - ArgumentChecks.ensureNonEmpty("text", text); + ArgumentChecks.ensureNonEmpty("wkt", wkt); Parser p = parser.getAndSet(null); if (p == null) try { Constructor<? extends Parser> c = parserConstructor; @@ -1687,7 +1687,7 @@ public class DefaultMathTransformFactory extends AbstractFactory implements Math */ final Object object; try { - object = p.createFromWKT(text); + object = p.createFromWKT(wkt); } catch (FactoryException e) { /* * The parsing may fail because a operation parameter is not known to SIS. If this happen, replace 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 b807ffad0f..c8ddc5db8c 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 @@ -46,8 +46,8 @@ public final class ElementTest extends TestCase { /** * A dummy parser to be given to the {@link Element} constructor. */ - private final AbstractParser parser = new AbstractParser(Symbols.SQUARE_BRACKETS, new HashMap<>(2), - null, null, null, Locale.ENGLISH) + private final AbstractParser parser = new AbstractParser( + null, new HashMap<>(2), Symbols.SQUARE_BRACKETS, null, null, null, Locale.ENGLISH) { @Override String getPublicFacade() { throw new UnsupportedOperationException(); diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java index 8ff779723e..32e34d9968 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java @@ -92,8 +92,8 @@ public final class GeodeticObjectParserTest extends TestCase { * Instantiates the parser to test. */ private void newParser(final Convention convention) { - parser = new GeodeticObjectParser(Symbols.getDefault(), Map.of(), - null, null, null, convention, Transliterator.DEFAULT, null, new ReferencingFactoryContainer()); + parser = new GeodeticObjectParser(null, Map.of(), Symbols.getDefault(), null, null, null, + convention, Transliterator.DEFAULT, null, new ReferencingFactoryContainer()); assertEquals(GeodeticObjectFactory.class.getCanonicalName(), parser.getPublicFacade()); } 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 3249a57314..13449a229e 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 @@ -144,7 +144,7 @@ public abstract class PRJDataStore extends URIDataStore { * @throws DataStoreException if an error occurred while reading the file. */ protected final void readPRJ() throws DataStoreException { - Exception cause = null; + Exception cause = null, suppressed = null; try { final AuxiliaryContent content = readAuxiliaryFile(PRJ); if (content == null) { @@ -154,6 +154,11 @@ public abstract class PRJDataStore extends URIDataStore { final String wkt = content.toString(); final StoreFormat format = new StoreFormat(locale, timezone, null, listeners); format.setConvention(Convention.WKT1_COMMON_UNITS); // Ignored if the format is WKT 2. + try { + format.setSourceFile(content.getURI()); + } catch (URISyntaxException e) { + suppressed = e; + } final ParsePosition pos = new ParsePosition(0); crs = (CoordinateReferenceSystem) format.parse(wkt, pos); if (crs != null) { @@ -171,7 +176,9 @@ public abstract class PRJDataStore extends URIDataStore { } catch (IOException | ParseException | ClassCastException e) { cause = e; } - throw new DataStoreReferencingException(cannotReadAuxiliaryFile(PRJ), cause); + final var e = new DataStoreReferencingException(cannotReadAuxiliaryFile(PRJ), cause); + if (suppressed != null) e.addSuppressed(suppressed); + throw e; } /** @@ -273,6 +280,16 @@ public abstract class PRJDataStore extends URIDataStore { return IOUtilities.filename(source); } + /** + * Returns the source as an URI if possible. + * + * @return the source as an URI, or {@code null} if none. + * @throws URISyntaxException if the URI cannot be parsed. + */ + public URI getURI() throws URISyntaxException { + return IOUtilities.toURI(source); + } + /** * Returns the number of valid characters in this sequence. */